diff --git a/.pylintrc b/.pylintrc index fce5efc89f9e..71149576dea8 100644 --- a/.pylintrc +++ b/.pylintrc @@ -71,7 +71,8 @@ disable=spelling, # way too noisy unnecessary-pass, # allow for methods with just "pass", for clarity no-else-return, # relax "elif" after a clause with a return docstring-first-line-empty, # relax docstring style - import-outside-toplevel + import-outside-toplevel, + bad-continuation, bad-whitespace # differences of opinion with black @@ -212,7 +213,7 @@ max-nested-blocks=5 [FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=105 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e0d937e6d933..9a185a385b0f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,10 @@ please ensure that: 1. The code follows the code style of the project and successfully passes the tests. For convenience, you can execute `tox` locally, which will run these checks and report any issues. + + If your code fails the local style checks (specifically the black + code formatting check) you can use `tox -eblack` to automatically + fix update the code formatting. 2. The documentation has been updated accordingly. In particular, if a function or class has been modified during the PR, please update the *docstring* accordingly. @@ -358,7 +362,24 @@ you just need to update the reference images as follows: new tests should now pass. Note: If you have run `test/ipynb/mpl_tester.ipynb` locally it is possible some file metadata has changed, **please do not commit and push changes to this file unless they were intentional**. -### Development Cycle + +## Style and lint + +Qiskit Terra uses 2 tools for verify code formatting and lint checking. The +first tool is [black](https://github.com/psf/black) which is a code formatting +tool that will automatically update the code formatting to a consistent style. +The second tool is [pylint]https://www.pylint.org/) which is a code linter +which does a deeper analysis of the Python code to find both style issues and +potential bugs and other common issues in Python. + +You can check that your local modifications conform to the style rules +by running `tox -elint` which will run `black` and `pylint` to check the local +code formatting and lint. If black returns a code formatting error you can +run `tox -eblack` to automatically update the code formatting to conform to +the style. However, if `pylint` returns any error you will have to fix these +issues by manually updating your code. + +## Development Cycle The development cycle for qiskit-terra is all handled in the open using the project boards in Github for project management. We use milestones diff --git a/Makefile b/Makefile index 1bd583d46e56..b63e3b84045c 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,10 @@ lint: python tools/find_optional_imports.py style: - pycodestyle qiskit test + black --check qiskit test tools + +black: + black qiskit test tools # Use the -s (starting directory) flag for "unittest discover" is necessary, # otherwise the QuantumCircuit header will be modified during the discovery. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index ddff03f217d2..2c75c6bce655 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -274,7 +274,7 @@ stages: - bash: | set -e source test-job/bin/activate - pycodestyle qiskit test + black --check qiskit test tools pylint -rn qiskit test tools/verify_headers.py qiskit test python tools/find_optional_imports.py diff --git a/pyproject.toml b/pyproject.toml index eae593b390e8..02197d279013 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,6 @@ [build-system] requires = ["Cython>=0.27.1", "setuptools", "wheel"] + +[tool.black] +line-length = 100 +target-version = ['py36', 'py37', 'py38', 'py39'] diff --git a/qiskit/__init__.py b/qiskit/__init__.py index 9b02be059324..5556c3f85705 100644 --- a/qiskit/__init__.py +++ b/qiskit/__init__.py @@ -64,7 +64,9 @@ warnings.warn( "Using Qiskit with Python 3.6 is deprecated as of the 0.17.0 release. " "Support for running Qiskit with Python 3.6 will be removed in a " - "future release.", DeprecationWarning) + "future release.", + DeprecationWarning, + ) class AerWrapper: @@ -77,6 +79,7 @@ def __bool__(self): if self.aer is None: try: from qiskit.providers import aer + self.aer = aer.Aer except ImportError: return False @@ -86,11 +89,14 @@ def __getattr__(self, attr): if not self.aer: try: from qiskit.providers import aer + self.aer = aer.Aer except ImportError as exc: - raise ImportError('Could not import the Aer provider from the ' - 'qiskit-aer package. Install qiskit-aer or ' - 'check your installation.') from exc + raise ImportError( + "Could not import the Aer provider from the " + "qiskit-aer package. Install qiskit-aer or " + "check your installation." + ) from exc return getattr(self.aer, attr) @@ -104,6 +110,7 @@ def __bool__(self): if self.ibmq is None: try: from qiskit.providers import ibmq + self.ibmq = ibmq.IBMQ except ImportError: return False @@ -113,12 +120,15 @@ def __getattr__(self, attr): if not self.ibmq: try: from qiskit.providers import ibmq + self.ibmq = ibmq.IBMQ except ImportError as exc: - raise ImportError('Could not import the IBMQ provider from the ' - 'qiskit-ibmq-provider package. Install ' - 'qiskit-ibmq-provider or check your ' - 'installation.') from exc + raise ImportError( + "Could not import the IBMQ provider from the " + "qiskit-ibmq-provider package. Install " + "qiskit-ibmq-provider or check your " + "installation." + ) from exc return getattr(self.ibmq, attr) diff --git a/qiskit/algorithms/__init__.py b/qiskit/algorithms/__init__.py index 495f2c26cd5a..e646fc452369 100644 --- a/qiskit/algorithms/__init__.py +++ b/qiskit/algorithms/__init__.py @@ -165,60 +165,75 @@ from .variational_algorithm import VariationalAlgorithm, VariationalResult from .amplitude_amplifiers import Grover, GroverResult, AmplificationProblem from .amplitude_estimators import ( - AmplitudeEstimator, AmplitudeEstimatorResult, - AmplitudeEstimation, AmplitudeEstimationResult, - FasterAmplitudeEstimation, FasterAmplitudeEstimationResult, - IterativeAmplitudeEstimation, IterativeAmplitudeEstimationResult, - MaximumLikelihoodAmplitudeEstimation, MaximumLikelihoodAmplitudeEstimationResult, - EstimationProblem + AmplitudeEstimator, + AmplitudeEstimatorResult, + AmplitudeEstimation, + AmplitudeEstimationResult, + FasterAmplitudeEstimation, + FasterAmplitudeEstimationResult, + IterativeAmplitudeEstimation, + IterativeAmplitudeEstimationResult, + MaximumLikelihoodAmplitudeEstimation, + MaximumLikelihoodAmplitudeEstimationResult, + EstimationProblem, ) from .eigen_solvers import NumPyEigensolver, Eigensolver, EigensolverResult from .factorizers import Shor, ShorResult from .linear_solvers import HHL, LinearSolver, NumPyLinearSolver, LinearSolverResult -from .minimum_eigen_solvers import (VQE, VQEResult, QAOA, - NumPyMinimumEigensolver, - MinimumEigensolver, MinimumEigensolverResult) -from .phase_estimators import (HamiltonianPhaseEstimation, HamiltonianPhaseEstimationResult, - PhaseEstimationScale, PhaseEstimation, PhaseEstimationResult) +from .minimum_eigen_solvers import ( + VQE, + VQEResult, + QAOA, + NumPyMinimumEigensolver, + MinimumEigensolver, + MinimumEigensolverResult, +) +from .phase_estimators import ( + HamiltonianPhaseEstimation, + HamiltonianPhaseEstimationResult, + PhaseEstimationScale, + PhaseEstimation, + PhaseEstimationResult, +) from .exceptions import AlgorithmError __all__ = [ - 'AlgorithmResult', - 'VariationalAlgorithm', - 'VariationalResult', - 'AmplificationProblem', - 'Grover', - 'GroverResult', - 'AmplitudeEstimator', - 'AmplitudeEstimatorResult', - 'AmplitudeEstimation', - 'AmplitudeEstimationResult', - 'FasterAmplitudeEstimation', - 'FasterAmplitudeEstimationResult', - 'IterativeAmplitudeEstimation', - 'IterativeAmplitudeEstimationResult', - 'MaximumLikelihoodAmplitudeEstimation', - 'MaximumLikelihoodAmplitudeEstimationResult', - 'EstimationProblem', - 'NumPyEigensolver', - 'LinearSolverResult', - 'Eigensolver', - 'EigensolverResult', - 'Shor', - 'ShorResult', - 'VQE', - 'VQEResult', - 'QAOA', - 'LinearSolver', - 'HHL', - 'NumPyLinearSolver', - 'NumPyMinimumEigensolver', - 'MinimumEigensolver', - 'MinimumEigensolverResult', - 'HamiltonianPhaseEstimation', - 'HamiltonianPhaseEstimationResult', - 'PhaseEstimationScale', - 'PhaseEstimation', - 'PhaseEstimationResult', - 'AlgorithmError', + "AlgorithmResult", + "VariationalAlgorithm", + "VariationalResult", + "AmplificationProblem", + "Grover", + "GroverResult", + "AmplitudeEstimator", + "AmplitudeEstimatorResult", + "AmplitudeEstimation", + "AmplitudeEstimationResult", + "FasterAmplitudeEstimation", + "FasterAmplitudeEstimationResult", + "IterativeAmplitudeEstimation", + "IterativeAmplitudeEstimationResult", + "MaximumLikelihoodAmplitudeEstimation", + "MaximumLikelihoodAmplitudeEstimationResult", + "EstimationProblem", + "NumPyEigensolver", + "LinearSolverResult", + "Eigensolver", + "EigensolverResult", + "Shor", + "ShorResult", + "VQE", + "VQEResult", + "QAOA", + "LinearSolver", + "HHL", + "NumPyLinearSolver", + "NumPyMinimumEigensolver", + "MinimumEigensolver", + "MinimumEigensolverResult", + "HamiltonianPhaseEstimation", + "HamiltonianPhaseEstimationResult", + "PhaseEstimationScale", + "PhaseEstimation", + "PhaseEstimationResult", + "AlgorithmError", ] diff --git a/qiskit/algorithms/algorithm_result.py b/qiskit/algorithms/algorithm_result.py index 655b44bb8b58..0804303a4ef6 100644 --- a/qiskit/algorithms/algorithm_result.py +++ b/qiskit/algorithms/algorithm_result.py @@ -20,20 +20,23 @@ class AlgorithmResult(ABC): - """ Abstract Base Class for algorithm results.""" + """Abstract Base Class for algorithm results.""" def __str__(self) -> str: result = {} for name, value in inspect.getmembers(self): - if not name.startswith('_') and \ - not inspect.ismethod(value) and not inspect.isfunction(value) and \ - hasattr(self, name): + if ( + not name.startswith("_") + and not inspect.ismethod(value) + and not inspect.isfunction(value) + and hasattr(self, name) + ): result[name] = value return pprint.pformat(result, indent=4) - def combine(self, result: 'AlgorithmResult') -> None: + def combine(self, result: "AlgorithmResult") -> None: """ Any property from the argument that exists in the receiver is updated. @@ -43,15 +46,18 @@ def combine(self, result: 'AlgorithmResult') -> None: TypeError: Argument is None """ if result is None: - raise TypeError('Argument result expected.') + raise TypeError("Argument result expected.") if result == self: return # find any result public property that exists in the receiver for name, value in inspect.getmembers(result): - if not name.startswith('_') and \ - not inspect.ismethod(value) and not inspect.isfunction(value) and \ - hasattr(self, name): + if ( + not name.startswith("_") + and not inspect.ismethod(value) + and not inspect.isfunction(value) + and hasattr(self, name) + ): try: setattr(self, name, value) except AttributeError: diff --git a/qiskit/algorithms/amplitude_amplifiers/__init__.py b/qiskit/algorithms/amplitude_amplifiers/__init__.py index 34d74de7fb44..058c31e840ec 100644 --- a/qiskit/algorithms/amplitude_amplifiers/__init__.py +++ b/qiskit/algorithms/amplitude_amplifiers/__init__.py @@ -17,9 +17,9 @@ from .grover import Grover, GroverResult __all__ = [ - 'AmplitudeAmplifier', - 'AmplitudeAmplifierResult', - 'AmplificationProblem', - 'Grover', - 'GroverResult' + "AmplitudeAmplifier", + "AmplitudeAmplifierResult", + "AmplificationProblem", + "Grover", + "GroverResult", ] diff --git a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py b/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py index e8e7da676c2d..0562bd823e50 100644 --- a/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py +++ b/qiskit/algorithms/amplitude_amplifiers/amplification_problem.py @@ -27,15 +27,17 @@ class AmplificationProblem: on the optimal bitstring. """ - def __init__(self, - oracle: Union[QuantumCircuit, Statevector], - state_preparation: Optional[QuantumCircuit] = None, - grover_operator: Optional[QuantumCircuit] = None, - post_processing: Optional[Callable[[str], Any]] = None, - objective_qubits: Optional[Union[int, List[int]]] = None, - is_good_state: Optional[Union[ - Callable[[str], bool], List[int], List[str], Statevector]] = None, - ) -> None: + def __init__( + self, + oracle: Union[QuantumCircuit, Statevector], + state_preparation: Optional[QuantumCircuit] = None, + grover_operator: Optional[QuantumCircuit] = None, + post_processing: Optional[Callable[[str], Any]] = None, + objective_qubits: Optional[Union[int, List[int]]] = None, + is_good_state: Optional[ + Union[Callable[[str], bool], List[int], List[str], Statevector] + ] = None, + ) -> None: r""" Args: oracle: The oracle reflecting about the bad states. @@ -159,15 +161,17 @@ def is_good_state(self) -> Callable[[str], bool]: if all(isinstance(good_bitstr, str) for good_bitstr in self._is_good_state): return lambda bitstr: bitstr in self._is_good_state else: - return lambda bitstr: all(bitstr[good_index] == '1' # type:ignore - for good_index in self._is_good_state) + return lambda bitstr: all( + bitstr[good_index] == "1" # type:ignore + for good_index in self._is_good_state + ) return lambda bitstr: bitstr in self._is_good_state.probabilities_dict() @is_good_state.setter - def is_good_state(self, - is_good_state: Union[Callable[[str], bool], List[int], List[str], Statevector] - ) -> None: + def is_good_state( + self, is_good_state: Union[Callable[[str], bool], List[int], List[str], Statevector] + ) -> None: """Set the ``is_good_state`` function. Args: diff --git a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py b/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py index 0e228e342469..ca9293afa35f 100644 --- a/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py +++ b/qiskit/algorithms/amplitude_amplifiers/amplitude_amplifier.py @@ -25,7 +25,7 @@ class AmplitudeAmplifier(ABC): """The interface for amplification algorithms.""" @abstractmethod - def amplify(self, amplification_problem: AmplificationProblem) -> 'AmplificationResult': + def amplify(self, amplification_problem: AmplificationProblem) -> "AmplificationResult": """Run the amplification algorithm. Args: diff --git a/qiskit/algorithms/amplitude_amplifiers/grover.py b/qiskit/algorithms/amplitude_amplifiers/grover.py index 75158b42d107..86a033b8ca41 100644 --- a/qiskit/algorithms/amplitude_amplifiers/grover.py +++ b/qiskit/algorithms/amplitude_amplifiers/grover.py @@ -104,12 +104,13 @@ class Grover(AmplitudeAmplifier): """ - def __init__(self, - iterations: Optional[Union[int, List[int], Iterator[int], float]] = None, - growth_rate: Optional[float] = None, - sample_from_iterations: bool = False, - quantum_instance: Optional[Union[QuantumInstance, Backend, BaseBackend]] = None, - ) -> None: + def __init__( + self, + iterations: Optional[Union[int, List[int], Iterator[int], float]] = None, + growth_rate: Optional[float] = None, + sample_from_iterations: bool = False, + quantum_instance: Optional[Union[QuantumInstance, Backend, BaseBackend]] = None, + ) -> None: r""" Args: iterations: Specify the number of iterations/power of Grover's operator to be checked. @@ -140,7 +141,7 @@ def __init__(self, growth_rate = 1.2 if growth_rate is not None and iterations is not None: - raise ValueError('Pass either a value for iterations or growth_rate, not both.') + raise ValueError("Pass either a value for iterations or growth_rate, not both.") if growth_rate is not None: # yield iterations ** 1, iterations ** 2, etc. and casts to int @@ -165,8 +166,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: quantum_instance: The quantum instance used to run this algorithm. @@ -175,7 +177,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance - def amplify(self, amplification_problem: AmplificationProblem) -> 'GroverResult': + def amplify(self, amplification_problem: AmplificationProblem) -> "GroverResult": """Run the Grover algorithm. Args: @@ -192,13 +194,14 @@ def amplify(self, amplification_problem: AmplificationProblem) -> 'GroverResult' else: max_iterations = max(10, 2 ** amplification_problem.oracle.num_qubits) max_power = np.ceil( - 2 ** (len(amplification_problem.grover_operator.reflection_qubits) / 2)) + 2 ** (len(amplification_problem.grover_operator.reflection_qubits) / 2) + ) iterator = self._iterations result = GroverResult() iterations = [] - top_measurement = '0' * len(amplification_problem.objective_qubits) + top_measurement = "0" * len(amplification_problem.objective_qubits) oracle_evaluation = False all_circuit_results = [] max_probability = 0 @@ -224,23 +227,27 @@ def amplify(self, amplification_problem: AmplificationProblem) -> 'GroverResult' # trace out work qubits if qc.width() != num_bits: - indices = [i for i in range(qc.num_qubits) - if i not in amplification_problem.objective_qubits] + indices = [ + i + for i in range(qc.num_qubits) + if i not in amplification_problem.objective_qubits + ] rho = partial_trace(circuit_results, indices) circuit_results = np.diag(rho.data) max_amplitude = max(circuit_results.max(), circuit_results.min(), key=abs) max_amplitude_idx = np.where(circuit_results == max_amplitude)[0][0] top_measurement = np.binary_repr(max_amplitude_idx, num_bits) - max_probability = np.abs(max_amplitude)**2 + max_probability = np.abs(max_amplitude) ** 2 shots = 1 else: qc = self.construct_circuit(amplification_problem, power, measurement=True) circuit_results = self._quantum_instance.execute(qc).get_counts(qc) top_measurement = max(circuit_results.items(), key=operator.itemgetter(1))[0] shots = sum(circuit_results.values()) - max_probability = max(circuit_results.items(), key=operator.itemgetter(1))[1] \ - / shots + max_probability = ( + max(circuit_results.items(), key=operator.itemgetter(1))[1] / shots + ) all_circuit_results.append(circuit_results) oracle_evaluation = amplification_problem.is_good_state(top_measurement) @@ -269,9 +276,9 @@ def optimal_num_iterations(num_solutions: int, num_qubits: int) -> int: """ return math.floor(np.pi * np.sqrt(2 ** num_qubits / num_solutions) / 4) - def construct_circuit(self, problem: AmplificationProblem, - power: Optional[int] = None, - measurement: bool = False) -> QuantumCircuit: + def construct_circuit( + self, problem: AmplificationProblem, power: Optional[int] = None, measurement: bool = False + ) -> QuantumCircuit: """Construct the circuit for Grover's algorithm with ``power`` Grover operators. Args: @@ -288,10 +295,10 @@ def construct_circuit(self, problem: AmplificationProblem, """ if power is None: if len(self._iterations) > 1: - raise ValueError('Please pass ``power`` if the iterations are not an integer.') + raise ValueError("Please pass ``power`` if the iterations are not an integer.") power = self._iterations[0] - qc = QuantumCircuit(problem.oracle.num_qubits, name='Grover circuit') + qc = QuantumCircuit(problem.oracle.num_qubits, name="Grover circuit") qc.compose(problem.state_preparation, inplace=True) if power > 0: qc.compose(problem.grover_operator.power(power), inplace=True) diff --git a/qiskit/algorithms/amplitude_estimators/__init__.py b/qiskit/algorithms/amplitude_estimators/__init__.py index a9274e707d82..764f8863857d 100644 --- a/qiskit/algorithms/amplitude_estimators/__init__.py +++ b/qiskit/algorithms/amplitude_estimators/__init__.py @@ -20,15 +20,15 @@ from .estimation_problem import EstimationProblem __all__ = [ - 'AmplitudeEstimator', - 'AmplitudeEstimatorResult', - 'AmplitudeEstimation', - 'AmplitudeEstimationResult', - 'FasterAmplitudeEstimation', - 'FasterAmplitudeEstimationResult', - 'IterativeAmplitudeEstimation', - 'IterativeAmplitudeEstimationResult', - 'MaximumLikelihoodAmplitudeEstimation', - 'MaximumLikelihoodAmplitudeEstimationResult', - 'EstimationProblem', + "AmplitudeEstimator", + "AmplitudeEstimatorResult", + "AmplitudeEstimation", + "AmplitudeEstimationResult", + "FasterAmplitudeEstimation", + "FasterAmplitudeEstimationResult", + "IterativeAmplitudeEstimation", + "IterativeAmplitudeEstimationResult", + "MaximumLikelihoodAmplitudeEstimation", + "MaximumLikelihoodAmplitudeEstimationResult", + "EstimationProblem", ] diff --git a/qiskit/algorithms/amplitude_estimators/ae.py b/qiskit/algorithms/amplitude_estimators/ae.py index 641c8c5751dd..ec86059787fb 100644 --- a/qiskit/algorithms/amplitude_estimators/ae.py +++ b/qiskit/algorithms/amplitude_estimators/ae.py @@ -53,12 +53,13 @@ class AmplitudeEstimation(AmplitudeEstimator): `arXiv:1912.05559 `_. """ - def __init__(self, num_eval_qubits: int, - phase_estimation_circuit: Optional[QuantumCircuit] = None, - iqft: Optional[QuantumCircuit] = None, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None, - ) -> None: + def __init__( + self, + num_eval_qubits: int, + phase_estimation_circuit: Optional[QuantumCircuit] = None, + iqft: Optional[QuantumCircuit] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: r""" Args: num_eval_qubits: The number of evaluation qubits. @@ -73,7 +74,7 @@ def __init__(self, num_eval_qubits: int, ValueError: If the number of evaluation qubits is smaller than 1. """ if num_eval_qubits < 1: - raise ValueError('The number of evaluation qubits must at least be 1.') + raise ValueError("The number of evaluation qubits must at least be 1.") super().__init__() @@ -97,8 +98,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: @@ -108,9 +110,9 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance - def construct_circuit(self, - estimation_problem: EstimationProblem, - measurement: bool = False) -> QuantumCircuit: + def construct_circuit( + self, estimation_problem: EstimationProblem, measurement: bool = False + ) -> QuantumCircuit: """Construct the Amplitude Estimation quantum circuit. Args: @@ -127,13 +129,16 @@ def construct_circuit(self, # otherwise use the circuit library -- note that this does not include the A operator else: from qiskit.circuit.library import PhaseEstimation + pec = PhaseEstimation(self._m, estimation_problem.grover_operator, iqft=self._iqft) # combine the Phase Estimation circuit with the A operator circuit = QuantumCircuit(*pec.qregs) - circuit.compose(estimation_problem.state_preparation, - list(range(self._m, circuit.num_qubits)), - inplace=True) + circuit.compose( + estimation_problem.state_preparation, + list(range(self._m, circuit.num_qubits)), + inplace=True, + ) circuit.compose(pec, inplace=True) # add measurements if necessary @@ -144,9 +149,11 @@ def construct_circuit(self, return circuit - def evaluate_measurements(self, circuit_results: Union[Dict[str, int], np.ndarray], - threshold: float = 1e-6, - ) -> Tuple[Dict[int, float], Dict[float, float]]: + def evaluate_measurements( + self, + circuit_results: Union[Dict[str, int], np.ndarray], + threshold: float = 1e-6, + ) -> Tuple[Dict[int, float], Dict[float, float]]: """Evaluate the results from the circuit simulation. Given the probabilities from statevector simulation of the QAE circuit, compute the @@ -179,7 +186,7 @@ def _evaluate_statevector_results(self, statevector): num_qubits = int(np.log2(len(statevector))) for i, amplitude in enumerate(statevector): b = bin(i)[2:].zfill(num_qubits)[::-1] - y = int(b[:self._m], 2) # chop off all except the evaluation qubits + y = int(b[: self._m], 2) # chop off all except the evaluation qubits measurements[y] = measurements.get(y, 0) + np.abs(amplitude) ** 2 samples = OrderedDict() # type: OrderedDict @@ -199,7 +206,7 @@ def _evaluate_count_results(self, counts): shots = self._quantum_instance._run_config.shots for state, count in counts.items(): - y = int(state.replace(' ', '')[:self._m][::-1], 2) + y = int(state.replace(" ", "")[: self._m][::-1], 2) probability = count / shots measurements[y] = probability a = np.round(np.power(np.sin(y * np.pi / 2 ** self._m), 2), decimals=7) @@ -208,8 +215,9 @@ def _evaluate_count_results(self, counts): return samples, measurements @staticmethod - def compute_mle(result: 'AmplitudeEstimationResult', apply_post_processing: bool = False - ) -> float: + def compute_mle( + result: "AmplitudeEstimationResult", apply_post_processing: bool = False + ) -> float: """Compute the Maximum Likelihood Estimator (MLE). Args: @@ -265,7 +273,7 @@ def loglikelihood(a): return a_opt - def estimate(self, estimation_problem: EstimationProblem) -> 'AmplitudeEstimationResult': + def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimationResult": """Run the amplitude estimation algorithm on provided estimation problem. Args: @@ -280,12 +288,14 @@ def estimate(self, estimation_problem: EstimationProblem) -> 'AmplitudeEstimatio """ # check if A factory or state_preparation has been set if estimation_problem.state_preparation is None: - raise ValueError('The state_preparation property of the estimation problem must be ' - 'set.') + raise ValueError( + "The state_preparation property of the estimation problem must be " "set." + ) if estimation_problem.objective_qubits is None: - raise ValueError('The objective_qubits property of the estimation problem must be ' - 'set.') + raise ValueError( + "The objective_qubits property of the estimation problem must be " "set." + ) result = AmplitudeEstimationResult() result.num_evaluation_qubits = self._m @@ -312,8 +322,9 @@ def estimate(self, estimation_problem: EstimationProblem) -> 'AmplitudeEstimatio samples, measurements = self.evaluate_measurements(result.circuit_results) result.samples = samples - result.samples_processed = {estimation_problem.post_processing(a): p - for a, p in samples.items()} + result.samples_processed = { + estimation_problem.post_processing(a): p for a, p in samples.items() + } result.measurements = measurements # determine the most likely estimate @@ -333,16 +344,16 @@ def estimate(self, estimation_problem: EstimationProblem) -> 'AmplitudeEstimatio result.mle_processed = estimation_problem.post_processing(mle) result.confidence_interval = self.compute_confidence_interval(result) - result.confidence_interval_processed = tuple(estimation_problem.post_processing(value) - for value in result.confidence_interval) + result.confidence_interval_processed = tuple( + estimation_problem.post_processing(value) for value in result.confidence_interval + ) return result @staticmethod - def compute_confidence_interval(result: 'AmplitudeEstimationResult', - alpha: float = 0.05, - kind: str = 'likelihood_ratio' - ) -> Tuple[float, float]: + def compute_confidence_interval( + result: "AmplitudeEstimationResult", alpha: float = 0.05, kind: str = "likelihood_ratio" + ) -> Tuple[float, float]: """Compute the (1 - alpha) confidence interval. Args: @@ -362,16 +373,16 @@ def compute_confidence_interval(result: 'AmplitudeEstimationResult', if isinstance(result.circuit_results, (list, np.ndarray)): return (result.mle, result.mle) - if kind in ['likelihood_ratio', 'lr']: + if kind in ["likelihood_ratio", "lr"]: return _likelihood_ratio_confint(result, alpha) - if kind in ['fisher', 'fi']: + if kind in ["fisher", "fi"]: return _fisher_confint(result, alpha, observed=False) - if kind in ['observed_fisher', 'observed_information', 'oi']: + if kind in ["observed_fisher", "observed_information", "oi"]: return _fisher_confint(result, alpha, observed=True) - raise NotImplementedError('CI `{}` is not implemented.'.format(kind)) + raise NotImplementedError("CI `{}` is not implemented.".format(kind)) class AmplitudeEstimationResult(AmplitudeEstimatorResult): @@ -479,11 +490,11 @@ def _compute_fisher_information(result: AmplitudeEstimationResult, observed: boo p_i = np.asarray(list(result.samples.values())) # Calculate the observed Fisher information - fisher_information = sum(p * derivative_log_pdf_a(a, mlv, m) ** 2 - for p, a in zip(p_i, a_i)) + fisher_information = sum(p * derivative_log_pdf_a(a, mlv, m) ** 2 for p, a in zip(p_i, a_i)) else: + def integrand(x): - return (derivative_log_pdf_a(x, mlv, m))**2 * pdf_a(x, mlv, m) + return (derivative_log_pdf_a(x, mlv, m)) ** 2 * pdf_a(x, mlv, m) grid = np.sin(np.pi * np.arange(M / 2 + 1) / M) ** 2 fisher_information = sum(integrand(x) for x in grid) @@ -491,8 +502,9 @@ def integrand(x): return fisher_information -def _fisher_confint(result: AmplitudeEstimationResult, alpha: float, observed: bool = False - ) -> List[float]: +def _fisher_confint( + result: AmplitudeEstimationResult, alpha: float, observed: bool = False +) -> List[float]: """Compute the Fisher information confidence interval for the MLE of the previous run. Args: @@ -512,8 +524,7 @@ def _fisher_confint(result: AmplitudeEstimationResult, alpha: float, observed: b return tuple(result.post_processing(bound) for bound in confint) -def _likelihood_ratio_confint(result: AmplitudeEstimationResult, - alpha: float) -> List[float]: +def _likelihood_ratio_confint(result: AmplitudeEstimationResult, alpha: float) -> List[float]: """Compute the likelihood ratio confidence interval for the MLE of the previous run. Args: @@ -531,16 +542,16 @@ def _likelihood_ratio_confint(result: AmplitudeEstimationResult, y = int(np.round(M * np.arcsin(np.sqrt(qae)) / np.pi)) if y == 0: - right_of_qae = np.sin(np.pi * (y + 1) / M)**2 + right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 bubbles = [qae, right_of_qae] elif y == int(M / 2): # remember, M = 2^m is a power of 2 - left_of_qae = np.sin(np.pi * (y - 1) / M)**2 + left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 bubbles = [left_of_qae, qae] else: - left_of_qae = np.sin(np.pi * (y - 1) / M)**2 - right_of_qae = np.sin(np.pi * (y + 1) / M)**2 + left_of_qae = np.sin(np.pi * (y - 1) / M) ** 2 + right_of_qae = np.sin(np.pi * (y + 1) / M) ** 2 bubbles = [left_of_qae, qae, right_of_qae] # likelihood function diff --git a/qiskit/algorithms/amplitude_estimators/ae_utils.py b/qiskit/algorithms/amplitude_estimators/ae_utils.py index aeebe50d655a..74e85a3c8d44 100644 --- a/qiskit/algorithms/amplitude_estimators/ae_utils.py +++ b/qiskit/algorithms/amplitude_estimators/ae_utils.py @@ -171,10 +171,10 @@ def _derivative_beta(x, p): def _pdf_a_single_angle(x, p, m, pi_delta): """Helper function for `pdf_a`.""" - M = 2**m + M = 2 ** m d = pi_delta(x, p) - res = np.sin(M * d)**2 / (M * np.sin(d))**2 if d != 0 else 1 + res = np.sin(M * d) ** 2 / (M * np.sin(d)) ** 2 if d != 0 else 1 return res @@ -201,10 +201,14 @@ def pdf_a(x, p, m): # Compute the probabilities: Add up both angles that produce the given # value, except for the angles 0 and 0.5, which map to the unique a-values, # 0 and 1, respectively - pr = np.array([_pdf_a_single_angle(xi, p, m, _alpha) + _pdf_a_single_angle(xi, p, m, _beta) - if (xi not in [0, 1]) else _pdf_a_single_angle(xi, p, m, _alpha) - for xi in x - ]).flatten() + pr = np.array( + [ + _pdf_a_single_angle(xi, p, m, _alpha) + _pdf_a_single_angle(xi, p, m, _beta) + if (xi not in [0, 1]) + else _pdf_a_single_angle(xi, p, m, _alpha) + for xi in x + ] + ).flatten() # If is was a scalar return scalar otherwise the array return pr[0] if scalar else pr @@ -222,24 +226,29 @@ def derivative_log_pdf_a(x, p, m): Returns: float: d/dp log(PDF(x|p)) """ - M = 2**m + M = 2 ** m if x not in [0, 1]: num_p1 = 0 - for A, dA, B, dB in zip([_alpha, _beta], - [_derivative_alpha, _derivative_beta], - [_beta, _alpha], [_derivative_beta, _derivative_alpha]): - num_p1 += 2 * M * np.sin(M * A(x, p)) * np.cos(M * A(x, p)) \ - * dA(x, p) * np.sin(B(x, p))**2 \ - + 2 * np.sin(M * A(x, p))**2 * np.sin(B(x, p)) * np.cos(B(x, p)) * dB(x, p) - - den_p1 = np.sin(M * _alpha(x, p))**2 * np.sin(_beta(x, p))**2 + \ - np.sin(M * _beta(x, p))**2 * np.sin(_alpha(x, p))**2 + for A, dA, B, dB in zip( + [_alpha, _beta], + [_derivative_alpha, _derivative_beta], + [_beta, _alpha], + [_derivative_beta, _derivative_alpha], + ): + num_p1 += 2 * M * np.sin(M * A(x, p)) * np.cos(M * A(x, p)) * dA(x, p) * np.sin( + B(x, p) + ) ** 2 + 2 * np.sin(M * A(x, p)) ** 2 * np.sin(B(x, p)) * np.cos(B(x, p)) * dB(x, p) + + den_p1 = ( + np.sin(M * _alpha(x, p)) ** 2 * np.sin(_beta(x, p)) ** 2 + + np.sin(M * _beta(x, p)) ** 2 * np.sin(_alpha(x, p)) ** 2 + ) num_p2 = 0 - for A, dA, B in zip([_alpha, _beta], - [_derivative_alpha, _derivative_beta], - [_beta, _alpha]): + for A, dA, B in zip( + [_alpha, _beta], [_derivative_alpha, _derivative_beta], [_beta, _alpha] + ): num_p2 += 2 * np.cos(A(x, p)) * dA(x, p) * np.sin(B(x, p)) den_p2 = np.sin(_alpha(x, p)) * np.sin(_beta(x, p)) diff --git a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py b/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py index 7cdcfa29973c..6f78257d095f 100644 --- a/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py +++ b/qiskit/algorithms/amplitude_estimators/amplitude_estimator.py @@ -24,7 +24,7 @@ class AmplitudeEstimator: """The Amplitude Estimation interface.""" @abstractmethod - def estimate(self, estimation_problem: EstimationProblem) -> 'AmplitudeEstimatorResult': + def estimate(self, estimation_problem: EstimationProblem) -> "AmplitudeEstimatorResult": """Run the amplitude estimation algorithm. Args: diff --git a/qiskit/algorithms/amplitude_estimators/estimation_problem.py b/qiskit/algorithms/amplitude_estimators/estimation_problem.py index f2ce03a241f6..056fc83687b4 100644 --- a/qiskit/algorithms/amplitude_estimators/estimation_problem.py +++ b/qiskit/algorithms/amplitude_estimators/estimation_problem.py @@ -29,13 +29,14 @@ class EstimationProblem: or a custom Grover operator. """ - def __init__(self, - state_preparation: QuantumCircuit, - objective_qubits: Union[int, List[int]], - grover_operator: Optional[QuantumCircuit] = None, - post_processing: Optional[Callable[[float], float]] = None, - is_good_state: Optional[Callable[[str], bool]] = None, - ) -> None: + def __init__( + self, + state_preparation: QuantumCircuit, + objective_qubits: Union[int, List[int]], + grover_operator: Optional[QuantumCircuit] = None, + post_processing: Optional[Callable[[float], float]] = None, + is_good_state: Optional[Callable[[str], bool]] = None, + ) -> None: r""" Args: state_preparation: A circuit preparing the input state, referred to as @@ -126,7 +127,7 @@ def is_good_state(self) -> Callable[[str], bool]: Handle to the ``is_good_state`` callable. """ if self._is_good_state is None: - return lambda x: all(bit == '1' for bit in x) + return lambda x: all(bit == "1" for bit in x) return self._is_good_state @@ -157,8 +158,7 @@ def grover_operator(self) -> Optional[QuantumCircuit]: # build the reflection about the bad state: a MCZ with open controls (thus X gates # around the controls) and X gates around the target to change from a phaseflip on # |1> to a phaseflip on |0> - num_state_qubits = self.state_preparation.num_qubits \ - - self.state_preparation.num_ancillas + num_state_qubits = self.state_preparation.num_qubits - self.state_preparation.num_ancillas oracle = QuantumCircuit(num_state_qubits) oracle.h(self.objective_qubits[-1]) @@ -181,7 +181,7 @@ def grover_operator(self, grover_operator: Optional[QuantumCircuit]) -> None: """ self._grover_operator = grover_operator - def rescale(self, scaling_factor: float) -> 'EstimationProblem': + def rescale(self, scaling_factor: float) -> "EstimationProblem": """Rescale the good state amplitude in the estimation problem. Args: @@ -191,7 +191,7 @@ def rescale(self, scaling_factor: float) -> 'EstimationProblem': A rescaled estimation problem. """ if self._grover_operator is not None: - warnings.warn('Rescaling discards the Grover operator.') + warnings.warn("Rescaling discards the Grover operator.") # rescale the amplitude by a factor of 1/4 by adding an auxiliary qubit rescaled_stateprep = _rescale_amplitudes(self.state_preparation, scaling_factor) @@ -201,13 +201,15 @@ def rescale(self, scaling_factor: float) -> 'EstimationProblem': # add the scaling qubit to the good state qualifier def is_good_state(bitstr): # pylint: disable=not-callable - return self.is_good_state(bitstr[1:]) and bitstr[0] == '1' + return self.is_good_state(bitstr[1:]) and bitstr[0] == "1" # rescaled estimation problem - problem = EstimationProblem(rescaled_stateprep, - objective_qubits=objective_qubits, - post_processing=self.post_processing, - is_good_state=is_good_state) + problem = EstimationProblem( + rescaled_stateprep, + objective_qubits=objective_qubits, + post_processing=self.post_processing, + is_good_state=is_good_state, + ) return problem @@ -251,7 +253,7 @@ def _rescale_amplitudes(circuit: QuantumCircuit, scaling_factor: float) -> Quant Returns: A copy of the circuit with an additional qubit and RY gate for the rescaling. """ - qr = QuantumRegister(1, 'scaling') + qr = QuantumRegister(1, "scaling") rescaled = QuantumCircuit(*circuit.qregs, qr) rescaled.compose(circuit, circuit.qubits, inplace=True) rescaled.ry(2 * numpy.arcsin(scaling_factor), qr) diff --git a/qiskit/algorithms/amplitude_estimators/fae.py b/qiskit/algorithms/amplitude_estimators/fae.py index 70665ab0e6f8..03968a28b72c 100644 --- a/qiskit/algorithms/amplitude_estimators/fae.py +++ b/qiskit/algorithms/amplitude_estimators/fae.py @@ -45,12 +45,13 @@ class FasterAmplitudeEstimation(AmplitudeEstimator): """ - def __init__(self, - delta: float, - maxiter: int, - rescale: bool = True, - quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None - ) -> None: + def __init__( + self, + delta: float, + maxiter: int, + rescale: bool = True, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: r""" Args: delta: The probability that the true value is outside of the final confidence interval. @@ -82,8 +83,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: @@ -95,7 +97,7 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, def _cos_estimate(self, estimation_problem, k, shots): if self._quantum_instance is None: - raise AlgorithmError('Quantum instance must be set.') + raise AlgorithmError("Quantum instance must be set.") if self._quantum_instance.is_statevector: circuit = self.construct_circuit(estimation_problem, k, measurement=False) @@ -106,7 +108,7 @@ def _cos_estimate(self, estimation_problem, k, shots): for i, amplitude in enumerate(statevector): # get bitstring of objective qubits full_state = bin(i)[2:].zfill(circuit.num_qubits)[::-1] - state = ''.join([full_state[i] for i in estimation_problem.objective_qubits]) + state = "".join([full_state[i] for i in estimation_problem.objective_qubits]) # check if it is a good state if estimation_problem.is_good_state(state[::-1]): @@ -134,9 +136,9 @@ def _chernoff(self, cos, shots): confint = [np.maximum(-1, cos - width), np.minimum(1, cos + width)] return confint - def construct_circuit(self, estimation_problem: EstimationProblem, k: int, - measurement: bool = False - ) -> Union[QuantumCircuit, Tuple[QuantumCircuit, List[int]]]: + def construct_circuit( + self, estimation_problem: EstimationProblem, k: int, measurement: bool = False + ) -> Union[QuantumCircuit, Tuple[QuantumCircuit, List[int]]]: r"""Construct the circuit :math:`Q^k X |0\rangle>`. The A operator is the unitary specifying the QAE problem and Q the associated Grover @@ -151,9 +153,11 @@ def construct_circuit(self, estimation_problem: EstimationProblem, k: int, Returns: The circuit :math:`Q^k X |0\rangle`. """ - num_qubits = max(estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits) - circuit = QuantumCircuit(num_qubits, name='circuit') + num_qubits = max( + estimation_problem.state_preparation.num_qubits, + estimation_problem.grover_operator.num_qubits, + ) + circuit = QuantumCircuit(num_qubits, name="circuit") # add classical register if needed if measurement: @@ -176,7 +180,7 @@ def construct_circuit(self, estimation_problem: EstimationProblem, k: int, return circuit - def estimate(self, estimation_problem: EstimationProblem) -> 'FasterAmplitudeEstimationResult': + def estimate(self, estimation_problem: EstimationProblem) -> "FasterAmplitudeEstimationResult": self._num_oracle_calls = 0 user_defined_shots = self.quantum_instance._run_config.shots @@ -208,7 +212,7 @@ def cos_estimate(power, shots): num_steps += 1 if first_stage: num_first_stage_steps += 1 - c = cos_estimate(2**(j - 1), self._shots[0]) + c = cos_estimate(2 ** (j - 1), self._shots[0]) chernoff_ci = self._chernoff(c, self._shots[0]) theta_ci = [np.arccos(x) / (2 ** (j + 1) + 2) for x in chernoff_ci[::-1]] @@ -217,14 +221,16 @@ def cos_estimate(power, shots): v = 2 ** j * np.sum(theta_ci) first_stage = False else: - cos = cos_estimate(2**(j - 1), self._shots[1]) + cos = cos_estimate(2 ** (j - 1), self._shots[1]) cos_2 = cos_estimate(2 ** (j - 1) + 2 ** (j_0 - 1), self._shots[1]) sin = (cos * np.cos(v) - cos_2) / np.sin(v) rho = np.arctan2(sin, cos) n = int(((2 ** (j + 1) + 2) * theta_ci[1] - rho + np.pi / 3) / (2 * np.pi)) - theta_ci = [(2 * np.pi * n + rho + sign * np.pi / 3) / (2 ** (j + 1) + 2) - for sign in [-1, 1]] + theta_ci = [ + (2 * np.pi * n + rho + sign * np.pi / 3) / (2 ** (j + 1) + 2) + for sign in [-1, 1] + ] theta_cis.append(theta_ci) theta = np.mean(theta_ci) @@ -244,8 +250,7 @@ def cos_estimate(power, shots): result.estimation = value result.estimation_processed = problem.post_processing(value) result.confidence_interval = value_ci - result.confidence_interval_processed = tuple(problem.post_processing(x) - for x in value_ci) + result.confidence_interval_processed = tuple(problem.post_processing(x) for x in value_ci) result.theta_intervals = theta_cis # reset shots to what the user had defined diff --git a/qiskit/algorithms/amplitude_estimators/iae.py b/qiskit/algorithms/amplitude_estimators/iae.py index 8112440e681a..e0e3a0e7f467 100644 --- a/qiskit/algorithms/amplitude_estimators/iae.py +++ b/qiskit/algorithms/amplitude_estimators/iae.py @@ -1,4 +1,3 @@ - # This code is part of Qiskit. # # (C) Copyright IBM 2018, 2020. @@ -47,13 +46,14 @@ class IterativeAmplitudeEstimation(AmplitudeEstimator): `arXiv:quant-ph/0005055 `_. """ - def __init__(self, - epsilon_target: float, - alpha: float, - confint_method: str = 'beta', - min_ratio: float = 2, - quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None - ) -> None: + def __init__( + self, + epsilon_target: float, + alpha: float, + confint_method: str = "beta", + min_ratio: float = 2, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: r""" The output of the algorithm is an estimate for the amplitude `a`, that with at least probability 1 - alpha has an error of epsilon. The number of A operator calls scales @@ -76,14 +76,16 @@ def __init__(self, """ # validate ranges of input arguments if not 0 < epsilon_target <= 0.5: - raise ValueError(f'The target epsilon must be in (0, 0.5], but is {epsilon_target}.') + raise ValueError(f"The target epsilon must be in (0, 0.5], but is {epsilon_target}.") if not 0 < alpha < 1: - raise ValueError(f'The confidence level alpha must be in (0, 1), but is {alpha}') + raise ValueError(f"The confidence level alpha must be in (0, 1), but is {alpha}") - if confint_method not in {'chernoff', 'beta'}: - raise ValueError('The confidence interval method must be chernoff or beta, but ' - f'is {confint_method}.') + if confint_method not in {"chernoff", "beta"}: + raise ValueError( + "The confidence interval method must be chernoff or beta, but " + f"is {confint_method}." + ) super().__init__() @@ -106,8 +108,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: @@ -135,8 +138,13 @@ def epsilon_target(self, epsilon: float) -> None: """ self._epsilon = epsilon - def _find_next_k(self, k: int, upper_half_circle: bool, theta_interval: Tuple[float, float], - min_ratio: float = 2.0) -> Tuple[int, bool]: + def _find_next_k( + self, + k: int, + upper_half_circle: bool, + theta_interval: Tuple[float, float], + min_ratio: float = 2.0, + ) -> Tuple[int, bool]: """Find the largest integer k_next, such that the interval (4 * k_next + 2)*theta_interval lies completely in [0, pi] or [pi, 2pi], for theta_interval = (theta_lower, theta_upper). @@ -155,7 +163,7 @@ def _find_next_k(self, k: int, upper_half_circle: bool, theta_interval: Tuple[fl AlgorithmError: if min_ratio is smaller or equal to 1 """ if min_ratio <= 1: - raise AlgorithmError('min_ratio must be larger than 1 to ensure convergence') + raise AlgorithmError("min_ratio must be larger than 1 to ensure convergence") # initialize variables theta_l, theta_u = theta_interval @@ -186,8 +194,9 @@ def _find_next_k(self, k: int, upper_half_circle: bool, theta_interval: Tuple[fl # if we do not find a feasible k, return the old one return int(k), upper_half_circle - def construct_circuit(self, estimation_problem: EstimationProblem, - k: int = 0, measurement: bool = False) -> QuantumCircuit: + def construct_circuit( + self, estimation_problem: EstimationProblem, k: int = 0, measurement: bool = False + ) -> QuantumCircuit: r"""Construct the circuit :math:`\mathcal{Q}^k \mathcal{A} |0\rangle`. The A operator is the unitary specifying the QAE problem and Q the associated Grover @@ -202,9 +211,11 @@ def construct_circuit(self, estimation_problem: EstimationProblem, Returns: The circuit implementing :math:`\mathcal{Q}^k \mathcal{A} |0\rangle`. """ - num_qubits = max(estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits) - circuit = QuantumCircuit(num_qubits, name='circuit') + num_qubits = max( + estimation_problem.state_preparation.num_qubits, + estimation_problem.grover_operator.num_qubits, + ) + circuit = QuantumCircuit(num_qubits, name="circuit") # add classical register if needed if measurement: @@ -227,11 +238,12 @@ def construct_circuit(self, estimation_problem: EstimationProblem, return circuit - def _good_state_probability(self, - problem: EstimationProblem, - counts_or_statevector: Union[Dict[str, int], np.ndarray], - num_state_qubits: int, - ) -> Union[Tuple[int, float], float]: + def _good_state_probability( + self, + problem: EstimationProblem, + counts_or_statevector: Union[Dict[str, int], np.ndarray], + num_state_qubits: int, + ) -> Union[Tuple[int, float], float]: """Get the probability to measure '1' in the last qubit. Args: @@ -263,12 +275,13 @@ def _good_state_probability(self, bitstr = bin(i)[2:].zfill(num_qubits)[-num_state_qubits:][::-1] objectives = [bitstr[index] for index in problem.objective_qubits] if problem.is_good_state(objectives): - prob = prob + np.abs(amplitude)**2 + prob = prob + np.abs(amplitude) ** 2 return prob - def estimate(self, estimation_problem: EstimationProblem - ) -> 'IterativeAmplitudeEstimationResult': + def estimate( + self, estimation_problem: EstimationProblem + ) -> "IterativeAmplitudeEstimationResult": # initialize memory variables powers = [0] # list of powers k: Q^k, (called 'k' in paper) ratios = [] # list of multiplication factors (called 'q' in paper) @@ -278,8 +291,9 @@ def estimate(self, estimation_problem: EstimationProblem num_one_shots = [] # maximum number of rounds - max_rounds = int(np.log(self._min_ratio * np.pi / 8 - / self._epsilon) / np.log(self._min_ratio)) + 1 + max_rounds = ( + int(np.log(self._min_ratio * np.pi / 8 / self._epsilon) / np.log(self._min_ratio)) + 1 + ) upper_half_circle = True # initially theta is in the upper half-circle # for statevector we can directly return the probability to measure 1 @@ -300,8 +314,9 @@ def estimate(self, estimation_problem: EstimationProblem a_confidence_interval = [prob, prob] # type: List[float] a_intervals.append(a_confidence_interval) - theta_i_interval = [np.arccos(1 - 2 * a_i) / 2 / np.pi # type: ignore - for a_i in a_confidence_interval] + theta_i_interval = [ + np.arccos(1 - 2 * a_i) / 2 / np.pi for a_i in a_confidence_interval # type: ignore + ] theta_intervals.append(theta_i_interval) num_oracle_queries = 0 # no Q-oracle call, only a single one to A @@ -314,9 +329,12 @@ def estimate(self, estimation_problem: EstimationProblem num_iterations += 1 # get the next k - k, upper_half_circle = self._find_next_k(powers[-1], upper_half_circle, - theta_intervals[-1], # type: ignore - min_ratio=self._min_ratio) + k, upper_half_circle = self._find_next_k( + powers[-1], + upper_half_circle, + theta_intervals[-1], # type: ignore + min_ratio=self._min_ratio, + ) # store the variables powers.append(k) @@ -332,8 +350,9 @@ def estimate(self, estimation_problem: EstimationProblem # calculate the probability of measuring '1', 'prob' is a_i in the paper num_qubits = circuit.num_qubits - circuit.num_ancillas # type: ignore - one_counts, prob = self._good_state_probability(estimation_problem, counts, - num_qubits) + one_counts, prob = self._good_state_probability( + estimation_problem, counts, num_qubits + ) num_one_shots.append(one_counts) @@ -345,19 +364,21 @@ def estimate(self, estimation_problem: EstimationProblem round_shots = shots round_one_counts = one_counts if num_iterations > 1: - while powers[num_iterations - j] == powers[num_iterations] \ - and num_iterations >= j + 1: + while ( + powers[num_iterations - j] == powers[num_iterations] + and num_iterations >= j + 1 + ): j = j + 1 round_shots += shots round_one_counts += num_one_shots[-j] # compute a_min_i, a_max_i - if self._confint_method == 'chernoff': - a_i_min, a_i_max = _chernoff_confint(prob, round_shots, max_rounds, - self._alpha) + if self._confint_method == "chernoff": + a_i_min, a_i_max = _chernoff_confint(prob, round_shots, max_rounds, self._alpha) else: # 'beta' - a_i_min, a_i_max = _clopper_pearson_confint(round_one_counts, round_shots, - self._alpha / max_rounds) + a_i_min, a_i_max = _clopper_pearson_confint( + round_one_counts, round_shots, self._alpha / max_rounds + ) # compute theta_min_i, theta_max_i if upper_half_circle: @@ -374,8 +395,8 @@ def estimate(self, estimation_problem: EstimationProblem theta_intervals.append([theta_l, theta_u]) # compute a_u_i, a_l_i - a_u = np.sin(2 * np.pi * theta_u)**2 - a_l = np.sin(2 * np.pi * theta_l)**2 + a_u = np.sin(2 * np.pi * theta_u) ** 2 + a_l = np.sin(2 * np.pi * theta_l) ** 2 a_u = cast(float, a_u) a_l = cast(float, a_l) a_intervals.append([a_l, a_u]) @@ -396,8 +417,9 @@ def estimate(self, estimation_problem: EstimationProblem result.confidence_interval = confidence_interval result.estimation_processed = estimation_problem.post_processing(estimation) - confidence_interval = tuple(estimation_problem.post_processing(x) - for x in confidence_interval) + confidence_interval = tuple( + estimation_problem.post_processing(x) for x in confidence_interval + ) result.confidence_interval_processed = confidence_interval result.epsilon_estimated_processed = (confidence_interval[1] - confidence_interval[0]) / 2 result.estimate_intervals = a_intervals @@ -514,8 +536,9 @@ def confidence_interval_processed(self, value: Tuple[float, float]) -> None: self._confidence_interval_processed = value -def _chernoff_confint(value: float, shots: int, max_rounds: int, alpha: float - ) -> Tuple[float, float]: +def _chernoff_confint( + value: float, shots: int, max_rounds: int, alpha: float +) -> Tuple[float, float]: """Compute the Chernoff confidence interval for `shots` i.i.d. Bernoulli trials. The confidence interval is diff --git a/qiskit/algorithms/amplitude_estimators/mlae.py b/qiskit/algorithms/amplitude_estimators/mlae.py index 9754a22c732b..c8f1f5b86cab 100644 --- a/qiskit/algorithms/amplitude_estimators/mlae.py +++ b/qiskit/algorithms/amplitude_estimators/mlae.py @@ -48,10 +48,12 @@ class in named ``MaximumLikelihoodAmplitudeEstimation``. `arXiv:quant-ph/0005055 `_. """ - def __init__(self, evaluation_schedule: Union[List[int], int], - minimizer: Optional[MINIMIZER] = None, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: + def __init__( + self, + evaluation_schedule: Union[List[int], int], + minimizer: Optional[MINIMIZER] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: r""" Args: evaluation_schedule: If a list, the powers applied to the Grover operator. The list @@ -77,12 +79,12 @@ def __init__(self, evaluation_schedule: Union[List[int], int], # get parameters if isinstance(evaluation_schedule, int): if evaluation_schedule < 0: - raise ValueError('The evaluation schedule cannot be < 0.') + raise ValueError("The evaluation schedule cannot be < 0.") - self._evaluation_schedule = [0] + [2**j for j in range(evaluation_schedule)] + self._evaluation_schedule = [0] + [2 ** j for j in range(evaluation_schedule)] else: if any(value < 0 for value in evaluation_schedule): - raise ValueError('The elements of the evaluation schedule cannot be < 0.') + raise ValueError("The elements of the evaluation schedule cannot be < 0.") self._evaluation_schedule = evaluation_schedule @@ -107,8 +109,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: @@ -118,8 +121,9 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance - def construct_circuits(self, estimation_problem: EstimationProblem, - measurement: bool = False) -> List[QuantumCircuit]: + def construct_circuits( + self, estimation_problem: EstimationProblem, measurement: bool = False + ) -> List[QuantumCircuit]: """Construct the Amplitude Estimation w/o QPE quantum circuits. Args: @@ -132,10 +136,12 @@ def construct_circuits(self, estimation_problem: EstimationProblem, # keep track of the Q-oracle queries circuits = [] - num_qubits = max(estimation_problem.state_preparation.num_qubits, - estimation_problem.grover_operator.num_qubits) - q = QuantumRegister(num_qubits, 'q') - qc_0 = QuantumCircuit(q, name='qc_a') # 0 applications of Q, only a single A operator + num_qubits = max( + estimation_problem.state_preparation.num_qubits, + estimation_problem.grover_operator.num_qubits, + ) + q = QuantumRegister(num_qubits, "q") + qc_0 = QuantumCircuit(q, name="qc_a") # 0 applications of Q, only a single A operator # add classical register if needed if measurement: @@ -145,7 +151,7 @@ def construct_circuits(self, estimation_problem: EstimationProblem, qc_0.compose(estimation_problem.state_preparation, inplace=True) for k in self._evaluation_schedule: - qc_k = qc_0.copy(name='qc_a_q_%s' % k) + qc_k = qc_0.copy(name="qc_a_q_%s" % k) if k != 0: qc_k.compose(estimation_problem.grover_operator.power(k), inplace=True) @@ -162,10 +168,12 @@ def construct_circuits(self, estimation_problem: EstimationProblem, return circuits @staticmethod - def compute_confidence_interval(result: 'MaximumLikelihoodAmplitudeEstimationResult', - alpha: float, - kind: str = 'fisher', - apply_post_processing: bool = False) -> Tuple[float, float]: + def compute_confidence_interval( + result: "MaximumLikelihoodAmplitudeEstimationResult", + alpha: float, + kind: str = "fisher", + apply_post_processing: bool = False, + ) -> Tuple[float, float]: """Compute the `alpha` confidence interval using the method `kind`. The confidence level is (1 - `alpha`) and supported kinds are 'fisher', @@ -192,28 +200,30 @@ def compute_confidence_interval(result: 'MaximumLikelihoodAmplitudeEstimationRes if all(isinstance(data, (list, np.ndarray)) for data in result.circuit_results): interval = 2 * [result.estimation] - elif kind in ['likelihood_ratio', 'lr']: + elif kind in ["likelihood_ratio", "lr"]: interval = _likelihood_ratio_confint(result, alpha) - elif kind in ['fisher', 'fi']: + elif kind in ["fisher", "fi"]: interval = _fisher_confint(result, alpha, observed=False) - elif kind in ['observed_fisher', 'observed_information', 'oi']: + elif kind in ["observed_fisher", "observed_information", "oi"]: interval = _fisher_confint(result, alpha, observed=True) if interval is None: - raise NotImplementedError('CI `{}` is not implemented.'.format(kind)) + raise NotImplementedError("CI `{}` is not implemented.".format(kind)) if apply_post_processing: return tuple(result.post_processing(value) for value in interval) return interval - def compute_mle(self, - circuit_results: Union[List[Dict[str, int]], List[np.ndarray]], - estimation_problem: EstimationProblem, - num_state_qubits: Optional[int] = None, - return_counts: bool = False) -> Union[float, Tuple[float, List[float]]]: + def compute_mle( + self, + circuit_results: Union[List[Dict[str, int]], List[np.ndarray]], + estimation_problem: EstimationProblem, + num_state_qubits: Optional[int] = None, + return_counts: bool = False, + ) -> Union[float, Tuple[float, List[float]]]: """Compute the MLE via a grid-search. This is a stable approach if sufficient gridpoints are used. @@ -249,11 +259,14 @@ def loglikelihood(theta): return est_theta, good_counts return est_theta - def estimate(self, estimation_problem: EstimationProblem - ) -> 'MaximumLikelihoodAmplitudeEstimationResult': + def estimate( + self, estimation_problem: EstimationProblem + ) -> "MaximumLikelihoodAmplitudeEstimationResult": if estimation_problem.state_preparation is None: - raise AlgorithmError('Either the state_preparation variable or the a_factory ' - '(deprecated) must be set to run the algorithm.') + raise AlgorithmError( + "Either the state_preparation variable or the a_factory " + "(deprecated) must be set to run the algorithm." + ) result = MaximumLikelihoodAmplitudeEstimationResult() result.evaluation_schedule = self._evaluation_schedule @@ -285,13 +298,14 @@ def estimate(self, estimation_problem: EstimationProblem # run maximum likelihood estimation num_state_qubits = circuits[0].num_qubits - circuits[0].num_ancillas - theta, good_counts = self.compute_mle(result.circuit_results, estimation_problem, - num_state_qubits, True) + theta, good_counts = self.compute_mle( + result.circuit_results, estimation_problem, num_state_qubits, True + ) # store results result.theta = theta result.good_counts = good_counts - result.estimation = np.sin(result.theta)**2 + result.estimation = np.sin(result.theta) ** 2 # not sure why pylint complains, this is a callable and the tests pass # pylint: disable=not-callable @@ -301,10 +315,11 @@ def estimate(self, estimation_problem: EstimationProblem result.num_oracle_queries = result.shots * sum(k for k in result.evaluation_schedule) # compute and store confidence interval - confidence_interval = self.compute_confidence_interval(result, alpha=0.05, kind='fisher') + confidence_interval = self.compute_confidence_interval(result, alpha=0.05, kind="fisher") result.confidence_interval = confidence_interval - result.confidence_interval_processed = tuple(estimation_problem.post_processing(value) - for value in confidence_interval) + result.confidence_interval_processed = tuple( + estimation_problem.post_processing(value) for value in confidence_interval + ) return result @@ -383,9 +398,11 @@ def _safe_max(array, default=(np.pi / 2)): return np.max(array) -def _compute_fisher_information(result: 'MaximumLikelihoodAmplitudeEstimationResult', - num_sum_terms: Optional[int] = None, - observed: bool = False) -> float: +def _compute_fisher_information( + result: "MaximumLikelihoodAmplitudeEstimationResult", + num_sum_terms: Optional[int] = None, + observed: bool = False, +) -> float: """Compute the Fisher information. Args: @@ -431,16 +448,17 @@ def _compute_fisher_information(result: 'MaximumLikelihoodAmplitudeEstimationRes fisher_information = d_loglik ** 2 / len(all_hits) else: - fisher_information = sum(shots_k * (2 * m_k + 1)**2 - for shots_k, m_k in zip(all_hits, evaluation_schedule)) + fisher_information = sum( + shots_k * (2 * m_k + 1) ** 2 for shots_k, m_k in zip(all_hits, evaluation_schedule) + ) fisher_information /= a * (1 - a) return fisher_information -def _fisher_confint(result: MaximumLikelihoodAmplitudeEstimationResult, - alpha: float = 0.05, - observed: bool = False) -> Tuple[float, float]: +def _fisher_confint( + result: MaximumLikelihoodAmplitudeEstimationResult, alpha: float = 0.05, observed: bool = False +) -> Tuple[float, float]: """Compute the `alpha` confidence interval based on the Fisher information. Args: @@ -464,15 +482,18 @@ def _fisher_confint(result: MaximumLikelihoodAmplitudeEstimationResult, fisher_information = _compute_fisher_information(result, observed=True) normal_quantile = norm.ppf(1 - alpha / 2) - confint = np.real(result.estimation) + \ - normal_quantile / np.sqrt(fisher_information) * np.array([-1, 1]) + confint = np.real(result.estimation) + normal_quantile / np.sqrt(fisher_information) * np.array( + [-1, 1] + ) mapped_confint = tuple(result.post_processing(bound) for bound in confint) return mapped_confint -def _likelihood_ratio_confint(result: MaximumLikelihoodAmplitudeEstimationResult, - alpha: float = 0.05, - nevals: Optional[int] = None) -> List[float]: +def _likelihood_ratio_confint( + result: MaximumLikelihoodAmplitudeEstimationResult, + alpha: float = 0.05, + nevals: Optional[int] = None, +) -> List[float]: """Compute the likelihood-ratio confidence interval. Args: @@ -513,17 +534,17 @@ def loglikelihood(theta, one_counts, all_counts): # it might happen that the `above_thres` array is empty, # to still provide a valid result use safe_min/max which # then yield [0, pi/2] - confint = [_safe_min(above_thres, default=0), - _safe_max(above_thres, default=np.pi / 2)] + confint = [_safe_min(above_thres, default=0), _safe_max(above_thres, default=np.pi / 2)] mapped_confint = tuple(result.post_processing(np.sin(bound) ** 2) for bound in confint) return mapped_confint -def _get_counts(circuit_results: List[Union[np.ndarray, List[float], Dict[str, int]]], - estimation_problem: EstimationProblem, - num_state_qubits: int, - ) -> Tuple[List[float], List[int]]: +def _get_counts( + circuit_results: List[Union[np.ndarray, List[float], Dict[str, int]]], + estimation_problem: EstimationProblem, + num_state_qubits: int, +) -> Tuple[List[float], List[int]]: """Get the good and total counts. Returns: @@ -553,7 +574,12 @@ def _get_counts(circuit_results: List[Union[np.ndarray, List[float], Dict[str, i else: for counts in circuit_results: all_hits.append(sum(counts.values())) - one_hits.append(sum(count for bitstr, count in counts.items() - if estimation_problem.is_good_state(bitstr))) + one_hits.append( + sum( + count + for bitstr, count in counts.items() + if estimation_problem.is_good_state(bitstr) + ) + ) return one_hits, all_hits diff --git a/qiskit/algorithms/eigen_solvers/__init__.py b/qiskit/algorithms/eigen_solvers/__init__.py index bb19908fc284..3afff89a5de2 100644 --- a/qiskit/algorithms/eigen_solvers/__init__.py +++ b/qiskit/algorithms/eigen_solvers/__init__.py @@ -15,6 +15,4 @@ from .numpy_eigen_solver import NumPyEigensolver from .eigen_solver import Eigensolver, EigensolverResult -__all__ = ['NumPyEigensolver', - 'Eigensolver', - 'EigensolverResult'] +__all__ = ["NumPyEigensolver", "Eigensolver", "EigensolverResult"] diff --git a/qiskit/algorithms/eigen_solvers/eigen_solver.py b/qiskit/algorithms/eigen_solvers/eigen_solver.py index 3e6ee2b68339..5f34d10e0c16 100644 --- a/qiskit/algorithms/eigen_solvers/eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/eigen_solver.py @@ -30,10 +30,8 @@ class Eigensolver(ABC): @abstractmethod def compute_eigenvalues( - self, - operator: OperatorBase, - aux_operators: Optional[List[Optional[OperatorBase]]] = None - ) -> 'EigensolverResult': + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None + ) -> "EigensolverResult": """ Computes eigenvalues. Operator and aux_operators can be supplied here and if not None will override any already set into algorithm so it can be reused with @@ -63,7 +61,7 @@ def supports_aux_operators(cls) -> bool: class EigensolverResult(AlgorithmResult): - """ Eigensolver Result.""" + """Eigensolver Result.""" def __init__(self) -> None: super().__init__() @@ -73,30 +71,30 @@ def __init__(self) -> None: @property def eigenvalues(self) -> Optional[np.ndarray]: - """ returns eigen values """ + """returns eigen values""" return self._eigenvalues @eigenvalues.setter def eigenvalues(self, value: np.ndarray) -> None: - """ set eigen values """ + """set eigen values""" self._eigenvalues = value @property def eigenstates(self) -> Optional[np.ndarray]: - """ return eigen states """ + """return eigen states""" return self._eigenstates @eigenstates.setter def eigenstates(self, value: np.ndarray) -> None: - """ set eigen states """ + """set eigen states""" self._eigenstates = value @property def aux_operator_eigenvalues(self) -> Optional[np.ndarray]: - """ return aux operator eigen values """ + """return aux operator eigen values""" return self._aux_operator_eigenvalues @aux_operator_eigenvalues.setter def aux_operator_eigenvalues(self, value: np.ndarray) -> None: - """ set aux operator eigen values """ + """set aux operator eigen values""" self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py index 36ae9a2a3d82..c9ab86e8a1a0 100755 --- a/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py +++ b/qiskit/algorithms/eigen_solvers/numpy_eigen_solver.py @@ -42,11 +42,13 @@ class NumPyEigensolver(Eigensolver): operator size, mostly in terms of number of qubits it represents, gets larger. """ - def __init__(self, - k: int = 1, - filter_criterion: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], - bool] = None - ) -> None: + def __init__( + self, + k: int = 1, + filter_criterion: Callable[ + [Union[List, np.ndarray], float, Optional[List[float]]], bool + ] = None, + ) -> None: """ Args: k: How many eigenvalues are to be computed, has a min. value of 1. @@ -57,7 +59,7 @@ def __init__(self, elements that satisfies the criterion is smaller than `k` then the returned list has fewer elements and can even be empty. """ - validate_min('k', k, 1) + validate_min("k", k, 1) super().__init__() self._in_k = k @@ -69,26 +71,31 @@ def __init__(self, @property def k(self) -> int: - """ returns k (number of eigenvalues requested) """ + """returns k (number of eigenvalues requested)""" return self._in_k @k.setter def k(self, k: int) -> None: - """ set k (number of eigenvalues requested) """ - validate_min('k', k, 1) + """set k (number of eigenvalues requested)""" + validate_min("k", k, 1) self._in_k = k self._k = k @property - def filter_criterion(self) -> Optional[ - Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]: - """ returns the filter criterion if set """ + def filter_criterion( + self, + ) -> Optional[Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]: + """returns the filter criterion if set""" return self._filter_criterion @filter_criterion.setter - def filter_criterion(self, filter_criterion: Optional[ - Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]) -> None: - """ set the filter criterion """ + def filter_criterion( + self, + filter_criterion: Optional[ + Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool] + ], + ) -> None: + """set the filter criterion""" self._filter_criterion = filter_criterion @classmethod @@ -99,18 +106,18 @@ def _check_set_k(self, operator: OperatorBase) -> None: if operator is not None: if self._in_k > 2 ** operator.num_qubits: self._k = 2 ** operator.num_qubits - logger.debug("WARNING: Asked for %s eigenvalues but max possible is %s.", - self._in_k, self._k) + logger.debug( + "WARNING: Asked for %s eigenvalues but max possible is %s.", self._in_k, self._k + ) else: self._k = self._in_k - def _solve(self, - operator: OperatorBase) -> None: + def _solve(self, operator: OperatorBase) -> None: sp_mat = operator.to_spmatrix() # If matrix is diagonal, the elements on the diagonal are the eigenvalues. Solve by sorting. if scisparse.csr_matrix(sp_mat.diagonal()).nnz == sp_mat.nnz: diag = sp_mat.diagonal() - indices = np.argsort(diag)[:self._k] + indices = np.argsort(diag)[: self._k] eigval = diag[indices] eigvec = np.zeros((sp_mat.shape[0], self._k)) for i, idx in enumerate(indices): @@ -120,36 +127,37 @@ def _solve(self, logger.debug("SciPy doesn't support to get all eigenvalues, using NumPy instead.") eigval, eigvec = np.linalg.eig(operator.to_matrix()) else: - eigval, eigvec = scisparse.linalg.eigs(operator.to_spmatrix(), - k=self._k, which='SR') - indices = np.argsort(eigval)[:self._k] + eigval, eigvec = scisparse.linalg.eigs( + operator.to_spmatrix(), k=self._k, which="SR" + ) + indices = np.argsort(eigval)[: self._k] eigval = eigval[indices] eigvec = eigvec[:, indices] self._ret.eigenvalues = eigval self._ret.eigenstates = eigvec.T - def _get_ground_state_energy(self, - operator: OperatorBase) -> None: + def _get_ground_state_energy(self, operator: OperatorBase) -> None: if self._ret.eigenvalues is None or self._ret.eigenstates is None: self._solve(operator) - def _get_energies(self, - operator: OperatorBase, - aux_operators: Optional[List[OperatorBase]]) -> None: + def _get_energies( + self, operator: OperatorBase, aux_operators: Optional[List[OperatorBase]] + ) -> None: if self._ret.eigenvalues is None or self._ret.eigenstates is None: self._solve(operator) if aux_operators is not None: aux_op_vals = [] for i in range(self._k): - aux_op_vals.append(self._eval_aux_operators(aux_operators, - self._ret.eigenstates[i])) + aux_op_vals.append( + self._eval_aux_operators(aux_operators, self._ret.eigenstates[i]) + ) self._ret.aux_operator_eigenvalues = aux_op_vals @staticmethod - def _eval_aux_operators(aux_operators: List[OperatorBase], - wavefn, - threshold: float = 1e-12) -> np.ndarray: + def _eval_aux_operators( + aux_operators: List[OperatorBase], wavefn, threshold: float = 1e-12 + ) -> np.ndarray: values = [] # type: List[Tuple[float, int]] for operator in aux_operators: if operator is None: @@ -170,9 +178,7 @@ def _eval_aux_operators(aux_operators: List[OperatorBase], return np.array(values, dtype=object) def compute_eigenvalues( - self, - operator: OperatorBase, - aux_operators: Optional[List[Optional[OperatorBase]]] = None + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> EigensolverResult: super().compute_eigenvalues(operator, aux_operators) @@ -233,5 +239,5 @@ def compute_eigenvalues( if self._ret.eigenstates is not None: self._ret.eigenstates = ListOp([StateFn(vec) for vec in self._ret.eigenstates]) - logger.debug('EigensolverResult:\n%s', self._ret) + logger.debug("EigensolverResult:\n%s", self._ret) return self._ret diff --git a/qiskit/algorithms/exceptions.py b/qiskit/algorithms/exceptions.py index bc004035e692..1f830a3cba95 100644 --- a/qiskit/algorithms/exceptions.py +++ b/qiskit/algorithms/exceptions.py @@ -17,4 +17,5 @@ class AlgorithmError(QiskitError): """For Algorithm specific errors.""" + pass diff --git a/qiskit/algorithms/factorizers/__init__.py b/qiskit/algorithms/factorizers/__init__.py index eb4dfffdf98c..76e3b59f2492 100644 --- a/qiskit/algorithms/factorizers/__init__.py +++ b/qiskit/algorithms/factorizers/__init__.py @@ -15,6 +15,6 @@ from .shor import Shor, ShorResult __all__ = [ - 'Shor', - 'ShorResult', + "Shor", + "ShorResult", ] diff --git a/qiskit/algorithms/factorizers/shor.py b/qiskit/algorithms/factorizers/shor.py index 99a5a9e5a9f4..ce279a995bb1 100644 --- a/qiskit/algorithms/factorizers/shor.py +++ b/qiskit/algorithms/factorizers/shor.py @@ -48,9 +48,9 @@ class Shor: See also https://arxiv.org/abs/quant-ph/0205095 """ - def __init__(self, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: + def __init__( + self, quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None + ) -> None: """ Args: quantum_instance: Quantum Instance or Backend @@ -73,13 +73,14 @@ def __init__(self, @property def quantum_instance(self) -> Optional[QuantumInstance]: - """ Returns quantum instance. """ + """Returns quantum instance.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: - """ Sets quantum instance. """ + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: + """Sets quantum instance.""" if isinstance(quantum_instance, (BaseBackend, Backend)): quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance @@ -90,7 +91,7 @@ def _get_angles(self, a: int) -> np.ndarray: angles = np.zeros([self._n + 1]) for i in range(0, self._n + 1): for j in range(i, self._n + 1): - if s[j] == '1': + if s[j] == "1": angles[self._n - i] += math.pow(2, -(j - i)) angles[self._n - i] *= np.pi return angles[::-1] @@ -103,10 +104,9 @@ def _phi_add_gate(size: int, angles: Union[np.ndarray, ParameterVector]) -> Gate circuit.p(angle, i) return circuit.to_gate() - def _double_controlled_phi_add_mod_N(self, - num_qubits: int, - angles: Union[np.ndarray, ParameterVector] - ) -> QuantumCircuit: + def _double_controlled_phi_add_mod_N( + self, num_qubits: int, angles: Union[np.ndarray, ParameterVector] + ) -> QuantumCircuit: """Creates a circuit which implements double-controlled modular addition by a.""" circuit = QuantumCircuit(num_qubits, name="ccphi_add_mod_N") @@ -142,11 +142,9 @@ def _double_controlled_phi_add_mod_N(self, def _controlled_multiple_mod_N(self, num_qubits: int, N: int, a: int) -> Instruction: """Implements modular multiplication by a as an instruction.""" - circuit = QuantumCircuit( - num_qubits, name="multiply_by_{}_mod_{}".format(a % N, N) - ) - down = circuit.qubits[1: self._n + 1] - aux = circuit.qubits[self._n + 1:] + circuit = QuantumCircuit(num_qubits, name="multiply_by_{}_mod_{}".format(a % N, N)) + down = circuit.qubits[1 : self._n + 1] + aux = circuit.qubits[self._n + 1 :] qubits = [aux[i] for i in reversed(range(self._n + 1))] ctl_up = 0 ctl_aux = aux[-1] @@ -183,10 +181,7 @@ def _controlled_multiple_mod_N(self, num_qubits: int, N: int, a: int) -> Instruc circuit.append(self._iqft, qubits) return circuit.to_instruction() - def construct_circuit(self, - N: int, - a: int = 2, - measurement: bool = False) -> QuantumCircuit: + def construct_circuit(self, N: int, a: int = 2, measurement: bool = False) -> QuantumCircuit: """Construct circuit. Args: @@ -200,15 +195,15 @@ def construct_circuit(self, Raises: ValueError: Invalid N """ - validate_min('N', N, 3) - validate_min('a', a, 2) + validate_min("N", N, 3) + validate_min("a", a, 2) # check the input integer if N < 1 or N % 2 == 0: - raise ValueError('The input needs to be an odd integer greater than 1.') + raise ValueError("The input needs to be an odd integer greater than 1.") if a >= N or math.gcd(a, N) != 1: - raise ValueError('The integer a needs to satisfy a < N and gcd(a, N) = 1.') + raise ValueError("The integer a needs to satisfy a < N and gcd(a, N) = 1.") # Get n value used in Shor's algorithm, to know how many qubits are used self._n = math.ceil(math.log(N, 2)) @@ -216,17 +211,16 @@ def construct_circuit(self, self._iqft.num_qubits = self._n + 1 # quantum register where the sequential QFT is performed - self._up_qreg = QuantumRegister(2 * self._n, name='up') + self._up_qreg = QuantumRegister(2 * self._n, name="up") # quantum register where the multiplications are made - self._down_qreg = QuantumRegister(self._n, name='down') + self._down_qreg = QuantumRegister(self._n, name="down") # auxiliary quantum register used in addition and multiplication - self._aux_qreg = QuantumRegister(self._n + 2, name='aux') + self._aux_qreg = QuantumRegister(self._n + 2, name="aux") # Create Quantum Circuit - circuit = QuantumCircuit(self._up_qreg, - self._down_qreg, - self._aux_qreg, - name="Shor(N={}, a={})".format(N, a)) + circuit = QuantumCircuit( + self._up_qreg, self._down_qreg, self._aux_qreg, name="Shor(N={}, a={})".format(N, a) + ) # Create gates to perform addition/subtraction by N in Fourier Space self._phi_add_N = self._phi_add_gate(self._aux_qreg.size - 1, self._get_angles(N)) @@ -243,18 +237,18 @@ def construct_circuit(self, for i, ctl_up in enumerate(self._up_qreg): # type: ignore a_aux = int(pow(a, pow(2, i))) controlled_multiple_mod_N = self._controlled_multiple_mod_N( - len(self._down_qreg) + len(self._aux_qreg) + 1, N, a_aux, - ) - circuit.append( - controlled_multiple_mod_N, [ctl_up, *self._down_qreg, *self._aux_qreg] + len(self._down_qreg) + len(self._aux_qreg) + 1, + N, + a_aux, ) + circuit.append(controlled_multiple_mod_N, [ctl_up, *self._down_qreg, *self._aux_qreg]) # Apply inverse QFT iqft = QFT(len(self._up_qreg)).inverse().to_instruction() circuit.append(iqft, self._up_qreg) if measurement: - up_cqreg = ClassicalRegister(2 * self._n, name='m') + up_cqreg = ClassicalRegister(2 * self._n, name="m") circuit.add_register(up_cqreg) circuit.measure(self._up_qreg, up_cqreg) @@ -265,6 +259,7 @@ def construct_circuit(self, @staticmethod def modinv(a: int, m: int) -> int: """Returns the modular multiplicative inverse of a with respect to the modulus m.""" + def egcd(a: int, b: int) -> Tuple[int, int, int]: if a == 0: return b, 0, 1 @@ -274,20 +269,22 @@ def egcd(a: int, b: int) -> Tuple[int, int, int]: g, x, _ = egcd(a, m) if g != 1: - raise ValueError("The greatest common divisor of {} and {} is {}, so the " - "modular inverse does not exist.".format(a, m, g)) + raise ValueError( + "The greatest common divisor of {} and {} is {}, so the " + "modular inverse does not exist.".format(a, m, g) + ) return x % m def _get_factors(self, N: int, a: int, measurement: str) -> Optional[List[int]]: """Apply the continued fractions to find r and the gcd to find the desired factors.""" x_final = int(measurement, 2) - logger.info('In decimal, x_final value for this result is: %s.', x_final) + logger.info("In decimal, x_final value for this result is: %s.", x_final) if x_final <= 0: - fail_reason = 'x_final value is <= 0, there are no continued fractions.' + fail_reason = "x_final value is <= 0, there are no continued fractions." else: fail_reason = None - logger.debug('Running continued fractions for this case.') + logger.debug("Running continued fractions for this case.") # Calculate T and x/T T_upper = len(measurement) @@ -299,8 +296,8 @@ def _get_factors(self, N: int, a: int, measurement: str) -> Optional[List[int]]: # Initialize the first values according to CF rule i = 0 - b = array.array('i') - t = array.array('f') + b = array.array("i") + t = array.array("f") b.append(math.floor(x_over_T)) t.append(x_over_T - b[i]) @@ -320,7 +317,7 @@ def _get_factors(self, N: int, a: int, measurement: str) -> Optional[List[int]]: i += 1 if denominator % 2 == 1: - logger.debug('Odd denominator, will try next iteration of continued fractions.') + logger.debug("Odd denominator, will try next iteration of continued fractions.") continue # Denominator is even, try to get factors of N @@ -331,7 +328,7 @@ def _get_factors(self, N: int, a: int, measurement: str) -> Optional[List[int]]: # Check if the value is too big or not if exponential > 1000000000: - fail_reason = 'denominator of continued fraction is too big.' + fail_reason = "denominator of continued fraction is too big." else: # The value is not too big, # get the right values and do the proper gcd() @@ -342,19 +339,20 @@ def _get_factors(self, N: int, a: int, measurement: str) -> Optional[List[int]]: # Check if the factors found are trivial factors or are the desired factors if any(factor in {1, N} for factor in (one_factor, other_factor)): - logger.debug('Found just trivial factors, not good enough.') + logger.debug("Found just trivial factors, not good enough.") # Check if the number has already been found, # (use i - 1 because i was already incremented) if t[i - 1] == 0: - fail_reason = 'the continued fractions found exactly x_final/(2^(2n)).' + fail_reason = "the continued fractions found exactly x_final/(2^(2n))." else: # Successfully factorized N return sorted((one_factor, other_factor)) # Search for factors failed, write the reason for failure to the debug logs logger.debug( - 'Cannot find factors from measurement %s because %s', - measurement, fail_reason or 'it took too many attempts.' + "Cannot find factors from measurement %s because %s", + measurement, + fail_reason or "it took too many attempts.", ) return None @@ -372,14 +370,15 @@ def _calculate_continued_fraction(b: array.array) -> int: # Get the denominator from the value obtained frac = fractions.Fraction(x_over_T).limit_denominator() - logger.debug('Approximation number %s of continued fractions:', len(b)) + logger.debug("Approximation number %s of continued fractions:", len(b)) logger.debug("Numerator:%s \t\t Denominator: %s.", frac.numerator, frac.denominator) return frac.denominator - def factor(self, - N: int, - a: int = 2, - ) -> 'ShorResult': + def factor( + self, + N: int, + a: int = 2, + ) -> "ShorResult": """Execute the algorithm. The input integer :math:`N` to be factored is expected to be odd and greater than 2. @@ -399,41 +398,43 @@ def factor(self, AlgorithmError: If a quantum instance or backend has not been provided """ - validate_min('N', N, 3) - validate_min('a', a, 2) + validate_min("N", N, 3) + validate_min("a", a, 2) # check the input integer if N < 1 or N % 2 == 0: - raise ValueError('The input needs to be an odd integer greater than 1.') + raise ValueError("The input needs to be an odd integer greater than 1.") if a >= N or math.gcd(a, N) != 1: - raise ValueError('The integer a needs to satisfy a < N and gcd(a, N) = 1.') + raise ValueError("The integer a needs to satisfy a < N and gcd(a, N) = 1.") if self.quantum_instance is None: - raise AlgorithmError("A QuantumInstance or Backend " - "must be supplied to run the quantum algorithm.") + raise AlgorithmError( + "A QuantumInstance or Backend " "must be supplied to run the quantum algorithm." + ) result = ShorResult() # check if the input integer is a power tf, b, p = is_power(N, return_decomposition=True) if tf: - logger.info('The input integer is a power: %s=%s^%s.', N, b, p) + logger.info("The input integer is a power: %s=%s^%s.", N, b, p) result.factors.append(b) if not result.factors: - logger.debug('Running with N=%s and a=%s.', N, a) + logger.debug("Running with N=%s and a=%s.", N, a) if self._quantum_instance.is_statevector: circuit = self.construct_circuit(N=N, a=a, measurement=False) - logger.warning('The statevector_simulator might lead to ' - 'subsequent computation using too much memory.') + logger.warning( + "The statevector_simulator might lead to " + "subsequent computation using too much memory." + ) result = self._quantum_instance.execute(circuit) complete_state_vec = result.get_statevector(circuit) # TODO: this uses too much memory up_qreg_density_mat = partial_trace( - complete_state_vec, - range(2 * self._n, 4 * self._n + 2) + complete_state_vec, range(2 * self._n, 4 * self._n + 2) ) up_qreg_density_mat_diag = np.diag(up_qreg_density_mat) @@ -455,10 +456,7 @@ def factor(self, factors = self._get_factors(N, a, measurement) if factors: - logger.info( - 'Found factors %s from measurement %s.', - factors, measurement - ) + logger.info("Found factors %s from measurement %s.", factors, measurement) result.successful_counts = result.successful_counts + 1 if factors not in result.factors: result.factors.append(factors) @@ -467,7 +465,7 @@ def factor(self, class ShorResult(AlgorithmResult): - """ Shor Result.""" + """Shor Result.""" def __init__(self) -> None: super().__init__() @@ -477,30 +475,30 @@ def __init__(self) -> None: @property def factors(self) -> List[List[int]]: - """ returns factors """ + """returns factors""" return self._factors @factors.setter def factors(self, value: List[List[int]]) -> None: - """ set factors """ + """set factors""" self._factors = value @property def total_counts(self) -> int: - """ returns total counts """ + """returns total counts""" return self._total_counts @total_counts.setter def total_counts(self, value: int) -> None: - """ set total counts """ + """set total counts""" self._total_counts = value @property def successful_counts(self) -> int: - """ returns successful counts """ + """returns successful counts""" return self._successful_counts @successful_counts.setter def successful_counts(self, value: int) -> None: - """ set successful counts """ + """set successful counts""" self._successful_counts = value diff --git a/qiskit/algorithms/linear_solvers/__init__.py b/qiskit/algorithms/linear_solvers/__init__.py index 3fefc7599150..9a8105d7f9b9 100644 --- a/qiskit/algorithms/linear_solvers/__init__.py +++ b/qiskit/algorithms/linear_solvers/__init__.py @@ -16,9 +16,4 @@ from .numpy_linear_solver import NumPyLinearSolver from .linear_solver import LinearSolver, LinearSolverResult -__all__ = [ - 'HHL', - 'NumPyLinearSolver', - 'LinearSolver', - 'LinearSolverResult' -] +__all__ = ["HHL", "NumPyLinearSolver", "LinearSolver", "LinearSolverResult"] diff --git a/qiskit/algorithms/linear_solvers/hhl.py b/qiskit/algorithms/linear_solvers/hhl.py index 26ae39eaccfa..edc364183882 100644 --- a/qiskit/algorithms/linear_solvers/hhl.py +++ b/qiskit/algorithms/linear_solvers/hhl.py @@ -19,8 +19,16 @@ from qiskit.circuit.library import PhaseEstimation from qiskit.circuit.library.arithmetic.piecewise_chebyshev import PiecewiseChebyshev from qiskit.circuit.library.arithmetic.exact_reciprocal import ExactReciprocal -from qiskit.opflow import (Z, I, StateFn, TensoredOp, ExpectationBase, CircuitSampler, ListOp, - ExpectationFactory) +from qiskit.opflow import ( + Z, + I, + StateFn, + TensoredOp, + ExpectationBase, + CircuitSampler, + ListOp, + ExpectationFactory, +) from qiskit.providers import Backend, BaseBackend from qiskit.quantum_info.operators.base_operator import BaseOperator from qiskit.utils import QuantumInstance @@ -88,11 +96,12 @@ class HHL(LinearSolver): """ - def __init__(self, - epsilon: float = 1e-2, - expectation: Optional[ExpectationBase] = None, - quantum_instance: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None)\ - -> None: + def __init__( + self, + epsilon: float = 1e-2, + expectation: Optional[ExpectationBase] = None, + quantum_instance: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None, + ) -> None: r""" Args: epsilon: Error tolerance of the approximation to the solution, i.e. if :math:`x` is the @@ -135,8 +144,9 @@ def quantum_instance(self) -> Optional[QuantumInstance]: return self._sampler.quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: """Set quantum instance. Args: @@ -157,7 +167,7 @@ def scaling(self, scaling: float) -> None: @property def expectation(self) -> ExpectationBase: """The expectation value algorithm used to construct the expectation measurement from - the observable. """ + the observable.""" return self._expectation @expectation.setter @@ -211,13 +221,15 @@ def _calculate_norm(self, qc: QuantumCircuit) -> float: return np.real(np.sqrt(norm_2) / self.scaling) - def _calculate_observable(self, solution: QuantumCircuit, - observable: Optional[Union[LinearSystemObservable, - BaseOperator]] = None, - observable_circuit: Optional[QuantumCircuit] = None, - post_processing: Optional[Callable[[Union[float, List[float]]], - Union[float, List[float]]]] = - None) -> Tuple[Union[float, List[float]], Union[float, List[float]]]: + def _calculate_observable( + self, + solution: QuantumCircuit, + observable: Optional[Union[LinearSystemObservable, BaseOperator]] = None, + observable_circuit: Optional[QuantumCircuit] = None, + post_processing: Optional[ + Callable[[Union[float, List[float]]], Union[float, List[float]]] + ] = None, + ) -> Tuple[Union[float, List[float]], Union[float, List[float]]]: """Calculates the value of the observable(s) given. Args: @@ -294,8 +306,9 @@ def _calculate_observable(self, solution: QuantumCircuit, return result, expectation_results - def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], - vector: Union[np.ndarray, QuantumCircuit]) -> QuantumCircuit: + def construct_circuit( + self, matrix: Union[np.ndarray, QuantumCircuit], vector: Union[np.ndarray, QuantumCircuit] + ) -> QuantumCircuit: """Construct the HHL circuit. Args: @@ -316,8 +329,7 @@ def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], elif isinstance(vector, np.ndarray): nb = int(np.log2(len(vector))) vector_circuit = QuantumCircuit(nb) - vector_circuit.isometry(vector / np.linalg.norm(vector), - list(range(nb)), None) + vector_circuit.isometry(vector / np.linalg.norm(vector), list(range(nb)), None) # If state preparation is probabilistic the number of qubit flags should increase nf = 1 @@ -336,22 +348,26 @@ def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], if not np.allclose(matrix, matrix.conj().T): raise ValueError("Input matrix must be hermitian!") if matrix.shape[0] != 2 ** vector_circuit.num_qubits: - raise ValueError("Input vector dimension does not match input " - "matrix dimension! Vector dimension: " + - str(vector_circuit.num_qubits) + - ". Matrix dimension: " + - str(matrix.shape[0])) + raise ValueError( + "Input vector dimension does not match input " + "matrix dimension! Vector dimension: " + + str(vector_circuit.num_qubits) + + ". Matrix dimension: " + + str(matrix.shape[0]) + ) matrix_circuit = NumPyMatrix(matrix, evolution_time=2 * np.pi) else: - raise ValueError(f'Invalid type for matrix: {type(matrix)}.') + raise ValueError(f"Invalid type for matrix: {type(matrix)}.") # Set the tolerance for the matrix approximation if hasattr(matrix_circuit, "tolerance"): matrix_circuit.tolerance = self._epsilon_a # check if the matrix can calculate the condition number and store the upper bound - if hasattr(matrix_circuit, "condition_bounds") and matrix_circuit.condition_bounds() is not\ - None: + if ( + hasattr(matrix_circuit, "condition_bounds") + and matrix_circuit.condition_bounds() is not None + ): kappa = matrix_circuit.condition_bounds()[1] else: kappa = 1 @@ -384,9 +400,21 @@ def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], # Calculate the degree of the polynomial and the number of intervals r = 2 * constant / a + np.sqrt(np.abs(1 - (2 * constant / a) ** 2)) - degree = min(nb, int(np.log(1 + (16.23 * np.sqrt(np.log(r) ** 2 + (np.pi / 2) ** 2) * - kappa * (2 * kappa - self._epsilon_r)) / - self._epsilon_r))) + degree = min( + nb, + int( + np.log( + 1 + + ( + 16.23 + * np.sqrt(np.log(r) ** 2 + (np.pi / 2) ** 2) + * kappa + * (2 * kappa - self._epsilon_r) + ) + / self._epsilon_r + ) + ), + ) num_intervals = int(np.ceil(np.log((num_values - 1) / a) / np.log(5))) # Calculate breakpoints and polynomials @@ -399,8 +427,9 @@ def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], if i == num_intervals - 1: breakpoints.append(num_values - 1) - reciprocal_circuit = PiecewiseChebyshev(lambda x: np.arcsin(constant / x), degree, - breakpoints, nl) + reciprocal_circuit = PiecewiseChebyshev( + lambda x: np.arcsin(constant / x), degree, breakpoints, nl + ) na = max(matrix_circuit.num_ancillas, reciprocal_circuit.num_ancillas) # Initialise the quantum registers @@ -420,31 +449,41 @@ def construct_circuit(self, matrix: Union[np.ndarray, QuantumCircuit], # QPE phase_estimation = PhaseEstimation(nl, matrix_circuit) if na > 0: - qc.append(phase_estimation, ql[:] + qb[:] + qa[:matrix_circuit.num_ancillas]) + qc.append(phase_estimation, ql[:] + qb[:] + qa[: matrix_circuit.num_ancillas]) else: qc.append(phase_estimation, ql[:] + qb[:]) # Conditioned rotation if self._exact_reciprocal: qc.append(reciprocal_circuit, ql[::-1] + [qf[0]]) else: - qc.append(reciprocal_circuit.to_instruction(), ql[:] + [qf[0]] + - qa[:reciprocal_circuit.num_ancillas]) + qc.append( + reciprocal_circuit.to_instruction(), + ql[:] + [qf[0]] + qa[: reciprocal_circuit.num_ancillas], + ) # QPE inverse if na > 0: - qc.append(phase_estimation.inverse(), ql[:] + qb[:] + - qa[:matrix_circuit.num_ancillas]) + qc.append(phase_estimation.inverse(), ql[:] + qb[:] + qa[: matrix_circuit.num_ancillas]) else: qc.append(phase_estimation.inverse(), ql[:] + qb[:]) return qc - def solve(self, matrix: Union[np.ndarray, QuantumCircuit], - vector: Union[np.ndarray, QuantumCircuit], - observable: Optional[Union[LinearSystemObservable, BaseOperator, - List[LinearSystemObservable], List[BaseOperator]]] = None, - observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, - post_processing: Optional[Callable[[Union[float, List[float]]], - Union[float, List[float]]]] = None) \ - -> LinearSolverResult: + def solve( + self, + matrix: Union[np.ndarray, QuantumCircuit], + vector: Union[np.ndarray, QuantumCircuit], + observable: Optional[ + Union[ + LinearSystemObservable, + BaseOperator, + List[LinearSystemObservable], + List[BaseOperator], + ] + ] = None, + observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, + post_processing: Optional[ + Callable[[Union[float, List[float]]], Union[float, List[float]]] + ] = None, + ) -> LinearSolverResult: """Tries to solve the given linear system of equations. Args: @@ -468,16 +507,18 @@ def solve(self, matrix: Union[np.ndarray, QuantumCircuit], # verify input if observable is not None: if observable_circuit is not None or post_processing is not None: - raise ValueError('If observable is passed, observable_circuit and post_processing ' - 'cannot be set.') + raise ValueError( + "If observable is passed, observable_circuit and post_processing " + "cannot be set." + ) solution = LinearSolverResult() solution.state = self.construct_circuit(matrix, vector) solution.euclidean_norm = self._calculate_norm(solution.state) if observable is not None or observable_circuit is not None: - solution.observable, solution.circuit_results = \ - self._calculate_observable(solution.state, observable, observable_circuit, - post_processing) + solution.observable, solution.circuit_results = self._calculate_observable( + solution.state, observable, observable_circuit, post_processing + ) return solution diff --git a/qiskit/algorithms/linear_solvers/linear_solver.py b/qiskit/algorithms/linear_solvers/linear_solver.py index f7c8c25107be..093d8cd30e93 100644 --- a/qiskit/algorithms/linear_solvers/linear_solver.py +++ b/qiskit/algorithms/linear_solvers/linear_solver.py @@ -96,14 +96,23 @@ class LinearSolver(ABC): """An abstract class for linear system solvers in Qiskit.""" @abstractmethod - def solve(self, matrix: Union[np.ndarray, QuantumCircuit], - vector: Union[np.ndarray, QuantumCircuit], - observable: Optional[Union[LinearSystemObservable, BaseOperator, - List[LinearSystemObservable], List[BaseOperator]]] = None, - observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, - post_processing: Optional[Callable[[Union[float, List[float]]], - Union[float, List[float]]]] = None) \ - -> LinearSolverResult: + def solve( + self, + matrix: Union[np.ndarray, QuantumCircuit], + vector: Union[np.ndarray, QuantumCircuit], + observable: Optional[ + Union[ + LinearSystemObservable, + BaseOperator, + List[LinearSystemObservable], + List[BaseOperator], + ] + ] = None, + observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, + post_processing: Optional[ + Callable[[Union[float, List[float]]], Union[float, List[float]]] + ] = None, + ) -> LinearSolverResult: """Solve the system and compute the observable(s) Args: diff --git a/qiskit/algorithms/linear_solvers/matrices/__init__.py b/qiskit/algorithms/linear_solvers/matrices/__init__.py index 295ae80ef007..97b34d9c624e 100644 --- a/qiskit/algorithms/linear_solvers/matrices/__init__.py +++ b/qiskit/algorithms/linear_solvers/matrices/__init__.py @@ -16,8 +16,4 @@ from .numpy_matrix import NumPyMatrix from .tridiagonal_toeplitz import TridiagonalToeplitz -__all__ = [ - 'LinearSystemMatrix', - 'NumPyMatrix', - 'TridiagonalToeplitz' -] +__all__ = ["LinearSystemMatrix", "NumPyMatrix", "TridiagonalToeplitz"] diff --git a/qiskit/algorithms/linear_solvers/matrices/linear_system_matrix.py b/qiskit/algorithms/linear_solvers/matrices/linear_system_matrix.py index d8e89bfdcc26..745fdca2b4f2 100644 --- a/qiskit/algorithms/linear_solvers/matrices/linear_system_matrix.py +++ b/qiskit/algorithms/linear_solvers/matrices/linear_system_matrix.py @@ -22,8 +22,13 @@ class LinearSystemMatrix(BlueprintCircuit, ABC): """Base class for linear system matrices.""" - def __init__(self, num_state_qubits: int, tolerance: float, evolution_time: float, - name: str = 'ls_matrix') -> None: + def __init__( + self, + num_state_qubits: int, + tolerance: float, + evolution_time: float, + name: str = "ls_matrix", + ) -> None: """ Args: num_state_qubits: the number of qubits where the unitary acts. diff --git a/qiskit/algorithms/linear_solvers/matrices/numpy_matrix.py b/qiskit/algorithms/linear_solvers/matrices/numpy_matrix.py index ef83da6af18a..9857498da071 100644 --- a/qiskit/algorithms/linear_solvers/matrices/numpy_matrix.py +++ b/qiskit/algorithms/linear_solvers/matrices/numpy_matrix.py @@ -44,8 +44,13 @@ class NumPyMatrix(LinearSystemMatrix): qc.append(matrix.power(power).control(), list(range(circ_qubits))) """ - def __init__(self, matrix: np.ndarray, tolerance: float = 1e-2, evolution_time: float = 1.0, - name: str = 'np_matrix') -> None: + def __init__( + self, + matrix: np.ndarray, + tolerance: float = 1e-2, + evolution_time: float = 1.0, + name: str = "np_matrix", + ) -> None: """ Args: matrix: The matrix defining the linear system problem. @@ -65,8 +70,12 @@ def __init__(self, matrix: np.ndarray, tolerance: float = 1e-2, evolution_time: self.tolerance = tolerance self.evolution_time = evolution_time self.matrix = matrix - super().__init__(num_state_qubits=int(np.log2(matrix.shape[0])), tolerance=tolerance, - evolution_time=evolution_time, name=name) + super().__init__( + num_state_qubits=int(np.log2(matrix.shape[0])), + tolerance=tolerance, + evolution_time=evolution_time, + name=name, + ) @property def num_state_qubits(self) -> int: @@ -170,7 +179,7 @@ def _reset_registers(self, num_state_qubits: int) -> None: Args: num_state_qubits: The number of qubits to represent the matrix. """ - qr_state = QuantumRegister(num_state_qubits, 'state') + qr_state = QuantumRegister(num_state_qubits, "state") self.qregs = [qr_state] self._qubits = qr_state[:] diff --git a/qiskit/algorithms/linear_solvers/matrices/tridiagonal_toeplitz.py b/qiskit/algorithms/linear_solvers/matrices/tridiagonal_toeplitz.py index 44cda5d78469..598e2723420c 100644 --- a/qiskit/algorithms/linear_solvers/matrices/tridiagonal_toeplitz.py +++ b/qiskit/algorithms/linear_solvers/matrices/tridiagonal_toeplitz.py @@ -56,9 +56,16 @@ class TridiagonalToeplitz(LinearSystemMatrix): qc.append(matrix.power(power).control(), list(range(circ_qubits))) """ - def __init__(self, num_state_qubits: int, main_diag: float, off_diag: float, - tolerance: float = 1e-2, evolution_time: float = 1.0, trotter_steps: int = 1, - name: str = 'tridi') -> None: + def __init__( + self, + num_state_qubits: int, + main_diag: float, + off_diag: float, + tolerance: float = 1e-2, + evolution_time: float = 1.0, + trotter_steps: int = 1, + name: str = "tridi", + ) -> None: """ Args: num_state_qubits: the number of qubits where the unitary acts. @@ -80,8 +87,12 @@ def __init__(self, num_state_qubits: int, main_diag: float, off_diag: float, # store parameters self.main_diag = main_diag self.off_diag = off_diag - super().__init__(num_state_qubits=num_state_qubits, tolerance=tolerance, - evolution_time=evolution_time, name=name) + super().__init__( + num_state_qubits=num_state_qubits, + tolerance=tolerance, + evolution_time=evolution_time, + name=name, + ) self.trotter_steps = trotter_steps @property @@ -162,8 +173,9 @@ def evolution_time(self, evolution_time: float) -> None: """ self._evolution_time = evolution_time # Update the number of trotter steps. Max 7 for now, upper bounds too loose. - self.trotter_steps = int(np.ceil(np.sqrt(((evolution_time * np.abs(self.off_diag)) ** 3) / - 2 / self.tolerance))) + self.trotter_steps = int( + np.ceil(np.sqrt(((evolution_time * np.abs(self.off_diag)) ** 3) / 2 / self.tolerance)) + ) @property def trotter_steps(self) -> int: @@ -181,9 +193,12 @@ def trotter_steps(self, trotter_steps: int) -> None: @property def matrix(self) -> np.ndarray: """Returns the tridiagonal Toeplitz matrix built according to the main and off diagonal - entries.""" - matrix = diags([self.off_diag, self.main_diag, self.off_diag], [-1, 0, 1], - shape=(2 ** self.num_state_qubits, 2 ** self.num_state_qubits)).toarray() + entries.""" + matrix = diags( + [self.off_diag, self.main_diag, self.off_diag], + [-1, 0, 1], + shape=(2 ** self.num_state_qubits, 2 ** self.num_state_qubits), + ).toarray() return matrix def eigs_bounds(self) -> Tuple[float, float]: @@ -208,7 +223,7 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.trotter_steps < 1: valid = False if raise_on_failure: - raise AttributeError('The number of trotter steps should be a positive integer.') + raise AttributeError("The number of trotter steps should be a positive integer.") return False return valid @@ -219,7 +234,7 @@ def _reset_registers(self, num_state_qubits: int) -> None: Args: num_state_qubits: The number of qubits to represent the matrix. """ - qr_state = QuantumRegister(num_state_qubits, 'state') + qr_state = QuantumRegister(num_state_qubits, "state") self.qregs = [qr_state] self._ancillas = [] self._qubits = qr_state[:] @@ -251,7 +266,7 @@ def _main_diag_circ(self, theta: float = 1) -> QuantumCircuit: The quantum circuit implementing the matrix consisting of entries in the main diagonal. """ theta *= self.main_diag - qc = QuantumCircuit(self.num_state_qubits, name='main_diag') + qc = QuantumCircuit(self.num_state_qubits, name="main_diag") qc.x(0) qc.p(theta, 0) qc.x(0) @@ -259,7 +274,7 @@ def _main_diag_circ(self, theta: float = 1) -> QuantumCircuit: # pylint: disable=unused-argument def control(num_ctrl_qubits=1, label=None, ctrl_state=None): - qc_control = QuantumCircuit(self.num_state_qubits + 1, name='main_diag') + qc_control = QuantumCircuit(self.num_state_qubits + 1, name="main_diag") qc_control.p(theta, 0) return qc_control @@ -280,9 +295,9 @@ def _off_diag_circ(self, theta: float = 1) -> QuantumCircuit: qr = QuantumRegister(self.num_state_qubits) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 2)) - qc = QuantumCircuit(qr, qr_ancilla, name='off_diags') + qc = QuantumCircuit(qr, qr_ancilla, name="off_diags") else: - qc = QuantumCircuit(qr, name='off_diags') + qc = QuantumCircuit(qr, name="off_diags") qr_ancilla = None qc.u(-2 * theta, 3 * np.pi / 2, np.pi / 2, qr[0]) @@ -302,8 +317,10 @@ def _off_diag_circ(self, theta: float = 1) -> QuantumCircuit: # Multicontrolled rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) - qc.append(MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + [qr[i]] + - qr_ancilla[:len(q_controls) - 1]) + qc.append( + MCMTVChain(ugate, len(q_controls), 1), + q_controls[:] + [qr[i]] + qr_ancilla[: len(q_controls) - 1], + ) else: qc.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) @@ -319,9 +336,9 @@ def control(num_ctrl_qubits=1, label=None, ctrl_state=None): qr_state = QuantumRegister(self.num_state_qubits + 1) if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 1)) - qc_control = QuantumCircuit(qr_state, qr_ancilla, name='off_diags') + qc_control = QuantumCircuit(qr_state, qr_ancilla, name="off_diags") else: - qc_control = QuantumCircuit(qr_state, name='off_diags') + qc_control = QuantumCircuit(qr_state, name="off_diags") qr_ancilla = None # Control will be qr[0] q_control = qr_state[0] @@ -344,8 +361,10 @@ def control(num_ctrl_qubits=1, label=None, ctrl_state=None): # Multicontrolled x rotation if len(q_controls) > 1: ugate = UGate(-2 * theta, 3 * np.pi / 2, np.pi / 2) - qc_control.append(MCMTVChain(ugate, len(q_controls), 1), q_controls[:] + - [qr[i]] + qr_ancilla[:len(q_controls) - 1]) + qc_control.append( + MCMTVChain(ugate, len(q_controls), 1), + q_controls[:] + [qr[i]] + qr_ancilla[: len(q_controls) - 1], + ) else: qc_control.cu(-2 * theta, 3 * np.pi / 2, np.pi / 2, 0, q_controls[0], qr[i]) @@ -361,8 +380,12 @@ def control(num_ctrl_qubits=1, label=None, ctrl_state=None): return qc def inverse(self): - return TridiagonalToeplitz(self.num_state_qubits, self.main_diag, self.off_diag, - evolution_time=-1 * self.evolution_time) + return TridiagonalToeplitz( + self.num_state_qubits, + self.main_diag, + self.off_diag, + evolution_time=-1 * self.evolution_time, + ) def power(self, power: int, matrix_power: bool = False) -> QuantumCircuit: """Build powers of the circuit. @@ -380,39 +403,54 @@ def power(self, power: int, matrix_power: bool = False) -> QuantumCircuit: # pylint: disable=unused-argument def control(num_ctrl_qubits=1, label=None, ctrl_state=None): - qr_state = QuantumRegister(self.num_state_qubits + 1, 'state') + qr_state = QuantumRegister(self.num_state_qubits + 1, "state") if self.num_state_qubits > 1: qr_ancilla = AncillaRegister(max(1, self.num_state_qubits - 1)) - qc = QuantumCircuit(qr_state, qr_ancilla, name='exp(iHk)') + qc = QuantumCircuit(qr_state, qr_ancilla, name="exp(iHk)") else: - qc = QuantumCircuit(qr_state, name='exp(iHk)') + qc = QuantumCircuit(qr_state, name="exp(iHk)") qr_ancilla = None # Control will be qr[0] q_control = qr_state[0] qr = qr_state[1:] # A1 commutes, so one application with evolution_time*2^{j} to the last qubit is enough - qc.append(self._main_diag_circ(self.evolution_time * power).control(), [q_control] + - qr[:]) + qc.append( + self._main_diag_circ(self.evolution_time * power).control(), [q_control] + qr[:] + ) # Update trotter steps to compensate the error trotter_steps_new = int(np.ceil(np.sqrt(power) * self.trotter_steps)) # exp(iA2t/2m) - qc.u(self.off_diag * self.evolution_time * power / trotter_steps_new, 3 * np.pi / 2, - np.pi / 2, qr[0]) + qc.u( + self.off_diag * self.evolution_time * power / trotter_steps_new, + 3 * np.pi / 2, + np.pi / 2, + qr[0], + ) # for _ in range(power): for _ in range(0, trotter_steps_new): if qr_ancilla: - qc.append(self._off_diag_circ(self.evolution_time * power / - trotter_steps_new).control(), - [q_control] + qr[:] + qr_ancilla[:]) + qc.append( + self._off_diag_circ( + self.evolution_time * power / trotter_steps_new + ).control(), + [q_control] + qr[:] + qr_ancilla[:], + ) else: - qc.append(self._off_diag_circ(self.evolution_time * power / - trotter_steps_new).control(), - [q_control] + qr[:]) + qc.append( + self._off_diag_circ( + self.evolution_time * power / trotter_steps_new + ).control(), + [q_control] + qr[:], + ) # exp(-iA2t/2m) - qc.u(-self.off_diag * self.evolution_time * power / trotter_steps_new, 3 * np.pi / 2, - np.pi / 2, qr[0]) + qc.u( + -self.off_diag * self.evolution_time * power / trotter_steps_new, + 3 * np.pi / 2, + np.pi / 2, + qr[0], + ) return qc qc_raw.control = control diff --git a/qiskit/algorithms/linear_solvers/numpy_linear_solver.py b/qiskit/algorithms/linear_solvers/numpy_linear_solver.py index c50376609302..bbbb3a35f50f 100644 --- a/qiskit/algorithms/linear_solvers/numpy_linear_solver.py +++ b/qiskit/algorithms/linear_solvers/numpy_linear_solver.py @@ -47,14 +47,18 @@ class NumPyLinearSolver(LinearSolver): result = solution.observable """ - def solve(self, matrix: Union[np.ndarray, QuantumCircuit], - vector: Union[np.ndarray, QuantumCircuit], - observable: Optional[Union[LinearSystemObservable, BaseOperator, - List[BaseOperator]]] = None, - observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, - post_processing: Optional[Callable[[Union[float, List[float]]], - Union[float, List[float]]]] = None) \ - -> LinearSolverResult: + def solve( + self, + matrix: Union[np.ndarray, QuantumCircuit], + vector: Union[np.ndarray, QuantumCircuit], + observable: Optional[ + Union[LinearSystemObservable, BaseOperator, List[BaseOperator]] + ] = None, + observable_circuit: Optional[Union[QuantumCircuit, List[QuantumCircuit]]] = None, + post_processing: Optional[ + Callable[[Union[float, List[float]]], Union[float, List[float]]] + ] = None, + ) -> LinearSolverResult: """Solve classically the linear system and compute the observable(s) Args: diff --git a/qiskit/algorithms/linear_solvers/observables/__init__.py b/qiskit/algorithms/linear_solvers/observables/__init__.py index 3631e04e70ed..f4ff2b7c9aea 100644 --- a/qiskit/algorithms/linear_solvers/observables/__init__.py +++ b/qiskit/algorithms/linear_solvers/observables/__init__.py @@ -16,8 +16,4 @@ from .absolute_average import AbsoluteAverage from .matrix_functional import MatrixFunctional -__all__ = [ - 'LinearSystemObservable', - 'AbsoluteAverage', - 'MatrixFunctional' -] +__all__ = ["LinearSystemObservable", "AbsoluteAverage", "MatrixFunctional"] diff --git a/qiskit/algorithms/linear_solvers/observables/absolute_average.py b/qiskit/algorithms/linear_solvers/observables/absolute_average.py index d9985c9f57aa..4eb9d5af2992 100644 --- a/qiskit/algorithms/linear_solvers/observables/absolute_average.py +++ b/qiskit/algorithms/linear_solvers/observables/absolute_average.py @@ -83,8 +83,9 @@ def observable_circuit(self, num_qubits: int) -> Union[QuantumCircuit, List[Quan qc.h(qc.qubits) return qc - def post_processing(self, solution: Union[float, List[float]], num_qubits: int, - scaling: float = 1) -> float: + def post_processing( + self, solution: Union[float, List[float]], num_qubits: int, scaling: float = 1 + ) -> float: """Evaluates the absolute average on the solution to the linear system. Args: diff --git a/qiskit/algorithms/linear_solvers/observables/linear_system_observable.py b/qiskit/algorithms/linear_solvers/observables/linear_system_observable.py index cbeccfa6ae4c..048291d3d96d 100644 --- a/qiskit/algorithms/linear_solvers/observables/linear_system_observable.py +++ b/qiskit/algorithms/linear_solvers/observables/linear_system_observable.py @@ -48,9 +48,9 @@ def observable_circuit(self, num_qubits: int) -> Union[QuantumCircuit, List[Quan raise NotImplementedError @abstractmethod - def post_processing(self, solution: Union[float, List[float]], - num_qubits: int, - scaling: float = 1) -> float: + def post_processing( + self, solution: Union[float, List[float]], num_qubits: int, scaling: float = 1 + ) -> float: """Evaluates the given observable on the solution to the linear system. Args: diff --git a/qiskit/algorithms/linear_solvers/observables/matrix_functional.py b/qiskit/algorithms/linear_solvers/observables/matrix_functional.py index 0aeb6d105e4a..d9df7dcfbd10 100644 --- a/qiskit/algorithms/linear_solvers/observables/matrix_functional.py +++ b/qiskit/algorithms/linear_solvers/observables/matrix_functional.py @@ -89,8 +89,8 @@ def observable(self, num_qubits: int) -> Union[TensoredOp, List[TensoredOp]]: Returns: The observable as a list of sums of Pauli strings. """ - zero_op = ((I + Z) / 2) - one_op = ((I - Z) / 2) + zero_op = (I + Z) / 2 + one_op = (I - Z) / 2 observables = [] # First we measure the norm of x observables.append(I ^ num_qubits) @@ -100,8 +100,10 @@ def observable(self, num_qubits: int) -> Union[TensoredOp, List[TensoredOp]]: # TODO this if can be removed once the bug in Opflow is fixed where # TensoredOp([X, TensoredOp([])]).eval() ends up in infinite recursion if i > 0: - observables += [(I ^ j) ^ zero_op ^ TensoredOp(i * [one_op]), - (I ^ j) ^ one_op ^ TensoredOp(i * [one_op])] + observables += [ + (I ^ j) ^ zero_op ^ TensoredOp(i * [one_op]), + (I ^ j) ^ one_op ^ TensoredOp(i * [one_op]), + ] else: observables += [(I ^ j) ^ zero_op, (I ^ j) ^ one_op] @@ -128,9 +130,9 @@ def observable_circuit(self, num_qubits: int) -> Union[QuantumCircuit, List[Quan return qcs - def post_processing(self, solution: Union[float, List[float]], - num_qubits: int, - scaling: float = 1) -> float: + def post_processing( + self, solution: Union[float, List[float]], num_qubits: int, scaling: float = 1 + ) -> float: """Evaluates the matrix functional on the solution to the linear system. Args: @@ -167,7 +169,10 @@ def evaluate_classically(self, solution: Union[np.array, QuantumCircuit]) -> flo if isinstance(solution, QuantumCircuit): solution = Statevector(solution).data - matrix = diags([self._off_diag, self._main_diag, self._off_diag], [-1, 0, 1], - shape=(len(solution), len(solution))).toarray() + matrix = diags( + [self._off_diag, self._main_diag, self._off_diag], + [-1, 0, 1], + shape=(len(solution), len(solution)), + ).toarray() return np.dot(solution.transpose(), np.dot(matrix, solution)) diff --git a/qiskit/algorithms/minimum_eigen_solvers/__init__.py b/qiskit/algorithms/minimum_eigen_solvers/__init__.py index e613f815592c..9a02b3fd556f 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/__init__.py +++ b/qiskit/algorithms/minimum_eigen_solvers/__init__.py @@ -18,10 +18,10 @@ from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult __all__ = [ - 'VQE', - 'VQEResult', - 'QAOA', - 'NumPyMinimumEigensolver', - 'MinimumEigensolver', - 'MinimumEigensolverResult' + "VQE", + "VQEResult", + "QAOA", + "NumPyMinimumEigensolver", + "MinimumEigensolver", + "MinimumEigensolverResult", ] diff --git a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py index b88519a90ac9..3c96dbae3ca8 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -30,10 +30,8 @@ class MinimumEigensolver(ABC): @abstractmethod def compute_minimum_eigenvalue( - self, - operator: OperatorBase, - aux_operators: Optional[List[Optional[OperatorBase]]] = None - ) -> 'MinimumEigensolverResult': + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None + ) -> "MinimumEigensolverResult": """ Computes minimum eigenvalue. Operator and aux_operators can be supplied here and if not None will override any already set into algorithm so it can be reused with @@ -67,7 +65,7 @@ def supports_aux_operators(cls) -> bool: class MinimumEigensolverResult(AlgorithmResult): - """ Minimum Eigensolver Result.""" + """Minimum Eigensolver Result.""" def __init__(self) -> None: super().__init__() @@ -77,30 +75,30 @@ def __init__(self) -> None: @property def eigenvalue(self) -> Optional[complex]: - """ returns eigen value """ + """returns eigen value""" return self._eigenvalue @eigenvalue.setter def eigenvalue(self, value: complex) -> None: - """ set eigen value """ + """set eigen value""" self._eigenvalue = value @property def eigenstate(self) -> Optional[np.ndarray]: - """ return eigen state """ + """return eigen state""" return self._eigenstate @eigenstate.setter def eigenstate(self, value: np.ndarray) -> None: - """ set eigen state """ + """set eigen state""" self._eigenstate = value @property def aux_operator_eigenvalues(self) -> Optional[np.ndarray]: - """ return aux operator eigen values """ + """return aux operator eigen values""" return self._aux_operator_eigenvalues @aux_operator_eigenvalues.setter def aux_operator_eigenvalues(self, value: np.ndarray) -> None: - """ set aux operator eigen values """ + """set aux operator eigen values""" self._aux_operator_eigenvalues = value diff --git a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py index d0a6d2408ded..1d2b29fd12cc 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py +++ b/qiskit/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py @@ -28,10 +28,12 @@ class NumPyMinimumEigensolver(MinimumEigensolver): The Numpy Minimum Eigensolver algorithm. """ - def __init__(self, - filter_criterion: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], - bool] = None - ) -> None: + def __init__( + self, + filter_criterion: Callable[ + [Union[List, np.ndarray], float, Optional[List[float]]], bool + ] = None, + ) -> None: """ Args: filter_criterion: callable that allows to filter eigenvalues/eigenstates. The minimum @@ -45,15 +47,20 @@ def __init__(self, self._ret = MinimumEigensolverResult() @property - def filter_criterion(self) -> Optional[ - Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]: - """ returns the filter criterion if set """ + def filter_criterion( + self, + ) -> Optional[Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]: + """returns the filter criterion if set""" return self._ces.filter_criterion @filter_criterion.setter - def filter_criterion(self, filter_criterion: Optional[ - Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool]]) -> None: - """ set the filter criterion """ + def filter_criterion( + self, + filter_criterion: Optional[ + Callable[[Union[List, np.ndarray], float, Optional[List[float]]], bool] + ], + ) -> None: + """set the filter criterion""" self._ces.filter_criterion = filter_criterion @classmethod @@ -61,9 +68,7 @@ def supports_aux_operators(cls) -> bool: return NumPyEigensolver.supports_aux_operators() def compute_minimum_eigenvalue( - self, - operator: OperatorBase, - aux_operators: Optional[List[Optional[OperatorBase]]] = None + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) result_ces = self._ces.compute_eigenvalues(operator, aux_operators) @@ -74,6 +79,6 @@ def compute_minimum_eigenvalue( if result_ces.aux_operator_eigenvalues: self._ret.aux_operator_eigenvalues = result_ces.aux_operator_eigenvalues[0] - logger.debug('MinimumEigensolver:\n%s', self._ret) + logger.debug("MinimumEigensolver:\n%s", self._ret) return self._ret diff --git a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py index 4c41dbf2fbdf..37085b3c4085 100644 --- a/qiskit/algorithms/minimum_eigen_solvers/qaoa.py +++ b/qiskit/algorithms/minimum_eigen_solvers/qaoa.py @@ -53,20 +53,20 @@ class QAOA(VQE): the evolution to a feasible subspace of the full Hilbert space. """ - def __init__(self, - optimizer: Optimizer = None, - reps: int = 1, - initial_state: Optional[QuantumCircuit] = None, - mixer: Union[QuantumCircuit, OperatorBase] = None, - initial_point: Optional[np.ndarray] = None, - gradient: Optional[Union[GradientBase, Callable[[Union[np.ndarray, List]], - List]]] = None, - expectation: Optional[ExpectationBase] = None, - include_custom: bool = False, - max_evals_grouped: int = 1, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: + def __init__( + self, + optimizer: Optimizer = None, + reps: int = 1, + initial_state: Optional[QuantumCircuit] = None, + mixer: Union[QuantumCircuit, OperatorBase] = None, + initial_point: Optional[np.ndarray] = None, + gradient: Optional[Union[GradientBase, Callable[[Union[np.ndarray, List]], List]]] = None, + expectation: Optional[ExpectationBase] = None, + include_custom: bool = False, + max_evals_grouped: int = 1, + callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: """ Args: optimizer: A classical optimizer. @@ -107,29 +107,30 @@ def __init__(self, ansatz, the evaluated mean and the evaluated standard deviation. quantum_instance: Quantum Instance or Backend """ - validate_min('reps', reps, 1) + validate_min("reps", reps, 1) self._reps = reps self._mixer = mixer self._initial_state = initial_state - super().__init__(ansatz=None, - optimizer=optimizer, - initial_point=initial_point, - gradient=gradient, - expectation=expectation, - include_custom=include_custom, - max_evals_grouped=max_evals_grouped, - callback=callback, - quantum_instance=quantum_instance) + super().__init__( + ansatz=None, + optimizer=optimizer, + initial_point=initial_point, + gradient=gradient, + expectation=expectation, + include_custom=include_custom, + max_evals_grouped=max_evals_grouped, + callback=callback, + quantum_instance=quantum_instance, + ) def _check_operator(self, operator: OperatorBase) -> OperatorBase: # Recreates a circuit based on operator parameter. if operator.num_qubits != self.ansatz.num_qubits: - self.ansatz = QAOAAnsatz(operator, - self._reps, - initial_state=self._initial_state, - mixer_operator=self._mixer) + self.ansatz = QAOAAnsatz( + operator, self._reps, initial_state=self._initial_state, mixer_operator=self._mixer + ) operator = super()._check_operator(operator) return operator diff --git a/qiskit/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/algorithms/minimum_eigen_solvers/vqe.py index f0b359bfe51f..1a6d08dc47ef 100755 --- a/qiskit/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/algorithms/minimum_eigen_solvers/vqe.py @@ -25,8 +25,16 @@ from qiskit.circuit.library import RealAmplitudes from qiskit.providers import BaseBackend from qiskit.providers import Backend -from qiskit.opflow import (OperatorBase, ExpectationBase, ExpectationFactory, StateFn, - CircuitStateFn, ListOp, I, CircuitSampler) +from qiskit.opflow import ( + OperatorBase, + ExpectationBase, + ExpectationFactory, + StateFn, + CircuitStateFn, + ListOp, + I, + CircuitSampler, +) from qiskit.opflow.gradients import GradientBase from qiskit.utils.validation import validate_min from qiskit.utils.backend_utils import is_aer_provider @@ -85,17 +93,18 @@ class VQE(VariationalAlgorithm, MinimumEigensolver): """ - def __init__(self, - ansatz: Optional[QuantumCircuit] = None, - optimizer: Optional[Optimizer] = None, - initial_point: Optional[np.ndarray] = None, - gradient: Optional[Union[GradientBase, Callable]] = None, - expectation: Optional[ExpectationBase] = None, - include_custom: bool = False, - max_evals_grouped: int = 1, - callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: + def __init__( + self, + ansatz: Optional[QuantumCircuit] = None, + optimizer: Optional[Optimizer] = None, + initial_point: Optional[np.ndarray] = None, + gradient: Optional[Union[GradientBase, Callable]] = None, + expectation: Optional[ExpectationBase] = None, + include_custom: bool = False, + max_evals_grouped: int = 1, + callback: Optional[Callable[[int, np.ndarray, float, float], None]] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: """ Args: @@ -131,7 +140,7 @@ def __init__(self, ansatz, the evaluated mean and the evaluated standard deviation.` quantum_instance: Quantum Instance or Backend """ - validate_min('max_evals_grouped', max_evals_grouped, 1) + validate_min("max_evals_grouped", max_evals_grouped, 1) if ansatz is None: ansatz = RealAmplitudes() @@ -139,7 +148,7 @@ def __init__(self, optimizer = SLSQP() # set the initial point to the preferred parameters of the ansatz - if initial_point is None and hasattr(ansatz, 'preferred_init_points'): + if initial_point is None and hasattr(ansatz, "preferred_init_points"): initial_point = ansatz.preferred_init_points self._max_evals_grouped = max_evals_grouped @@ -149,12 +158,14 @@ def __init__(self, self._include_custom = include_custom self._expect_op = None - super().__init__(ansatz=ansatz, - optimizer=optimizer, - cost_fn=self._energy_evaluation, - gradient=gradient, - initial_point=initial_point, - quantum_instance=quantum_instance) + super().__init__( + ansatz=ansatz, + optimizer=optimizer, + cost_fn=self._energy_evaluation, + gradient=gradient, + initial_point=initial_point, + quantum_instance=quantum_instance, + ) self._ret = VQEResult() self._eval_time = None self._optimizer.set_max_evals_grouped(max_evals_grouped) @@ -163,12 +174,15 @@ def __init__(self, self._eval_count = 0 logger.info(self.print_settings()) - def _try_set_expectation_value_from_factory(self, - operator: OperatorBase) -> None: + def _try_set_expectation_value_from_factory(self, operator: OperatorBase) -> None: if operator is not None and self.quantum_instance is not None: - self._set_expectation(ExpectationFactory.build(operator=operator, - backend=self.quantum_instance, - include_custom=self._include_custom)) + self._set_expectation( + ExpectationFactory.build( + operator=operator, + backend=self.quantum_instance, + include_custom=self._include_custom, + ) + ) def _set_expectation(self, exp: ExpectationBase) -> None: self._expectation = exp @@ -176,19 +190,20 @@ def _set_expectation(self, exp: ExpectationBase) -> None: self._expect_op = None @VariationalAlgorithm.quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: - """ set quantum_instance """ + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: + """set quantum_instance""" super(VQE, self.__class__).quantum_instance.__set__(self, quantum_instance) self._circuit_sampler = CircuitSampler( - self._quantum_instance, - param_qobj=is_aer_provider(self._quantum_instance.backend)) + self._quantum_instance, param_qobj=is_aer_provider(self._quantum_instance.backend) + ) @property def expectation(self) -> ExpectationBase: - """ The expectation value algorithm used to construct the expectation measurement from - the observable. """ + """The expectation value algorithm used to construct the expectation measurement from + the observable.""" return self._expectation @expectation.setter @@ -196,8 +211,7 @@ def expectation(self, exp: ExpectationBase) -> None: self._set_expectation(exp) self._user_valid_expectation = self._expectation is not None - def _check_operator_varform(self, - operator: OperatorBase): + def _check_operator_varform(self, operator: OperatorBase): """Check that the number of qubits of operator and ansatz match.""" if operator is not None and self.ansatz is not None: if operator.num_qubits != self.ansatz.num_qubits: @@ -206,13 +220,15 @@ def _check_operator_varform(self, self.ansatz.num_qubits = operator.num_qubits self._ansatz_params = sorted(self.ansatz.parameters, key=lambda p: p.name) except AttributeError as ex: - raise AlgorithmError("The number of qubits of the ansatz does not match the " - "operator, and the ansatz does not allow setting the " - "number of qubits using `num_qubits`.") from ex + raise AlgorithmError( + "The number of qubits of the ansatz does not match the " + "operator, and the ansatz does not allow setting the " + "number of qubits using `num_qubits`." + ) from ex @VariationalAlgorithm.optimizer.setter # type: ignore def optimizer(self, optimizer: Optimizer): - """ Sets optimizer """ + """Sets optimizer""" super(VQE, self.__class__).optimizer.__set__(self, optimizer) # type: ignore if optimizer is not None: optimizer.set_max_evals_grouped(self._max_evals_grouped) @@ -240,12 +256,13 @@ def print_settings(self): """ ret = "\n" ret += "==================== Setting of {} ============================\n".format( - self.__class__.__name__) + self.__class__.__name__ + ) ret += "{}".format(self.setting) ret += "===============================================================\n" - if hasattr(self._ansatz, 'setting'): + if hasattr(self._ansatz, "setting"): ret += "{}".format(self._ansatz.setting) - elif hasattr(self._ansatz, 'print_settings'): + elif hasattr(self._ansatz, "print_settings"): ret += "{}".format(self._ansatz.print_settings()) elif isinstance(self._ansatz, QuantumCircuit): ret += "ansatz is a custom circuit" @@ -256,10 +273,11 @@ def print_settings(self): ret += "===============================================================\n" return ret - def construct_expectation(self, - parameter: Union[List[float], List[Parameter], np.ndarray], - operator: OperatorBase, - ) -> OperatorBase: + def construct_expectation( + self, + parameter: Union[List[float], List[Parameter], np.ndarray], + operator: OperatorBase, + ) -> OperatorBase: r""" Generate the ansatz circuit and expectation value measurement, and return their runnable composition. @@ -292,18 +310,21 @@ def construct_expectation(self, # If setting the expectation failed, raise an Error: if self._expectation is None: - raise AlgorithmError('No expectation set and could not automatically set one, please ' - 'try explicitly setting an expectation or specify a backend so it ' - 'can be chosen automatically.') + raise AlgorithmError( + "No expectation set and could not automatically set one, please " + "try explicitly setting an expectation or specify a backend so it " + "can be chosen automatically." + ) observable_meas = self.expectation.convert(StateFn(operator, is_measurement=True)) ansatz_circuit_op = CircuitStateFn(wave_function) return observable_meas.compose(ansatz_circuit_op).reduce() - def construct_circuit(self, - parameter: Union[List[float], List[Parameter], np.ndarray], - operator: OperatorBase, - ) -> List[QuantumCircuit]: + def construct_circuit( + self, + parameter: Union[List[float], List[Parameter], np.ndarray], + operator: OperatorBase, + ) -> List[QuantumCircuit]: """Return the circuits used to compute the expectation value. Args: @@ -333,31 +354,30 @@ def extract_circuits(op): def supports_aux_operators(cls) -> bool: return True - def _eval_aux_ops(self, - aux_operators: List[OperatorBase], - threshold: float = 1e-12) -> None: + def _eval_aux_ops(self, aux_operators: List[OperatorBase], threshold: float = 1e-12) -> None: # Create new CircuitSampler to avoid breaking existing one's caches. sampler = CircuitSampler(self.quantum_instance) - aux_op_meas = self.expectation.convert(StateFn(ListOp(aux_operators), - is_measurement=True)) + aux_op_meas = self.expectation.convert(StateFn(ListOp(aux_operators), is_measurement=True)) aux_op_expect = aux_op_meas.compose(CircuitStateFn(self.get_optimal_circuit())) values = np.real(sampler.convert(aux_op_expect).eval()) # Discard values below threshold - aux_op_results = (values * (np.abs(values) > threshold)) + aux_op_results = values * (np.abs(values) > threshold) # Deal with the aux_op behavior where there can be Nones or Zero qubit Paulis in the list _aux_op_nones = [op is None for op in aux_operators] - self._ret.aux_operator_eigenvalues = \ - [None if is_none else [result] - for (is_none, result) in zip(_aux_op_nones, aux_op_results)] + self._ret.aux_operator_eigenvalues = [ + None if is_none else [result] + for (is_none, result) in zip(_aux_op_nones, aux_op_results) + ] # As this has mixed types, since it can included None, it needs to explicitly pass object # data type to avoid numpy 1.19 warning message about implicit conversion being deprecated - self._ret.aux_operator_eigenvalues = \ - np.array([self._ret.aux_operator_eigenvalues], dtype=object) + self._ret.aux_operator_eigenvalues = np.array( + [self._ret.aux_operator_eigenvalues], dtype=object + ) def _check_operator(self, operator: OperatorBase) -> OperatorBase: - """ set operator """ + """set operator""" self._expect_op = None self._check_operator_varform(operator) # Expectation was not passed by user, try to create one @@ -366,15 +386,14 @@ def _check_operator(self, operator: OperatorBase) -> OperatorBase: return operator def compute_minimum_eigenvalue( - self, - operator: OperatorBase, - aux_operators: Optional[List[Optional[OperatorBase]]] = None + self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None ) -> MinimumEigensolverResult: super().compute_minimum_eigenvalue(operator, aux_operators) if self.quantum_instance is None: - raise AlgorithmError("A QuantumInstance or Backend " - "must be supplied to run the quantum algorithm.") + raise AlgorithmError( + "A QuantumInstance or Backend " "must be supplied to run the quantum algorithm." + ) if operator is None: raise AlgorithmError("The operator was never provided.") @@ -406,24 +425,30 @@ def compute_minimum_eigenvalue( self._gradient = self._gradient.gradient_wrapper( ~StateFn(operator) @ StateFn(self._ansatz), bind_params=self._ansatz_params, - backend=self._quantum_instance) + backend=self._quantum_instance, + ) if not self._expect_op: self._expect_op = self.construct_expectation(self._ansatz_params, operator) - vqresult = self.find_minimum(initial_point=self.initial_point, - ansatz=self.ansatz, - cost_fn=self._energy_evaluation, - gradient_fn=self._gradient, - optimizer=self.optimizer) + vqresult = self.find_minimum( + initial_point=self.initial_point, + ansatz=self.ansatz, + cost_fn=self._energy_evaluation, + gradient_fn=self._gradient, + optimizer=self.optimizer, + ) self._ret = VQEResult() self._ret.combine(vqresult) - if vqresult.optimizer_evals is not None and \ - self._eval_count >= vqresult.optimizer_evals: + if vqresult.optimizer_evals is not None and self._eval_count >= vqresult.optimizer_evals: self._eval_count = vqresult.optimizer_evals self._eval_time = vqresult.optimizer_time - logger.info('Optimization complete in %s seconds.\nFound opt_params %s in %s evals', - self._eval_time, vqresult.optimal_point, self._eval_count) + logger.info( + "Optimization complete in %s seconds.\nFound opt_params %s in %s evals", + self._eval_time, + vqresult.optimal_point, + self._eval_count, + ) self._ret.eigenvalue = vqresult.optimal_value + 0j self._ret.eigenstate = self.get_optimal_vector() @@ -436,9 +461,9 @@ def compute_minimum_eigenvalue( return self._ret - def _energy_evaluation(self, - parameters: Union[List[float], np.ndarray] - ) -> Union[float, List[float]]: + def _energy_evaluation( + self, parameters: Union[List[float], np.ndarray] + ) -> Union[float, List[float]]: """Evaluate energy at given parameters for the ansatz. This is the objective function to be passed to the optimizer that is used for evaluation. @@ -455,12 +480,13 @@ def _energy_evaluation(self, """ num_parameters = self.ansatz.num_parameters if self._ansatz.num_parameters == 0: - raise RuntimeError('The ansatz cannot have 0 parameters.') + raise RuntimeError("The ansatz cannot have 0 parameters.") parameter_sets = np.reshape(parameters, (-1, num_parameters)) # Create dict associating each parameter with the lists of parameterization values for it - param_bindings = dict(zip(self._ansatz_params, - parameter_sets.transpose().tolist())) # type: Dict + param_bindings = dict( + zip(self._ansatz_params, parameter_sets.transpose().tolist()) + ) # type: Dict start_time = time() sampled_expect_op = self._circuit_sampler.convert(self._expect_op, params=param_bindings) @@ -476,40 +502,48 @@ def _energy_evaluation(self, self._eval_count += len(means) end_time = time() - logger.info('Energy evaluation returned %s - %.5f (ms), eval count: %s', - means, (end_time - start_time) * 1000, self._eval_count) + logger.info( + "Energy evaluation returned %s - %.5f (ms), eval count: %s", + means, + (end_time - start_time) * 1000, + self._eval_count, + ) return means if len(means) > 1 else means[0] def get_optimal_cost(self) -> float: """Get the minimal cost or energy found by the VQE.""" if self._ret.optimal_point is None: - raise AlgorithmError("Cannot return optimal cost before running the " - "algorithm to find optimal params.") + raise AlgorithmError( + "Cannot return optimal cost before running the " "algorithm to find optimal params." + ) return self._ret.optimal_value def get_optimal_circuit(self) -> QuantumCircuit: """Get the circuit with the optimal parameters.""" if self._ret.optimal_point is None: - raise AlgorithmError("Cannot find optimal circuit before running the " - "algorithm to find optimal params.") + raise AlgorithmError( + "Cannot find optimal circuit before running the " + "algorithm to find optimal params." + ) return self.ansatz.assign_parameters(self._ret.optimal_parameters) def get_optimal_vector(self) -> Union[List[float], Dict[str, int]]: - """Get the simulation outcome of the optimal circuit. """ + """Get the simulation outcome of the optimal circuit.""" from qiskit.utils.run_circuits import find_regs_by_name if self._ret.optimal_point is None: - raise AlgorithmError("Cannot find optimal vector before running the " - "algorithm to find optimal params.") + raise AlgorithmError( + "Cannot find optimal vector before running the " "algorithm to find optimal params." + ) qc = self.get_optimal_circuit() min_vector = {} if self._quantum_instance.is_statevector: ret = self._quantum_instance.execute(qc) min_vector = ret.get_statevector(qc) else: - c = ClassicalRegister(qc.width(), name='c') - q = find_regs_by_name(qc, 'q') + c = ClassicalRegister(qc.width(), name="c") + q = find_regs_by_name(qc, "q") qc.add_register(c) qc.barrier(q) qc.measure(q, c) @@ -529,7 +563,7 @@ def optimal_params(self) -> List[float]: class VQEResult(VariationalResult, MinimumEigensolverResult): - """ VQE Result.""" + """VQE Result.""" def __init__(self) -> None: super().__init__() @@ -537,10 +571,10 @@ def __init__(self) -> None: @property def cost_function_evals(self) -> Optional[int]: - """ Returns number of cost optimizer evaluations """ + """Returns number of cost optimizer evaluations""" return self._cost_function_evals @cost_function_evals.setter def cost_function_evals(self, value: int) -> None: - """ Sets number of cost function evaluations """ + """Sets number of cost function evaluations""" self._cost_function_evals = value diff --git a/qiskit/algorithms/optimizers/__init__.py b/qiskit/algorithms/optimizers/__init__.py index adb1db08f211..caa5809be06a 100644 --- a/qiskit/algorithms/optimizers/__init__.py +++ b/qiskit/algorithms/optimizers/__init__.py @@ -120,20 +120,28 @@ from .bobyqa import BOBYQA from .imfil import IMFIL -__all__ = ['Optimizer', - 'OptimizerSupportLevel', - 'ADAM', - 'AQGD', - 'CG', - 'COBYLA', - 'GSLS', - 'L_BFGS_B', - 'NELDER_MEAD', - 'NFT', - 'P_BFGS', - 'POWELL', - 'SLSQP', - 'SPSA', - 'TNC', - 'CRS', 'DIRECT_L', 'DIRECT_L_RAND', 'ESCH', 'ISRES', - 'SNOBFIT', 'BOBYQA', 'IMFIL'] +__all__ = [ + "Optimizer", + "OptimizerSupportLevel", + "ADAM", + "AQGD", + "CG", + "COBYLA", + "GSLS", + "L_BFGS_B", + "NELDER_MEAD", + "NFT", + "P_BFGS", + "POWELL", + "SLSQP", + "SPSA", + "TNC", + "CRS", + "DIRECT_L", + "DIRECT_L_RAND", + "ESCH", + "ISRES", + "SNOBFIT", + "BOBYQA", + "IMFIL", +] diff --git a/qiskit/algorithms/optimizers/adam_amsgrad.py b/qiskit/algorithms/optimizers/adam_amsgrad.py index 038f1597f65e..ff87749ee31a 100644 --- a/qiskit/algorithms/optimizers/adam_amsgrad.py +++ b/qiskit/algorithms/optimizers/adam_amsgrad.py @@ -1,4 +1,3 @@ - # This code is part of Qiskit. # # (C) Copyright IBM 2019, 2021. @@ -46,19 +45,30 @@ class ADAM(Optimizer): """ - _OPTIONS = ['maxiter', 'tol', 'lr', 'beta_1', 'beta_2', - 'noise_factor', 'eps', 'amsgrad', 'snapshot_dir'] - - def __init__(self, - maxiter: int = 10000, - tol: float = 1e-6, - lr: float = 1e-3, - beta_1: float = 0.9, - beta_2: float = 0.99, - noise_factor: float = 1e-8, - eps: float = 1e-10, - amsgrad: bool = False, - snapshot_dir: Optional[str] = None) -> None: + _OPTIONS = [ + "maxiter", + "tol", + "lr", + "beta_1", + "beta_2", + "noise_factor", + "eps", + "amsgrad", + "snapshot_dir", + ] + + def __init__( + self, + maxiter: int = 10000, + tol: float = 1e-6, + lr: float = 1e-3, + beta_1: float = 0.9, + beta_2: float = 0.99, + noise_factor: float = 1e-8, + eps: float = 1e-10, + amsgrad: bool = False, + snapshot_dir: Optional[str] = None, + ) -> None: """ Args: maxiter: Maximum number of iterations @@ -96,20 +106,20 @@ def __init__(self, if self._snapshot_dir: - with open(os.path.join(self._snapshot_dir, 'adam_params.csv'), mode='w') as csv_file: + with open(os.path.join(self._snapshot_dir, "adam_params.csv"), mode="w") as csv_file: if self._amsgrad: - fieldnames = ['v', 'v_eff', 'm', 't'] + fieldnames = ["v", "v_eff", "m", "t"] else: - fieldnames = ['v', 'm', 't'] + fieldnames = ["v", "m", "t"] writer = csv.DictWriter(csv_file, fieldnames=fieldnames) writer.writeheader() def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.supported + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.supported, } def save_params(self, snapshot_dir: str) -> None: @@ -124,16 +134,15 @@ def save_params(self, snapshot_dir: str) -> None: snapshot_dir: The directory to store the file in. """ if self._amsgrad: - with open(os.path.join(snapshot_dir, 'adam_params.csv'), mode='a') as csv_file: - fieldnames = ['v', 'v_eff', 'm', 't'] + with open(os.path.join(snapshot_dir, "adam_params.csv"), mode="a") as csv_file: + fieldnames = ["v", "v_eff", "m", "t"] writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writerow({'v': self._v, 'v_eff': self._v_eff, - 'm': self._m, 't': self._t}) + writer.writerow({"v": self._v, "v_eff": self._v_eff, "m": self._m, "t": self._t}) else: - with open(os.path.join(snapshot_dir, 'adam_params.csv'), mode='a') as csv_file: - fieldnames = ['v', 'm', 't'] + with open(os.path.join(snapshot_dir, "adam_params.csv"), mode="a") as csv_file: + fieldnames = ["v", "m", "t"] writer = csv.DictWriter(csv_file, fieldnames=fieldnames) - writer.writerow({'v': self._v, 'm': self._m, 't': self._t}) + writer.writerow({"v": self._v, "m": self._m, "t": self._t}) def load_params(self, load_dir: str) -> None: """Load iteration parameters for a file called ``adam_params.csv``. @@ -141,31 +150,35 @@ def load_params(self, load_dir: str) -> None: Args: load_dir: The directory containing ``adam_params.csv``. """ - with open(os.path.join(load_dir, 'adam_params.csv'), mode='r') as csv_file: + with open(os.path.join(load_dir, "adam_params.csv"), mode="r") as csv_file: if self._amsgrad: - fieldnames = ['v', 'v_eff', 'm', 't'] + fieldnames = ["v", "v_eff", "m", "t"] else: - fieldnames = ['v', 'm', 't'] + fieldnames = ["v", "m", "t"] reader = csv.DictReader(csv_file, fieldnames=fieldnames) for line in reader: - v = line['v'] + v = line["v"] if self._amsgrad: - v_eff = line['v_eff'] - m = line['m'] - t = line['t'] + v_eff = line["v_eff"] + m = line["m"] + t = line["t"] v = v[1:-1] - self._v = np.fromstring(v, dtype=float, sep=' ') + self._v = np.fromstring(v, dtype=float, sep=" ") if self._amsgrad: v_eff = v_eff[1:-1] - self._v_eff = np.fromstring(v_eff, dtype=float, sep=' ') + self._v_eff = np.fromstring(v_eff, dtype=float, sep=" ") m = m[1:-1] - self._m = np.fromstring(m, dtype=float, sep=' ') + self._m = np.fromstring(m, dtype=float, sep=" ") t = t[1:-1] - self._t = np.fromstring(t, dtype=int, sep=' ') - - def minimize(self, objective_function: Callable[[np.ndarray], float], initial_point: np.ndarray, - gradient_function: Callable[[np.ndarray], float]) -> Tuple[np.ndarray, float, int]: + self._t = np.fromstring(t, dtype=int, sep=" ") + + def minimize( + self, + objective_function: Callable[[np.ndarray], float], + initial_point: np.ndarray, + gradient_function: Callable[[np.ndarray], float], + ) -> Tuple[np.ndarray, float, int]: """Run the minimization. Args: @@ -192,12 +205,14 @@ def minimize(self, objective_function: Callable[[np.ndarray], float], initial_po self._v = self._beta_2 * self._v + (1 - self._beta_2) * derivative * derivative lr_eff = self._lr * np.sqrt(1 - self._beta_2 ** self._t) / (1 - self._beta_1 ** self._t) if not self._amsgrad: - params_new = (params - lr_eff * self._m.flatten() - / (np.sqrt(self._v.flatten()) + self._noise_factor)) + params_new = params - lr_eff * self._m.flatten() / ( + np.sqrt(self._v.flatten()) + self._noise_factor + ) else: self._v_eff = np.maximum(self._v_eff, self._v) - params_new = (params - lr_eff * self._m.flatten() - / (np.sqrt(self._v_eff.flatten()) + self._noise_factor)) + params_new = params - lr_eff * self._m.flatten() / ( + np.sqrt(self._v_eff.flatten()) + self._noise_factor + ) if self._snapshot_dir: self.save_params(self._snapshot_dir) @@ -208,11 +223,14 @@ def minimize(self, objective_function: Callable[[np.ndarray], float], initial_po return params_new, objective_function(params_new), self._t - def optimize(self, num_vars: int, objective_function: Callable[[np.ndarray], float], - gradient_function: Optional[Callable[[np.ndarray], float]] = None, - variable_bounds: Optional[List[Tuple[float, float]]] = None, - initial_point: Optional[np.ndarray] = None - ) -> Tuple[np.ndarray, float, int]: + def optimize( + self, + num_vars: int, + objective_function: Callable[[np.ndarray], float], + gradient_function: Optional[Callable[[np.ndarray], float]] = None, + variable_bounds: Optional[List[Tuple[float, float]]] = None, + initial_point: Optional[np.ndarray] = None, + ) -> Tuple[np.ndarray, float, int]: """Perform optimization. Args: @@ -229,13 +247,15 @@ def optimize(self, num_vars: int, objective_function: Callable[[np.ndarray], flo value: is a float with the objective function value\n nfev: is the number of objective function calls """ - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if initial_point is None: initial_point = algorithm_globals.random.random(num_vars) if gradient_function is None: - gradient_function = Optimizer.wrap_function(Optimizer.gradient_num_diff, - (objective_function, self._eps)) + gradient_function = Optimizer.wrap_function( + Optimizer.gradient_num_diff, (objective_function, self._eps) + ) point, value, nfev = self.minimize(objective_function, initial_point, gradient_function) return point, value, nfev diff --git a/qiskit/algorithms/optimizers/aqgd.py b/qiskit/algorithms/optimizers/aqgd.py index fa870e5c9ad5..7ba2b635de8d 100644 --- a/qiskit/algorithms/optimizers/aqgd.py +++ b/qiskit/algorithms/optimizers/aqgd.py @@ -43,15 +43,18 @@ class AQGD(Optimizer): the objective function. """ - _OPTIONS = ['maxiter', 'eta', 'tol', 'disp', 'momentum', 'param_tol', 'averaging'] - - def __init__(self, - maxiter: Union[int, List[int]] = 1000, - eta: Union[float, List[float]] = 1.0, - tol: float = 1e-6, # this is tol - momentum: Union[float, List[float]] = 0.25, - param_tol: float = 1e-6, - averaging: int = 10) -> None: + + _OPTIONS = ["maxiter", "eta", "tol", "disp", "momentum", "param_tol", "averaging"] + + def __init__( + self, + maxiter: Union[int, List[int]] = 1000, + eta: Union[float, List[float]] = 1.0, + tol: float = 1e-6, # this is tol + momentum: Union[float, List[float]] = 0.25, + param_tol: float = 1e-6, + averaging: int = 10, + ) -> None: """ Performs Analytical Quantum Gradient Descent (AQGD) with Epochs. @@ -79,10 +82,12 @@ def __init__(self, if isinstance(momentum, (int, float)): momentum = [momentum] if len(maxiter) != len(eta) or len(maxiter) != len(momentum): - raise AlgorithmError("AQGD input parameter length mismatch. Parameters `maxiter`, " - "`eta`, and `momentum` must have the same length.") + raise AlgorithmError( + "AQGD input parameter length mismatch. Parameters `maxiter`, " + "`eta`, and `momentum` must have the same length." + ) for m in momentum: - validate_range_exclusive_max('momentum', m, 0, 1) + validate_range_exclusive_max("momentum", m, 0, 1) self._eta = eta self._maxiter = maxiter @@ -94,25 +99,26 @@ def __init__(self, # state self._avg_objval = None self._prev_param = None - self._eval_count = 0 # function evaluations - self._prev_loss = [] # type: List[float] - self._prev_grad = [] # type: List[List[float]] + self._eval_count = 0 # function evaluations + self._prev_loss = [] # type: List[float] + self._prev_grad = [] # type: List[List[float]] def get_support_level(self) -> Dict[str, OptimizerSupportLevel]: - """ Support level dictionary + """Support level dictionary Returns: Dict[str, int]: gradient, bounds and initial point support information that is ignored/required. """ return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def _compute_objective_fn_and_gradient(self, params: List[float], - obj: Callable) -> Tuple[float, np.array]: + def _compute_objective_fn_and_gradient( + self, params: List[float], obj: Callable + ) -> Tuple[float, np.array]: """ Obtains the objective function value for params and the analytical quantum derivatives of the objective function with respect to each parameter. Requires @@ -127,10 +133,13 @@ def _compute_objective_fn_and_gradient(self, params: List[float], """ num_params = len(params) param_sets_to_eval = params + np.concatenate( - (np.zeros((1, num_params)), # copy of the parameters as is - np.eye(num_params) * np.pi / 2, # copy of the parameters with the positive shift - -np.eye(num_params) * np.pi / 2), # copy of the parameters with the negative shift - axis=0) + ( + np.zeros((1, num_params)), # copy of the parameters as is + np.eye(num_params) * np.pi / 2, # copy of the parameters with the positive shift + -np.eye(num_params) * np.pi / 2, + ), # copy of the parameters with the negative shift + axis=0, + ) # Evaluate, # reshaping to flatten, as expected by objective function values = np.array(obj(param_sets_to_eval.reshape(-1))) @@ -142,11 +151,17 @@ def _compute_objective_fn_and_gradient(self, params: List[float], obj_value = values[0] # return the gradient values - gradient = 0.5 * (values[1:num_params + 1] - values[1 + num_params:]) + gradient = 0.5 * (values[1 : num_params + 1] - values[1 + num_params :]) return obj_value, gradient - def _update(self, params: np.array, gradient: np.array, mprev: np.array, - step_size: float, momentum_coeff: float) -> Tuple[List[float], List[float]]: + def _update( + self, + params: np.array, + gradient: np.array, + mprev: np.array, + step_size: float, + momentum_coeff: float, + ) -> Tuple[List[float], List[float]]: """ Updates full parameter array based on a step that is a convex combination of the gradient and previous momentum @@ -193,7 +208,7 @@ def _converged_objective(self, objval: float, tol: float, window_size: int) -> b # Calculate previous windowed average # and current windowed average of objective values prev_avg = np.mean(self._prev_loss[:window_size]) - curr_avg = np.mean(self._prev_loss[1:window_size + 1]) + curr_avg = np.mean(self._prev_loss[1 : window_size + 1]) self._avg_objval = curr_avg # Update window of objective values @@ -265,14 +280,17 @@ def _converged_alt(self, gradient: List[float], tol: float, window_size: int) -> return True return False - def optimize(self, - num_vars: int, - objective_function: Callable, - gradient_function: Callable = None, - variable_bounds: List[Tuple[float, float]] = None, - initial_point: np.ndarray = None) -> Tuple[np.ndarray, float, int]: - super().optimize(num_vars, objective_function, gradient_function, variable_bounds, - initial_point) + def optimize( + self, + num_vars: int, + objective_function: Callable, + gradient_function: Callable = None, + variable_bounds: List[Tuple[float, float]] = None, + initial_point: np.ndarray = None, + ) -> Tuple[np.ndarray, float, int]: + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) params = np.array(initial_point) momentum = np.zeros(shape=(num_vars,)) @@ -281,7 +299,7 @@ def optimize(self, self._prev_loss = [] self._prev_grad = [] self._prev_param = None - self._eval_count = 0 # function evaluations + self._eval_count = 0 # function evaluations iter_count = 0 logger.info("Initial Params: %s", params) @@ -291,7 +309,7 @@ def optimize(self, for (eta, mom_coeff) in zip(self._eta, self._momenta_coeff): logger.info("Epoch: %4d | Stepsize: %6.4f | Momentum: %6.4f", epoch, eta, mom_coeff) - sum_max_iters = sum(self._maxiter[0:epoch + 1]) + sum_max_iters = sum(self._maxiter[0 : epoch + 1]) while iter_count < sum_max_iters: # update the iteration count iter_count += 1 @@ -303,14 +321,19 @@ def optimize(self, # Calculate objective function and estimate of analytical gradient if gradient_function is None: - objval, gradient = \ - self._compute_objective_fn_and_gradient(params, objective_function) + objval, gradient = self._compute_objective_fn_and_gradient( + params, objective_function + ) else: objval = objective_function(params) gradient = gradient_function(params) - logger.info(" Iter: %4d | Obj: %11.6f | Grad Norm: %f", - iter_count, objval, np.linalg.norm(gradient, ord=np.inf)) + logger.info( + " Iter: %4d | Obj: %11.6f | Grad Norm: %f", + iter_count, + objval, + np.linalg.norm(gradient, ord=np.inf), + ) # Check for objective convergence converged = self._converged_objective(objval, self._tol, self._averaging) diff --git a/qiskit/algorithms/optimizers/bobyqa.py b/qiskit/algorithms/optimizers/bobyqa.py index a5c74cd30fb6..a7a9edf427be 100644 --- a/qiskit/algorithms/optimizers/bobyqa.py +++ b/qiskit/algorithms/optimizers/bobyqa.py @@ -19,13 +19,14 @@ try: import skquant.opt as skq + _HAS_SKQUANT = True except ImportError: _HAS_SKQUANT = False class BOBYQA(Optimizer): - """ Bound Optimization BY Quadratic Approximation algorithm. + """Bound Optimization BY Quadratic Approximation algorithm. BOBYQA finds local solutions to nonlinear, non-convex minimization problems with optional bound constraints, without requirement of derivatives of the objective function. @@ -35,9 +36,10 @@ class BOBYQA(Optimizer): https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. """ - def __init__(self, - maxiter: int = 1000, - ) -> None: + def __init__( + self, + maxiter: int = 1000, + ) -> None: """ Args: maxiter: Maximum number of function evaluations. @@ -47,26 +49,36 @@ def __init__(self, """ if not _HAS_SKQUANT: raise MissingOptionalLibraryError( - libname='scikit-quant', - name='BOBYQA', - pip_install='pip install scikit-quant') + libname="scikit-quant", name="BOBYQA", pip_install="pip install scikit-quant" + ) super().__init__() self._maxiter = maxiter def get_support_level(self): - """ Returns support level dictionary. """ + """Returns support level dictionary.""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.required, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.required, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - """ Runs the optimization. """ - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) - res, history = skq.minimize(objective_function, np.array(initial_point), - bounds=np.array(variable_bounds), budget=self._maxiter, - method="bobyqa") + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + """Runs the optimization.""" + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) + res, history = skq.minimize( + objective_function, + np.array(initial_point), + bounds=np.array(variable_bounds), + budget=self._maxiter, + method="bobyqa", + ) return res.optpar, res.optval, len(history) diff --git a/qiskit/algorithms/optimizers/cg.py b/qiskit/algorithms/optimizers/cg.py index 7b286ac5269a..906357450189 100644 --- a/qiskit/algorithms/optimizers/cg.py +++ b/qiskit/algorithms/optimizers/cg.py @@ -32,15 +32,17 @@ class CG(Optimizer): https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'disp', 'gtol', 'eps'] + _OPTIONS = ["maxiter", "disp", "gtol", "eps"] # pylint: disable=unused-argument - def __init__(self, - maxiter: int = 20, - disp: bool = False, - gtol: float = 1e-5, - tol: Optional[float] = None, - eps: float = 1.4901161193847656e-08) -> None: + def __init__( + self, + maxiter: int = 20, + disp: bool = False, + gtol: float = 1e-5, + tol: Optional[float] = None, + eps: float = 1.4901161193847656e-08, + ) -> None: """ Args: maxiter: Maximum number of iterations to perform. @@ -56,24 +58,37 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if gradient_function is None and self._max_evals_grouped > 1: - epsilon = self._options['eps'] - gradient_function = Optimizer.wrap_function(Optimizer.gradient_num_diff, - (objective_function, epsilon, - self._max_evals_grouped)) + epsilon = self._options["eps"] + gradient_function = Optimizer.wrap_function( + Optimizer.gradient_num_diff, (objective_function, epsilon, self._max_evals_grouped) + ) - res = minimize(objective_function, initial_point, jac=gradient_function, - tol=self._tol, method="CG", options=self._options) + res = minimize( + objective_function, + initial_point, + jac=gradient_function, + tol=self._tol, + method="CG", + options=self._options, + ) return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/optimizers/cobyla.py b/qiskit/algorithms/optimizers/cobyla.py index c1c4ec00259f..244edd5d8921 100644 --- a/qiskit/algorithms/optimizers/cobyla.py +++ b/qiskit/algorithms/optimizers/cobyla.py @@ -30,14 +30,16 @@ class COBYLA(Optimizer): https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'disp', 'rhobeg'] + _OPTIONS = ["maxiter", "disp", "rhobeg"] # pylint: disable=unused-argument - def __init__(self, - maxiter: int = 1000, - disp: bool = False, - rhobeg: float = 1.0, - tol: Optional[float] = None) -> None: + def __init__( + self, + maxiter: int = 1000, + disp: bool = False, + rhobeg: float = 1.0, + tol: Optional[float] = None, + ) -> None: """ Args: maxiter: Maximum number of function evaluations. @@ -53,18 +55,26 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) - res = minimize(objective_function, initial_point, tol=self._tol, - method="COBYLA", options=self._options) + res = minimize( + objective_function, initial_point, tol=self._tol, method="COBYLA", options=self._options + ) return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/optimizers/gsls.py b/qiskit/algorithms/optimizers/gsls.py index 9d01a5987fed..08609998803d 100644 --- a/qiskit/algorithms/optimizers/gsls.py +++ b/qiskit/algorithms/optimizers/gsls.py @@ -27,24 +27,35 @@ class GSLS(Optimizer): based on Gaussian-smoothed samples on a sphere. """ - _OPTIONS = ['maxiter', 'max_eval', 'disp', 'sampling_radius', - 'sample_size_factor', 'initial_step_size', 'min_step_size', - 'step_size_multiplier', 'armijo_parameter', - 'min_gradient_norm', 'max_failed_rejection_sampling'] + _OPTIONS = [ + "maxiter", + "max_eval", + "disp", + "sampling_radius", + "sample_size_factor", + "initial_step_size", + "min_step_size", + "step_size_multiplier", + "armijo_parameter", + "min_gradient_norm", + "max_failed_rejection_sampling", + ] # pylint: disable=unused-argument - def __init__(self, - maxiter: int = 10000, - max_eval: int = 10000, - disp: bool = False, - sampling_radius: float = 1.0e-6, - sample_size_factor: int = 1, - initial_step_size: float = 1.0e-2, - min_step_size: float = 1.0e-10, - step_size_multiplier: float = 0.4, - armijo_parameter: float = 1.0e-1, - min_gradient_norm: float = 1e-8, - max_failed_rejection_sampling: int = 50) -> None: + def __init__( + self, + maxiter: int = 10000, + max_eval: int = 10000, + disp: bool = False, + sampling_radius: float = 1.0e-6, + sample_size_factor: int = 1, + initial_step_size: float = 1.0e-2, + min_step_size: float = 1.0e-10, + step_size_multiplier: float = 0.4, + armijo_parameter: float = 1.0e-1, + min_gradient_norm: float = 1e-8, + max_failed_rejection_sampling: int = 50, + ) -> None: """ Args: maxiter: Maximum number of iterations. @@ -75,18 +86,22 @@ def get_support_level(self) -> Dict[str, int]: A dictionary containing the support levels for different options. """ return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars: int, - objective_function: Callable, - gradient_function: Optional[Callable] = None, - variable_bounds: Optional[List[Tuple[float, float]]] = None, - initial_point: Optional[np.ndarray] = None) -> Tuple[np.ndarray, float, int]: - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars: int, + objective_function: Callable, + gradient_function: Optional[Callable] = None, + variable_bounds: Optional[List[Tuple[float, float]]] = None, + initial_point: Optional[np.ndarray] = None, + ) -> Tuple[np.ndarray, float, int]: + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if initial_point is None: initial_point = algorithm_globals.random.normal(size=num_vars) @@ -106,8 +121,14 @@ def optimize(self, num_vars: int, return x, x_value, n_evals - def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_lb: np.ndarray, - var_ub: np.ndarray) -> Tuple[np.ndarray, float, int, float]: + def ls_optimize( + self, + n: int, + obj_fun: Callable, + initial_point: np.ndarray, + var_lb: np.ndarray, + var_ub: np.ndarray, + ) -> Tuple[np.ndarray, float, int, float]: """Run the line search optimization. Args: @@ -128,11 +149,11 @@ def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_ the length of the lower or upper bound. """ if len(initial_point) != n: - raise ValueError('Size of the initial point mismatches the number of dimensions.') + raise ValueError("Size of the initial point mismatches the number of dimensions.") if len(var_lb) != n: - raise ValueError('Length of the lower bound mismatches the number of dimensions.') + raise ValueError("Length of the lower bound mismatches the number of dimensions.") if len(var_ub) != n: - raise ValueError('Length of the upper bound mismatches the number of dimensions.') + raise ValueError("Length of the upper bound mismatches the number of dimensions.") # Initialize counters and data iter_count = 0 @@ -140,21 +161,20 @@ def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_ prev_iter_successful = True prev_directions, prev_sample_set_x, prev_sample_set_y = None, None, None consecutive_fail_iter = 0 - alpha = self._options['initial_step_size'] + alpha = self._options["initial_step_size"] grad_norm = np.inf - sample_set_size = int(round(self._options['sample_size_factor'] * n)) + sample_set_size = int(round(self._options["sample_size_factor"] * n)) # Initial point x = initial_point x_value = obj_fun(x) n_evals += 1 - while iter_count < self._options['maxiter'] \ - and n_evals < self._options['max_eval']: + while iter_count < self._options["maxiter"] and n_evals < self._options["max_eval"]: # Determine set of sample points directions, sample_set_x = self.sample_set(n, x, var_lb, var_ub, sample_set_size) - if n_evals + len(sample_set_x) + 1 >= self._options['max_eval']: + if n_evals + len(sample_set_x) + 1 >= self._options["max_eval"]: # The evaluation budget is too small to allow for # another full iteration; we therefore exit now break @@ -169,27 +189,29 @@ def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_ sample_set_y = np.hstack((prev_sample_set_y, sample_set_y)) # Find gradient approximation and candidate point - grad = self.gradient_approximation(n, x, x_value, directions, sample_set_x, - sample_set_y) + grad = self.gradient_approximation( + n, x, x_value, directions, sample_set_x, sample_set_y + ) grad_norm = np.linalg.norm(grad) new_x = np.clip(x - alpha * grad, var_lb, var_ub) new_x_value = obj_fun(new_x) n_evals += 1 # Print information - if self._options['disp']: - print('Iter {:d}'.format(iter_count)) - print('Point {} obj {}'.format(x, x_value)) - print('Gradient {}'.format(grad)) - print('Grad norm {} new_x_value {} step_size {}'.format(grad_norm, new_x_value, - alpha)) - print('Direction {}'.format(directions)) + if self._options["disp"]: + print("Iter {:d}".format(iter_count)) + print("Point {} obj {}".format(x, x_value)) + print("Gradient {}".format(grad)) + print( + "Grad norm {} new_x_value {} step_size {}".format(grad_norm, new_x_value, alpha) + ) + print("Direction {}".format(directions)) # Test Armijo condition for sufficient decrease - if new_x_value <= x_value - self._options['armijo_parameter'] * alpha * grad_norm: + if new_x_value <= x_value - self._options["armijo_parameter"] * alpha * grad_norm: # Accept point x, x_value = new_x, new_x_value - alpha /= 2 * self._options['step_size_multiplier'] + alpha /= 2 * self._options["step_size_multiplier"] prev_iter_successful = True consecutive_fail_iter = 0 @@ -199,7 +221,7 @@ def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_ prev_sample_set_y = None else: # Do not accept point - alpha *= self._options['step_size_multiplier'] + alpha *= self._options["step_size_multiplier"] prev_iter_successful = False consecutive_fail_iter += 1 @@ -210,14 +232,17 @@ def ls_optimize(self, n: int, obj_fun: Callable, initial_point: np.ndarray, var_ iter_count += 1 # Check termination criterion - if grad_norm <= self._options['min_gradient_norm'] \ - or alpha <= self._options['min_step_size']: + if ( + grad_norm <= self._options["min_gradient_norm"] + or alpha <= self._options["min_step_size"] + ): break return x, x_value, n_evals, grad_norm - def sample_points(self, n: int, x: np.ndarray, num_points: int - ) -> Tuple[np.ndarray, np.ndarray]: + def sample_points( + self, n: int, x: np.ndarray, num_points: int + ) -> Tuple[np.ndarray, np.ndarray]: """Sample ``num_points`` points around ``x`` on the ``n``-sphere of specified radius. The radius of the sphere is ``self._options['sampling_radius']``. @@ -233,12 +258,13 @@ def sample_points(self, n: int, x: np.ndarray, num_points: int normal_samples = algorithm_globals.random.normal(size=(num_points, n)) row_norms = np.linalg.norm(normal_samples, axis=1, keepdims=True) directions = normal_samples / row_norms - points = x + self._options['sampling_radius'] * directions + points = x + self._options["sampling_radius"] * directions return points, directions - def sample_set(self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarray, - num_points: int) -> Tuple[np.ndarray, np.ndarray]: + def sample_set( + self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarray, num_points: int + ) -> Tuple[np.ndarray, np.ndarray]: """Construct sample set of given size. Args: @@ -263,7 +289,7 @@ def sample_set(self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarr # Check bounds if (points >= var_lb).all() and (points <= var_ub).all(): # If all points are within bounds, return them - return directions, (x + self._options['sampling_radius'] * directions) + return directions, (x + self._options["sampling_radius"] * directions) else: # Otherwise we perform rejection sampling until we have # enough points that satisfy the bounds @@ -271,12 +297,15 @@ def sample_set(self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarr accepted = directions[indices] num_trials = 0 - while len(accepted) < num_points \ - and num_trials < self._options['max_failed_rejection_sampling']: + while ( + len(accepted) < num_points + and num_trials < self._options["max_failed_rejection_sampling"] + ): # Generate points uniformly on the sphere points, directions = self.sample_points(n, x, num_points) - indices = np.where((points >= var_lb).all(axis=1) & - (points <= var_ub).all(axis=1))[0] + indices = np.where((points >= var_lb).all(axis=1) & (points <= var_ub).all(axis=1))[ + 0 + ] accepted = np.vstack((accepted, directions[indices])) num_trials += 1 @@ -288,21 +317,32 @@ def sample_set(self, n: int, x: np.ndarray, var_lb: np.ndarray, var_ub: np.ndarr points, directions = self.sample_points(n, x, num_points) to_be_flipped = (points < var_lb) | (points > var_ub) directions *= np.where(to_be_flipped, -1, 1) - points = x + self._options['sampling_radius'] * directions - indices = np.where((points >= var_lb).all(axis=1) & - (points <= var_ub).all(axis=1))[0] + points = x + self._options["sampling_radius"] * directions + indices = np.where((points >= var_lb).all(axis=1) & (points <= var_ub).all(axis=1))[ + 0 + ] accepted = np.vstack((accepted, directions[indices])) # If we still do not have enough sampling points, we have failed. if len(accepted) < num_points: - raise RuntimeError('Could not generate enough samples ' - 'within bounds; try smaller radius.') - - return (accepted[:num_points], - x + self._options['sampling_radius'] * accepted[:num_points]) - - def gradient_approximation(self, n: int, x: np.ndarray, x_value: float, directions: np.ndarray, - sample_set_x: np.ndarray, sample_set_y: np.ndarray) -> np.ndarray: + raise RuntimeError( + "Could not generate enough samples " "within bounds; try smaller radius." + ) + + return ( + accepted[:num_points], + x + self._options["sampling_radius"] * accepted[:num_points], + ) + + def gradient_approximation( + self, + n: int, + x: np.ndarray, + x_value: float, + directions: np.ndarray, + sample_set_x: np.ndarray, + sample_set_y: np.ndarray, + ) -> np.ndarray: """Construct gradient approximation from given sample. Args: @@ -317,7 +357,11 @@ def gradient_approximation(self, n: int, x: np.ndarray, x_value: float, directio Gradient approximation at x, as a 1D array. """ ffd = sample_set_y - x_value - gradient = float(n) / len(sample_set_y) * np.sum(ffd.reshape(len(sample_set_y), 1) / - self._options['sampling_radius'] - * directions, 0) + gradient = ( + float(n) + / len(sample_set_y) + * np.sum( + ffd.reshape(len(sample_set_y), 1) / self._options["sampling_radius"] * directions, 0 + ) + ) return gradient diff --git a/qiskit/algorithms/optimizers/imfil.py b/qiskit/algorithms/optimizers/imfil.py index 5bb9469572bd..ad7ea8872b52 100644 --- a/qiskit/algorithms/optimizers/imfil.py +++ b/qiskit/algorithms/optimizers/imfil.py @@ -17,6 +17,7 @@ try: import skquant.opt as skq + _HAS_SKQUANT = True except ImportError: _HAS_SKQUANT = False @@ -35,9 +36,10 @@ class IMFIL(Optimizer): https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. """ - def __init__(self, - maxiter: int = 1000, - ) -> None: + def __init__( + self, + maxiter: int = 1000, + ) -> None: """ Args: maxiter: Maximum number of function evaluations. @@ -47,26 +49,36 @@ def __init__(self, """ if not _HAS_SKQUANT: raise MissingOptionalLibraryError( - libname='scikit-quant', - name='IMFIL', - pip_install='pip install scikit-quant') + libname="scikit-quant", name="IMFIL", pip_install="pip install scikit-quant" + ) super().__init__() self._maxiter = maxiter def get_support_level(self): - """ Returns support level dictionary. """ + """Returns support level dictionary.""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.required, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.required, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, variable_bounds=None, - initial_point=None): - """ Runs the optimization. """ - super().optimize(num_vars, objective_function, gradient_function, variable_bounds, - initial_point) - res, history = skq.minimize(func=objective_function, x0=initial_point, - bounds=variable_bounds, budget=self._maxiter, - method="imfil") + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + """Runs the optimization.""" + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) + res, history = skq.minimize( + func=objective_function, + x0=initial_point, + bounds=variable_bounds, + budget=self._maxiter, + method="imfil", + ) return res.optpar, res.optval, len(history) diff --git a/qiskit/algorithms/optimizers/l_bfgs_b.py b/qiskit/algorithms/optimizers/l_bfgs_b.py index 80c81227e431..0a57c17063b0 100644 --- a/qiskit/algorithms/optimizers/l_bfgs_b.py +++ b/qiskit/algorithms/optimizers/l_bfgs_b.py @@ -40,15 +40,17 @@ class L_BFGS_B(Optimizer): # pylint: disable=invalid-name https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html """ - _OPTIONS = ['maxfun', 'maxiter', 'factr', 'iprint', 'epsilon'] + _OPTIONS = ["maxfun", "maxiter", "factr", "iprint", "epsilon"] # pylint: disable=unused-argument - def __init__(self, - maxfun: int = 1000, - maxiter: int = 15000, - factr: float = 10, - iprint: int = -1, - epsilon: float = 1e-08) -> None: + def __init__( + self, + maxfun: int = 1000, + maxiter: int = 15000, + factr: float = 10, + iprint: int = -1, + epsilon: float = 1e-08, + ) -> None: r""" Args: maxfun: Maximum number of function evaluations. @@ -75,28 +77,39 @@ def __init__(self, self._options[k] = v def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if gradient_function is None and self._max_evals_grouped > 1: - epsilon = self._options['epsilon'] - gradient_function = Optimizer.wrap_function(Optimizer.gradient_num_diff, - (objective_function, - epsilon, self._max_evals_grouped)) + epsilon = self._options["epsilon"] + gradient_function = Optimizer.wrap_function( + Optimizer.gradient_num_diff, (objective_function, epsilon, self._max_evals_grouped) + ) approx_grad = bool(gradient_function is None) - sol, opt, info = sciopt.fmin_l_bfgs_b(objective_function, - initial_point, bounds=variable_bounds, - fprime=gradient_function, - approx_grad=approx_grad, **self._options) + sol, opt, info = sciopt.fmin_l_bfgs_b( + objective_function, + initial_point, + bounds=variable_bounds, + fprime=gradient_function, + approx_grad=approx_grad, + **self._options, + ) - return sol, opt, info['funcalls'] + return sol, opt, info["funcalls"] diff --git a/qiskit/algorithms/optimizers/nelder_mead.py b/qiskit/algorithms/optimizers/nelder_mead.py index 60e7cf242212..eb2d5f37e7a3 100644 --- a/qiskit/algorithms/optimizers/nelder_mead.py +++ b/qiskit/algorithms/optimizers/nelder_mead.py @@ -39,16 +39,18 @@ class NELDER_MEAD(Optimizer): # pylint: disable=invalid-name See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'maxfev', 'disp', 'xatol', 'adaptive'] + _OPTIONS = ["maxiter", "maxfev", "disp", "xatol", "adaptive"] # pylint: disable=unused-argument - def __init__(self, - maxiter: Optional[int] = None, - maxfev: int = 1000, - disp: bool = False, - xatol: float = 0.0001, - tol: Optional[float] = None, - adaptive: bool = False) -> None: + def __init__( + self, + maxiter: Optional[int] = None, + maxfev: int = 1000, + disp: bool = False, + xatol: float = 0.0001, + tol: Optional[float] = None, + adaptive: bool = False, + ) -> None: """ Args: maxiter: Maximum allowed number of iterations. If both maxiter and maxfev are set, @@ -67,18 +69,30 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) - res = minimize(objective_function, initial_point, tol=self._tol, - method="Nelder-Mead", options=self._options) + res = minimize( + objective_function, + initial_point, + tol=self._tol, + method="Nelder-Mead", + options=self._options, + ) return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/optimizers/nft.py b/qiskit/algorithms/optimizers/nft.py index 6f40d73f6339..bf7583b45cd9 100644 --- a/qiskit/algorithms/optimizers/nft.py +++ b/qiskit/algorithms/optimizers/nft.py @@ -27,14 +27,16 @@ class NFT(Optimizer): See https://arxiv.org/abs/1903.12166 """ - _OPTIONS = ['maxiter', 'maxfev', 'disp', 'reset_interval'] + _OPTIONS = ["maxiter", "maxfev", "disp", "reset_interval"] # pylint: disable=unused-argument - def __init__(self, - maxiter: Optional[int] = None, - maxfev: int = 1024, - disp: bool = False, - reset_interval: int = 32) -> None: + def __init__( + self, + maxiter: Optional[int] = None, + maxfev: int = 1024, + disp: bool = False, + reset_interval: int = 32, + ) -> None: """ Built out using scipy framework, for details, please refer to https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html. @@ -61,26 +63,35 @@ def __init__(self, self._options[k] = v def get_support_level(self): - """ return support level dictionary """ + """return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) - - res = minimize(objective_function, initial_point, - method=nakanishi_fujii_todo, options=self._options) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) + + res = minimize( + objective_function, initial_point, method=nakanishi_fujii_todo, options=self._options + ) return res.x, res.fun, res.nfev # pylint: disable=invalid-name -def nakanishi_fujii_todo(fun, x0, args=(), maxiter=None, maxfev=1024, - reset_interval=32, eps=1e-32, callback=None, **_): +def nakanishi_fujii_todo( + fun, x0, args=(), maxiter=None, maxfev=1024, reset_interval=32, eps=1e-32, callback=None, **_ +): """ Find the global minimum of a function using the nakanishi_fujii_todo algorithm [1]. diff --git a/qiskit/algorithms/optimizers/nlopts/crs.py b/qiskit/algorithms/optimizers/nlopts/crs.py index 161f15ed52e2..77eb67b298b6 100644 --- a/qiskit/algorithms/optimizers/nlopts/crs.py +++ b/qiskit/algorithms/optimizers/nlopts/crs.py @@ -31,5 +31,5 @@ class CRS(NLoptOptimizer): """ def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ Return NLopt optimizer type """ + """Return NLopt optimizer type""" return NLoptOptimizerType.GN_CRS2_LM diff --git a/qiskit/algorithms/optimizers/nlopts/direct_l.py b/qiskit/algorithms/optimizers/nlopts/direct_l.py index f8f9617d375c..1226defc361f 100644 --- a/qiskit/algorithms/optimizers/nlopts/direct_l.py +++ b/qiskit/algorithms/optimizers/nlopts/direct_l.py @@ -30,5 +30,5 @@ class DIRECT_L(NLoptOptimizer): # pylint: disable=invalid-name """ def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ Return NLopt optimizer type """ + """Return NLopt optimizer type""" return NLoptOptimizerType.GN_DIRECT_L diff --git a/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py b/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py index 9df18046c0a2..352e31592f9d 100644 --- a/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py +++ b/qiskit/algorithms/optimizers/nlopts/direct_l_rand.py @@ -28,5 +28,5 @@ class DIRECT_L_RAND(NLoptOptimizer): # pylint: disable=invalid-name """ def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ Return NLopt optimizer type """ + """Return NLopt optimizer type""" return NLoptOptimizerType.GN_DIRECT_L_RAND diff --git a/qiskit/algorithms/optimizers/nlopts/esch.py b/qiskit/algorithms/optimizers/nlopts/esch.py index 473a18f8f71b..4700fc5339a6 100644 --- a/qiskit/algorithms/optimizers/nlopts/esch.py +++ b/qiskit/algorithms/optimizers/nlopts/esch.py @@ -29,5 +29,5 @@ class ESCH(NLoptOptimizer): """ def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ Return NLopt optimizer type """ + """Return NLopt optimizer type""" return NLoptOptimizerType.GN_ESCH diff --git a/qiskit/algorithms/optimizers/nlopts/isres.py b/qiskit/algorithms/optimizers/nlopts/isres.py index 057bc0fa1b1f..9cbcbaca7e92 100644 --- a/qiskit/algorithms/optimizers/nlopts/isres.py +++ b/qiskit/algorithms/optimizers/nlopts/isres.py @@ -35,5 +35,5 @@ class ISRES(NLoptOptimizer): """ def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ Return NLopt optimizer type """ + """Return NLopt optimizer type""" return NLoptOptimizerType.GN_ISRES diff --git a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py b/qiskit/algorithms/optimizers/nlopts/nloptimizer.py index 2f30f58ec6c4..c38ba9609bc5 100644 --- a/qiskit/algorithms/optimizers/nlopts/nloptimizer.py +++ b/qiskit/algorithms/optimizers/nlopts/nloptimizer.py @@ -24,15 +24,21 @@ try: import nlopt - logger.info('NLopt version: %s.%s.%s', nlopt.version_major(), - nlopt.version_minor(), nlopt.version_bugfix()) + + logger.info( + "NLopt version: %s.%s.%s", + nlopt.version_major(), + nlopt.version_minor(), + nlopt.version_bugfix(), + ) _HAS_NLOPT = True except ImportError: _HAS_NLOPT = False class NLoptOptimizerType(Enum): - """ NLopt Valid Optimizer """ + """NLopt Valid Optimizer""" + GN_CRS2_LM = 1 GN_DIRECT_L_RAND = 2 GN_DIRECT_L = 3 @@ -45,7 +51,7 @@ class NLoptOptimizer(Optimizer): NLopt global optimizer base class """ - _OPTIONS = ['max_evals'] + _OPTIONS = ["max_evals"] def __init__(self, max_evals: int = 1000) -> None: # pylint: disable=unused-argument """ @@ -57,11 +63,12 @@ def __init__(self, max_evals: int = 1000) -> None: # pylint: disable=unused-arg """ if not _HAS_NLOPT: raise MissingOptionalLibraryError( - libname='nlopt', - name='NLoptOptimizer', - msg='See https://qiskit.org/documentation/apidoc/' - 'qiskit.aqua.components.optimizers.nlopts.html' - ' for installation information') + libname="nlopt", + name="NLoptOptimizer", + msg="See https://qiskit.org/documentation/apidoc/" + "qiskit.aqua.components.optimizers.nlopts.html" + " for installation information", + ) super().__init__() for k, v in list(locals().items()): @@ -78,34 +85,46 @@ def __init__(self, max_evals: int = 1000) -> None: # pylint: disable=unused-arg @abstractmethod def get_nlopt_optimizer(self) -> NLoptOptimizerType: - """ return NLopt optimizer enum type """ + """return NLopt optimizer enum type""" raise NotImplementedError def get_support_level(self): - """ return support level dictionary """ + """return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, - gradient_function, variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if variable_bounds is None: variable_bounds = [(None, None)] * num_vars - return self._minimize(self._optimizer_names[self.get_nlopt_optimizer()], - objective_function, - variable_bounds, - initial_point, **self._options) - - def _minimize(self, - name: str, - objective_function: Callable, - variable_bounds: Optional[List[Tuple[float, float]]], - initial_point: Optional[np.ndarray] = None, - max_evals: int = 1000) -> Tuple[float, float, int]: + return self._minimize( + self._optimizer_names[self.get_nlopt_optimizer()], + objective_function, + variable_bounds, + initial_point, + **self._options, + ) + + def _minimize( + self, + name: str, + objective_function: Callable, + variable_bounds: Optional[List[Tuple[float, float]]], + initial_point: Optional[np.ndarray] = None, + max_evals: int = 1000, + ) -> Tuple[float, float, int]: """Minimize using objective function Args: @@ -145,5 +164,5 @@ def wrap_objfunc_global(x, _grad): xopt = opt.optimize(initial_point) minf = opt.last_optimum_value() - logger.debug('Global minimize found %s eval count %s', minf, eval_count) + logger.debug("Global minimize found %s eval count %s", minf, eval_count) return xopt, minf, eval_count diff --git a/qiskit/algorithms/optimizers/optimizer.py b/qiskit/algorithms/optimizers/optimizer.py index dea21d82bbe6..106913b0d894 100644 --- a/qiskit/algorithms/optimizers/optimizer.py +++ b/qiskit/algorithms/optimizers/optimizer.py @@ -21,7 +21,8 @@ class OptimizerSupportLevel(IntEnum): - """ Support Level enum for features such as bounds, gradient and initial point """ + """Support Level enum for features such as bounds, gradient and initial point""" + # pylint: disable=invalid-name not_supported = 0 # Does not support the corresponding parameter in optimize() ignored = 1 # Feature can be passed as non None but will be ignored @@ -39,15 +40,15 @@ def __init__(self): level for _gradient_support_level, _bound_support_level, _initial_point_support_level, and empty options. """ - self._gradient_support_level = self.get_support_level()['gradient'] - self._bounds_support_level = self.get_support_level()['bounds'] - self._initial_point_support_level = self.get_support_level()['initial_point'] + self._gradient_support_level = self.get_support_level()["gradient"] + self._bounds_support_level = self.get_support_level()["bounds"] + self._initial_point_support_level = self.get_support_level()["initial_point"] self._options = {} self._max_evals_grouped = 1 @abstractmethod def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" raise NotImplementedError def set_options(self, **kwargs): @@ -64,7 +65,7 @@ def set_options(self, **kwargs): """ for name, value in kwargs.items(): self._options[name] = value - logger.debug('options: %s', self._options) + logger.debug("options: %s", self._options) # pylint: disable=invalid-name @staticmethod @@ -129,13 +130,15 @@ def wrap_function(function, args): Returns: function_wrapper: wrapper """ + def function_wrapper(*wrapper_args): return function(*(wrapper_args + args)) + return function_wrapper @property def setting(self): - """ Return setting """ + """Return setting""" ret = "Optimizer: {}\n".format(self.__class__.__name__) params = "" for key, value in self.__dict__.items(): @@ -145,8 +148,14 @@ def setting(self): return ret @abstractmethod - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): """ Perform optimization. @@ -172,9 +181,9 @@ def optimize(self, num_vars, objective_function, gradient_function=None, """ if initial_point is not None and len(initial_point) != num_vars: - raise ValueError('Initial point does not match dimension') + raise ValueError("Initial point does not match dimension") if variable_bounds is not None and len(variable_bounds) != num_vars: - raise ValueError('Variable bounds not match dimension') + raise ValueError("Variable bounds not match dimension") has_bounds = False if variable_bounds is not None: @@ -182,91 +191,93 @@ def optimize(self, num_vars, objective_function, gradient_function=None, has_bounds = not np.any(np.equal(variable_bounds, None)) if gradient_function is None and self.is_gradient_required: - raise ValueError('Gradient is required but None given') + raise ValueError("Gradient is required but None given") if not has_bounds and self.is_bounds_required: - raise ValueError('Variable bounds is required but None given') + raise ValueError("Variable bounds is required but None given") if initial_point is None and self.is_initial_point_required: - raise ValueError('Initial point is required but None given') + raise ValueError("Initial point is required but None given") if gradient_function is not None and self.is_gradient_ignored: logger.debug( - 'WARNING: %s does not support gradient function. It will be ignored.', - self.__class__.__name__) + "WARNING: %s does not support gradient function. It will be ignored.", + self.__class__.__name__, + ) if has_bounds and self.is_bounds_ignored: logger.debug( - 'WARNING: %s does not support bounds. It will be ignored.', - self.__class__.__name__) + "WARNING: %s does not support bounds. It will be ignored.", self.__class__.__name__ + ) if initial_point is not None and self.is_initial_point_ignored: logger.debug( - 'WARNING: %s does not support initial point. It will be ignored.', - self.__class__.__name__) + "WARNING: %s does not support initial point. It will be ignored.", + self.__class__.__name__, + ) pass @property def gradient_support_level(self): - """ Returns gradient support level """ + """Returns gradient support level""" return self._gradient_support_level @property def is_gradient_ignored(self): - """ Returns is gradient ignored """ + """Returns is gradient ignored""" return self._gradient_support_level == OptimizerSupportLevel.ignored @property def is_gradient_supported(self): - """ Returns is gradient supported """ + """Returns is gradient supported""" return self._gradient_support_level != OptimizerSupportLevel.not_supported @property def is_gradient_required(self): - """ Returns is gradient required """ + """Returns is gradient required""" return self._gradient_support_level == OptimizerSupportLevel.required @property def bounds_support_level(self): - """ Returns bounds support level """ + """Returns bounds support level""" return self._bounds_support_level @property def is_bounds_ignored(self): - """ Returns is bounds ignored """ + """Returns is bounds ignored""" return self._bounds_support_level == OptimizerSupportLevel.ignored @property def is_bounds_supported(self): - """ Returns is bounds supported """ + """Returns is bounds supported""" return self._bounds_support_level != OptimizerSupportLevel.not_supported @property def is_bounds_required(self): - """ Returns is bounds required """ + """Returns is bounds required""" return self._bounds_support_level == OptimizerSupportLevel.required @property def initial_point_support_level(self): - """ Returns initial point support level """ + """Returns initial point support level""" return self._initial_point_support_level @property def is_initial_point_ignored(self): - """ Returns is initial point ignored """ + """Returns is initial point ignored""" return self._initial_point_support_level == OptimizerSupportLevel.ignored @property def is_initial_point_supported(self): - """ Returns is initial point supported """ + """Returns is initial point supported""" return self._initial_point_support_level != OptimizerSupportLevel.not_supported @property def is_initial_point_required(self): - """ Returns is initial point required """ + """Returns is initial point required""" return self._initial_point_support_level == OptimizerSupportLevel.required def print_options(self): """Print algorithm-specific options.""" for name in sorted(self._options): - logger.debug('{:s} = {:s}'.format(name, str(self._options[name]))) + logger.debug("{:s} = {:s}".format(name, str(self._options[name]))) def set_max_evals_grouped(self, limit): - """ Set max evals grouped """ + """Set max evals grouped""" self._max_evals_grouped = limit diff --git a/qiskit/algorithms/optimizers/p_bfgs.py b/qiskit/algorithms/optimizers/p_bfgs.py index b8c86a8d5a87..0de78bd09077 100644 --- a/qiskit/algorithms/optimizers/p_bfgs.py +++ b/qiskit/algorithms/optimizers/p_bfgs.py @@ -41,14 +41,16 @@ class P_BFGS(Optimizer): # pylint: disable=invalid-name https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html """ - _OPTIONS = ['maxfun', 'factr', 'iprint'] + _OPTIONS = ["maxfun", "factr", "iprint"] # pylint: disable=unused-argument - def __init__(self, - maxfun: int = 1000, - factr: float = 10, - iprint: int = -1, - max_processes: Optional[int] = None) -> None: + def __init__( + self, + maxfun: int = 1000, + factr: float = 10, + iprint: int = -1, + max_processes: Optional[int] = None, + ) -> None: r""" Args: maxfun: Maximum number of function evaluations. @@ -68,7 +70,7 @@ def __init__(self, max_processes: maximum number of processes allowed, has a min. value of 1 if not None. """ if max_processes: - validate_min('max_processes', max_processes, 1) + validate_min("max_processes", max_processes, 1) super().__init__() for k, v in list(locals().items()): if k in self._OPTIONS: @@ -76,34 +78,44 @@ def __init__(self, self._max_processes = max_processes def get_support_level(self): - """ return support level dictionary """ + """return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): num_procs = multiprocessing.cpu_count() - 1 - num_procs = \ + num_procs = ( num_procs if self._max_processes is None else min(num_procs, self._max_processes) + ) num_procs = num_procs if num_procs >= 0 else 0 - if platform.system() == 'Darwin': + if platform.system() == "Darwin": # Changed in version 3.8: On macOS, the spawn start method is now the # default. The fork start method should be considered unsafe as it can # lead to crashes. # However P_BFGS doesn't support spawn, so we revert to single process. major, minor, _ = platform.python_version_tuple() - if major > '3' or (major == '3' and minor >= '8'): + if major > "3" or (major == "3" and minor >= "8"): num_procs = 0 - logger.warning("For MacOS, python >= 3.8, using only current process. " - "Multiple core use not supported.") - elif platform.system() == 'Windows': + logger.warning( + "For MacOS, python >= 3.8, using only current process. " + "Multiple core use not supported." + ) + elif platform.system() == "Windows": num_procs = 0 - logger.warning("For Windows, using only current process. " - "Multiple core use not supported.") + logger.warning( + "For Windows, using only current process. " "Multiple core use not supported." + ) queue = multiprocessing.Queue() # bounds for additional initial points in case bounds has any None values @@ -114,8 +126,9 @@ def optimize(self, num_vars, objective_function, gradient_function=None, high = [(u if u is not None else threshold) for (l, u) in variable_bounds] def optimize_runner(_queue, _i_pt): # Multi-process sampling - _sol, _opt, _nfev = self._optimize(num_vars, objective_function, - gradient_function, variable_bounds, _i_pt) + _sol, _opt, _nfev = self._optimize( + num_vars, objective_function, gradient_function, variable_bounds, _i_pt + ) _queue.put((_sol, _opt, _nfev)) # Start off as many other processes running the optimize (can be 0) @@ -129,8 +142,9 @@ def optimize_runner(_queue, _i_pt): # Multi-process sampling # While the one _optimize in this process below runs the other processes will # be running to. This one runs # with the supplied initial point. The process ones have their own random one - sol, opt, nfev = self._optimize(num_vars, objective_function, - gradient_function, variable_bounds, initial_point) + sol, opt, nfev = self._optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) for proc in processes: # For each other process we wait now for it to finish and see if it has @@ -143,14 +157,25 @@ def optimize_runner(_queue, _i_pt): # Multi-process sampling return sol, opt, nfev - def _optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def _optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) approx_grad = bool(gradient_function is None) - sol, opt, info = sciopt.fmin_l_bfgs_b(objective_function, initial_point, - bounds=variable_bounds, - fprime=gradient_function, - approx_grad=approx_grad, **self._options) - return sol, opt, info['funcalls'] + sol, opt, info = sciopt.fmin_l_bfgs_b( + objective_function, + initial_point, + bounds=variable_bounds, + fprime=gradient_function, + approx_grad=approx_grad, + **self._options, + ) + return sol, opt, info["funcalls"] diff --git a/qiskit/algorithms/optimizers/powell.py b/qiskit/algorithms/optimizers/powell.py index daa3b39793ab..3b422bac8fff 100644 --- a/qiskit/algorithms/optimizers/powell.py +++ b/qiskit/algorithms/optimizers/powell.py @@ -33,15 +33,17 @@ class POWELL(Optimizer): See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'maxfev', 'disp', 'xtol'] + _OPTIONS = ["maxiter", "maxfev", "disp", "xtol"] # pylint: disable=unused-argument - def __init__(self, - maxiter: Optional[int] = None, - maxfev: int = 1000, - disp: bool = False, - xtol: float = 0.0001, - tol: Optional[float] = None) -> None: + def __init__( + self, + maxiter: Optional[int] = None, + maxfev: int = 1000, + disp: bool = False, + xtol: float = 0.0001, + tol: Optional[float] = None, + ) -> None: """ Args: maxiter: Maximum allowed number of iterations. If both maxiter and maxfev @@ -59,18 +61,26 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) - res = minimize(objective_function, initial_point, tol=self._tol, - method="Powell", options=self._options) + res = minimize( + objective_function, initial_point, tol=self._tol, method="Powell", options=self._options + ) return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/optimizers/slsqp.py b/qiskit/algorithms/optimizers/slsqp.py index 22fcaf0dd3ee..7eb28a0ffbb1 100644 --- a/qiskit/algorithms/optimizers/slsqp.py +++ b/qiskit/algorithms/optimizers/slsqp.py @@ -35,15 +35,17 @@ class SLSQP(Optimizer): See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'disp', 'ftol', 'eps'] + _OPTIONS = ["maxiter", "disp", "ftol", "eps"] # pylint: disable=unused-argument - def __init__(self, - maxiter: int = 100, - disp: bool = False, - ftol: float = 1e-06, - tol: Optional[float] = None, - eps: float = 1.4901161193847656e-08) -> None: + def __init__( + self, + maxiter: int = 100, + disp: bool = False, + ftol: float = 1e-06, + tol: Optional[float] = None, + eps: float = 1.4901161193847656e-08, + ) -> None: """ Args: maxiter: Maximum number of iterations. @@ -59,25 +61,38 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ Return support level dictionary """ + """Return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, - gradient_function, variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if gradient_function is None and self._max_evals_grouped > 1: - epsilon = self._options['eps'] - gradient_function = Optimizer.wrap_function(Optimizer.gradient_num_diff, - (objective_function, epsilon, - self._max_evals_grouped)) + epsilon = self._options["eps"] + gradient_function = Optimizer.wrap_function( + Optimizer.gradient_num_diff, (objective_function, epsilon, self._max_evals_grouped) + ) - res = minimize(objective_function, initial_point, jac=gradient_function, - tol=self._tol, bounds=variable_bounds, method="SLSQP", - options=self._options) + res = minimize( + objective_function, + initial_point, + jac=gradient_function, + tol=self._tol, + bounds=variable_bounds, + method="SLSQP", + options=self._options, + ) return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/optimizers/snobfit.py b/qiskit/algorithms/optimizers/snobfit.py index 51c5bdb8c32d..f9a81efbc774 100644 --- a/qiskit/algorithms/optimizers/snobfit.py +++ b/qiskit/algorithms/optimizers/snobfit.py @@ -19,12 +19,14 @@ try: import skquant.opt as skq + _HAS_SKQUANT = True except ImportError: _HAS_SKQUANT = False try: from SQSnobFit import optset + _HAS_SKSNOBFIT = True except ImportError: _HAS_SKSNOBFIT = False @@ -41,12 +43,13 @@ class SNOBFIT(Optimizer): https://github.com/scikit-quant/scikit-quant and https://qat4chem.lbl.gov/software. """ - def __init__(self, - maxiter: int = 1000, - maxfail: int = 10, - maxmp: int = None, - verbose: bool = False, - ) -> None: + def __init__( + self, + maxiter: int = 1000, + maxfail: int = 10, + maxmp: int = None, + verbose: bool = False, + ) -> None: """ Args: maxiter: Maximum number of function evaluations. @@ -61,14 +64,12 @@ def __init__(self, """ if not _HAS_SKQUANT: raise MissingOptionalLibraryError( - libname='scikit-quant', - name='SNOBFIT', - pip_install='pip install scikit-quant') + libname="scikit-quant", name="SNOBFIT", pip_install="pip install scikit-quant" + ) if not _HAS_SKSNOBFIT: raise MissingOptionalLibraryError( - libname='SQSnobFit', - name='SNOBFIT', - pip_install='pip install SQSnobFit') + libname="SQSnobFit", name="SNOBFIT", pip_install="pip install SQSnobFit" + ) super().__init__() self._maxiter = maxiter self._maxfail = maxfail @@ -76,22 +77,29 @@ def __init__(self, self._verbose = verbose def get_support_level(self): - """ Returns support level dictionary. """ + """Returns support level dictionary.""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.required, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.required, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - """ Runs the optimization. """ - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + """Runs the optimization.""" + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) snobfit_settings = { - 'maxmp': self._maxmp, - 'maxfail': self._maxfail, - 'verbose': self._verbose, + "maxmp": self._maxmp, + "maxfail": self._maxfail, + "verbose": self._verbose, } options = optset(optin=snobfit_settings) # counters the error when initial point is outside the acceptable bounds @@ -100,7 +108,12 @@ def optimize(self, num_vars, objective_function, gradient_function=None, initial_point[idx] = initial_point[idx] % variable_bounds[idx][0] elif abs(theta) > variable_bounds[idx][1]: initial_point[idx] = initial_point[idx] % variable_bounds[idx][1] - res, history = skq.minimize(objective_function, np.array(initial_point, dtype=float), - bounds=variable_bounds, budget=self._maxiter, - method="snobfit", options=options) + res, history = skq.minimize( + objective_function, + np.array(initial_point, dtype=float), + bounds=variable_bounds, + budget=self._maxiter, + method="snobfit", + options=options, + ) return res.optpar, res.optval, len(history) diff --git a/qiskit/algorithms/optimizers/spsa.py b/qiskit/algorithms/optimizers/spsa.py index 0d263bd19ef5..8e96fabef367 100644 --- a/qiskit/algorithms/optimizers/spsa.py +++ b/qiskit/algorithms/optimizers/spsa.py @@ -72,18 +72,19 @@ class SPSA(Optimizer): """ - def __init__(self, - maxiter: int = 100, - blocking: bool = False, - allowed_increase: Optional[float] = None, - trust_region: bool = False, - learning_rate: Optional[Union[float, Callable[[], Iterator]]] = None, - perturbation: Optional[Union[float, Callable[[], Iterator]]] = None, - last_avg: int = 1, - resamplings: Union[int, Dict[int, int]] = 1, - perturbation_dims: Optional[int] = None, - callback: Optional[CALLBACK] = None, - ) -> None: + def __init__( + self, + maxiter: int = 100, + blocking: bool = False, + allowed_increase: Optional[float] = None, + trust_region: bool = False, + learning_rate: Optional[Union[float, Callable[[], Iterator]]] = None, + perturbation: Optional[Union[float, Callable[[], Iterator]]] = None, + last_avg: int = 1, + resamplings: Union[int, Dict[int, int]] = 1, + perturbation_dims: Optional[int] = None, + callback: Optional[CALLBACK] = None, + ) -> None: r""" Args: maxiter: The maximum number of iterations. @@ -136,14 +137,16 @@ def __init__(self, self._nfev = None @staticmethod - def calibrate(loss: Callable[[np.ndarray], float], - initial_point: np.ndarray, - c: float = 0.2, - stability_constant: float = 0, - target_magnitude: Optional[float] = None, # 2 pi / 10 - alpha: float = 0.602, - gamma: float = 0.101, - modelspace: bool = False) -> Tuple[Iterator[float], Iterator[float]]: + def calibrate( + loss: Callable[[np.ndarray], float], + initial_point: np.ndarray, + c: float = 0.2, + stability_constant: float = 0, + target_magnitude: Optional[float] = None, # 2 pi / 10 + alpha: float = 0.602, + gamma: float = 0.101, + modelspace: bool = False, + ) -> Tuple[Iterator[float], Iterator[float]]: r"""Calibrate SPSA parameters with a powerseries as learning rate and perturbation coeffs. The powerseries are: @@ -192,7 +195,7 @@ def calibrate(loss: Callable[[np.ndarray], float], # compute the rescaling factor for correct first learning rate if a < 1e-10: - warnings.warn(f'Calibration failed, using {target_magnitude} for `a`') + warnings.warn(f"Calibration failed, using {target_magnitude} for `a`") a = target_magnitude # set up the powerseries @@ -205,9 +208,9 @@ def perturbation(): return learning_rate, perturbation @staticmethod - def estimate_stddev(loss: Callable[[np.ndarray], float], - initial_point: np.ndarray, - avg: int = 25) -> float: + def estimate_stddev( + loss: Callable[[np.ndarray], float], initial_point: np.ndarray, avg: int = 25 + ) -> float: """Estimate the standard deviation of the loss function.""" losses = [loss(initial_point) for _ in range(avg)] return np.std(losses) @@ -225,8 +228,7 @@ def _point_sample(self, loss, x, eps, delta): return gradient_sample def _point_estimate(self, loss, x, eps, deltas): - """The gradient estimate at point ``x`` consisting as average of all directions ``delta``. - """ + """The gradient estimate at point ``x`` consisting as average of all directions ``delta``.""" # number of samples resamplings = len(deltas) @@ -265,7 +267,7 @@ def _minimize(self, loss, initial_point): eta = get_learning_rate() eps = get_perturbation() elif self.learning_rate is None or self.perturbation is None: - raise ValueError('If one of learning rate or perturbation is set, both must be set.') + raise ValueError("If one of learning rate or perturbation is set, both must be set.") else: # get iterator eta = self.learning_rate() @@ -284,8 +286,8 @@ def _minimize(self, loss, initial_point): if self.allowed_increase is None: self.allowed_increase = 2 * self.estimate_stddev(loss, x) - logger.info('=' * 30) - logger.info('Starting SPSA optimization') + logger.info("=" * 30) + logger.info("Starting SPSA optimization") start = time() # keep track of the last few steps to return their average @@ -313,19 +315,26 @@ def _minimize(self, loss, initial_point): if fx + self.allowed_increase <= fx_next: # accept only if loss improved if self.callback is not None: - self.callback(self._nfev, # number of function evals - x_next, # next parameters - fx_next, # loss at next parameters - np.linalg.norm(update), # size of the update step - False) # not accepted - - logger.info('Iteration %s/%s rejected in %s.', - k, self.maxiter + 1, time() - iteration_start) + self.callback( + self._nfev, # number of function evals + x_next, # next parameters + fx_next, # loss at next parameters + np.linalg.norm(update), # size of the update step + False, + ) # not accepted + + logger.info( + "Iteration %s/%s rejected in %s.", + k, + self.maxiter + 1, + time() - iteration_start, + ) continue fx = fx_next - logger.info('Iteration %s/%s done in %s.', - k, self.maxiter + 1, time() - iteration_start) + logger.info( + "Iteration %s/%s done in %s.", k, self.maxiter + 1, time() - iteration_start + ) if self.callback is not None: # if we didn't evaluate the function yet, do it now @@ -333,11 +342,13 @@ def _minimize(self, loss, initial_point): self._nfev += 1 fx_next = loss(x_next) - self.callback(self._nfev, # number of function evals - x_next, # next parameters - fx_next, # loss at next parameters - np.linalg.norm(update), # size of the update step - True) # accepted + self.callback( + self._nfev, # number of function evals + x_next, # next parameters + fx_next, # loss at next parameters + np.linalg.norm(update), # size of the update step + True, + ) # accepted # update parameters x = x_next @@ -348,8 +359,8 @@ def _minimize(self, loss, initial_point): if len(last_steps) > self.last_avg: last_steps.popleft() - logger.info('SPSA finished in %s', time() - start) - logger.info('=' * 30) + logger.info("SPSA finished in %s", time() - start) + logger.info("=" * 30) if self.last_avg > 1: x = np.mean(last_steps, axis=0) @@ -359,13 +370,19 @@ def _minimize(self, loss, initial_point): def get_support_level(self): """Get the support level dictionary.""" return { - 'gradient': OptimizerSupportLevel.ignored, - 'bounds': OptimizerSupportLevel.ignored, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.ignored, + "bounds": OptimizerSupportLevel.ignored, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): return self._minimize(objective_function, initial_point) @@ -375,8 +392,9 @@ def bernoulli_perturbation(dim, perturbation_dims=None): return 1 - 2 * algorithm_globals.random.binomial(1, 0.5, size=dim) pert = 1 - 2 * algorithm_globals.random.binomial(1, 0.5, size=perturbation_dims) - indices = algorithm_globals.random.choice(list(range(dim)), size=perturbation_dims, - replace=False) + indices = algorithm_globals.random.choice( + list(range(dim)), size=perturbation_dims, replace=False + ) result = np.zeros(dim) result[indices] = pert diff --git a/qiskit/algorithms/optimizers/tnc.py b/qiskit/algorithms/optimizers/tnc.py index ebba9a995297..d74a686672b9 100644 --- a/qiskit/algorithms/optimizers/tnc.py +++ b/qiskit/algorithms/optimizers/tnc.py @@ -32,18 +32,20 @@ class TNC(Optimizer): See https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html """ - _OPTIONS = ['maxiter', 'disp', 'accuracy', 'ftol', 'xtol', 'gtol', 'eps'] + _OPTIONS = ["maxiter", "disp", "accuracy", "ftol", "xtol", "gtol", "eps"] # pylint: disable=unused-argument - def __init__(self, - maxiter: int = 100, - disp: bool = False, - accuracy: float = 0, - ftol: float = -1, - xtol: float = -1, - gtol: float = -1, - tol: Optional[float] = None, - eps: float = 1e-08) -> None: + def __init__( + self, + maxiter: int = 100, + disp: bool = False, + accuracy: float = 0, + ftol: float = -1, + xtol: float = -1, + gtol: float = -1, + tol: Optional[float] = None, + eps: float = 1e-08, + ) -> None: """ Args: maxiter: Maximum number of function evaluation. @@ -69,25 +71,39 @@ def __init__(self, self._tol = tol def get_support_level(self): - """ return support level dictionary """ + """return support level dictionary""" return { - 'gradient': OptimizerSupportLevel.supported, - 'bounds': OptimizerSupportLevel.supported, - 'initial_point': OptimizerSupportLevel.required + "gradient": OptimizerSupportLevel.supported, + "bounds": OptimizerSupportLevel.supported, + "initial_point": OptimizerSupportLevel.required, } - def optimize(self, num_vars, objective_function, gradient_function=None, - variable_bounds=None, initial_point=None): - super().optimize(num_vars, objective_function, gradient_function, - variable_bounds, initial_point) + def optimize( + self, + num_vars, + objective_function, + gradient_function=None, + variable_bounds=None, + initial_point=None, + ): + super().optimize( + num_vars, objective_function, gradient_function, variable_bounds, initial_point + ) if gradient_function is None and self._max_evals_grouped > 1: - epsilon = self._options['eps'] - gradient_function = Optimizer.wrap_function(Optimizer.gradient_num_diff, - (objective_function, - epsilon, self._max_evals_grouped)) + epsilon = self._options["eps"] + gradient_function = Optimizer.wrap_function( + Optimizer.gradient_num_diff, (objective_function, epsilon, self._max_evals_grouped) + ) - res = minimize(objective_function, initial_point, jac=gradient_function, tol=self._tol, - bounds=variable_bounds, method="TNC", options=self._options) + res = minimize( + objective_function, + initial_point, + jac=gradient_function, + tol=self._tol, + bounds=variable_bounds, + method="TNC", + options=self._options, + ) # Note: nfev here seems to be iterations not function evaluations return res.x, res.fun, res.nfev diff --git a/qiskit/algorithms/phase_estimators/__init__.py b/qiskit/algorithms/phase_estimators/__init__.py index 19567798d9af..6d3ef1541440 100644 --- a/qiskit/algorithms/phase_estimators/__init__.py +++ b/qiskit/algorithms/phase_estimators/__init__.py @@ -20,10 +20,10 @@ from .hamiltonian_phase_estimation_result import HamiltonianPhaseEstimationResult __all__ = [ - 'PhaseEstimator', - 'PhaseEstimation', - 'PhaseEstimationResult', - 'PhaseEstimationScale', - 'HamiltonianPhaseEstimation', - 'HamiltonianPhaseEstimationResult' + "PhaseEstimator", + "PhaseEstimation", + "PhaseEstimationResult", + "PhaseEstimationScale", + "HamiltonianPhaseEstimation", + "HamiltonianPhaseEstimationResult", ] diff --git a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py index 1a3e40273051..28934a7a2343 100644 --- a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py +++ b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation.py @@ -15,8 +15,16 @@ from typing import Optional, Union from qiskit import QuantumCircuit from qiskit.utils import QuantumInstance -from qiskit.opflow import (EvolutionBase, PauliTrotterEvolution, OperatorBase, - SummedOp, PauliOp, MatrixOp, PauliSumOp, StateFn) +from qiskit.opflow import ( + EvolutionBase, + PauliTrotterEvolution, + OperatorBase, + SummedOp, + PauliOp, + MatrixOp, + PauliSumOp, + StateFn, +) from qiskit.providers import BaseBackend from .phase_estimation import PhaseEstimation from .hamiltonian_phase_estimation_result import HamiltonianPhaseEstimationResult @@ -77,9 +85,11 @@ class HamiltonianPhaseEstimation: """ - def __init__(self, - num_evaluation_qubits: int, - quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None) -> None: + def __init__( + self, + num_evaluation_qubits: int, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None, + ) -> None: """ Args: num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will @@ -87,8 +97,8 @@ def __init__(self, quantum_instance: The quantum instance on which the circuit will be run. """ self._phase_estimation = PhaseEstimation( - num_evaluation_qubits=num_evaluation_qubits, - quantum_instance=quantum_instance) + num_evaluation_qubits=num_evaluation_qubits, quantum_instance=quantum_instance + ) def _get_scale(self, hamiltonian, bound=None) -> None: if bound is None: @@ -116,10 +126,13 @@ def _get_unitary(self, hamiltonian, pe_scale, evolution) -> QuantumCircuit: return unitary_circuit.decompose().decompose() # pylint: disable=arguments-differ - def estimate(self, hamiltonian: OperatorBase, - state_preparation: Optional[StateFn] = None, - evolution: Optional[EvolutionBase] = None, - bound: Optional[float] = None) -> HamiltonianPhaseEstimationResult: + def estimate( + self, + hamiltonian: OperatorBase, + state_preparation: Optional[StateFn] = None, + evolution: Optional[EvolutionBase] = None, + bound: Optional[float] = None, + ) -> HamiltonianPhaseEstimationResult: """Run the Hamiltonian phase estimation algorithm. Args: @@ -147,7 +160,7 @@ def estimate(self, hamiltonian: OperatorBase, if evolution is None: evolution = PauliTrotterEvolution() elif not isinstance(evolution, EvolutionBase): - raise TypeError(f'Expecting type EvolutionBase, got {type(evolution)}') + raise TypeError(f"Expecting type EvolutionBase, got {type(evolution)}") if isinstance(hamiltonian, PauliSumOp): hamiltonian = hamiltonian.to_pauli_op() @@ -174,25 +187,27 @@ def estimate(self, hamiltonian: OperatorBase, elif isinstance(hamiltonian, MatrixOp): if bound is None: - raise ValueError('bound must be specified if Hermitian operator is MatrixOp') + raise ValueError("bound must be specified if Hermitian operator is MatrixOp") # Do not subtract an identity term from the matrix, so do not compensate. id_coefficient = 0.0 pe_scale = self._get_scale(hamiltonian, bound) unitary = self._get_unitary(hamiltonian, pe_scale, evolution) else: - raise TypeError(f'Hermitian operator of type {type(hamiltonian)} not supported.') + raise TypeError(f"Hermitian operator of type {type(hamiltonian)} not supported.") if state_preparation is not None: state_preparation = state_preparation.to_circuit_op().to_circuit() # run phase estimation phase_estimation_result = self._phase_estimation.estimate( - unitary=unitary, state_preparation=state_preparation) + unitary=unitary, state_preparation=state_preparation + ) return HamiltonianPhaseEstimationResult( phase_estimation_result=phase_estimation_result, id_coefficient=id_coefficient, - phase_estimation_scale=pe_scale) + phase_estimation_scale=pe_scale, + ) def _remove_identity(pauli_sum): diff --git a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py index 303cb248075c..49b2efe22a85 100644 --- a/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py +++ b/qiskit/algorithms/phase_estimators/hamiltonian_phase_estimation_result.py @@ -32,11 +32,12 @@ class HamiltonianPhaseEstimationResult(AlgorithmResult): This class is meant to be instantiated via `HamiltonianPhaseEstimation.estimate`. """ - def __init__(self, - phase_estimation_result: PhaseEstimationResult, - phase_estimation_scale: PhaseEstimationScale, - id_coefficient: float, - ) -> None: + def __init__( + self, + phase_estimation_result: PhaseEstimationResult, + phase_estimation_scale: PhaseEstimationScale, + id_coefficient: float, + ) -> None: """ Args: phase_estimation_result: The result object returned by PhaseEstimation.estimate. @@ -52,8 +53,9 @@ def __init__(self, self._phase_estimation_result = phase_estimation_result # pylint: disable=arguments-differ - def filter_phases(self, cutoff: float = 0.0, scaled: bool = True, - as_float: bool = True) -> Dict[Union[str, float], float]: + def filter_phases( + self, cutoff: float = 0.0, scaled: bool = True, as_float: bool = True + ) -> Dict[Union[str, float], float]: """Filter phases as does `PhaseEstimatorResult.filter_phases`, with the addition that `phi` is shifted and translated to return eigenvalues of the Hamiltonian. @@ -73,19 +75,22 @@ def filter_phases(self, cutoff: float = 0.0, scaled: bool = True, A dict of filtered phases. """ if scaled and not as_float: - raise ValueError('`as_float` must be `True` if `scaled` is `True`.') + raise ValueError("`as_float` must be `True` if `scaled` is `True`.") phases = self._phase_estimation_result.filter_phases(cutoff, as_float=as_float) if scaled: - return cast(Dict, self._phase_estimation_scale.scale_phases(phases, - self._id_coefficient)) + return cast( + Dict, self._phase_estimation_scale.scale_phases(phases, self._id_coefficient) + ) else: return cast(Dict, phases) @property - @deprecate_function("""The 'HamiltonianPhaseEstimationResult.most_likely_phase' attribute + @deprecate_function( + """The 'HamiltonianPhaseEstimationResult.most_likely_phase' attribute is deprecated as of 0.18.0 and will be removed no earlier than 3 months - after the release date. It has been renamed as the 'phase' attribute.""") + after the release date. It has been renamed as the 'phase' attribute.""" + ) def most_likely_phase(self) -> float: """DEPRECATED - The most likely phase of the unitary corresponding to the Hamiltonian. diff --git a/qiskit/algorithms/phase_estimators/phase_estimation.py b/qiskit/algorithms/phase_estimators/phase_estimation.py index 1d4e542c6b10..54d6bcf0fcd4 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimation.py +++ b/qiskit/algorithms/phase_estimators/phase_estimation.py @@ -74,10 +74,11 @@ class PhaseEstimation(PhaseEstimator): """ - def __init__(self, - num_evaluation_qubits: int, - quantum_instance: Optional[Union[QuantumInstance, - BaseBackend, Backend]] = None) -> None: + def __init__( + self, + num_evaluation_qubits: int, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: """ Args: num_evaluation_qubits: The number of qubits used in estimating the phase. The phase will @@ -93,9 +94,9 @@ def __init__(self, quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance - def construct_circuit(self, - unitary: QuantumCircuit, - state_preparation: Optional[QuantumCircuit] = None) -> QuantumCircuit: + def construct_circuit( + self, unitary: QuantumCircuit, state_preparation: Optional[QuantumCircuit] = None + ) -> QuantumCircuit: """Return the circuit to be executed to estimate phases. This circuit includes as sub-circuits the core phase estimation circuit, @@ -109,10 +110,10 @@ def construct_circuit(self, if state_preparation is not None: pe_circuit.compose( state_preparation, - qubits=range(num_evaluation_qubits, - num_evaluation_qubits + num_unitary_qubits), + qubits=range(num_evaluation_qubits, num_evaluation_qubits + num_unitary_qubits), inplace=True, - front=True) + front=True, + ) self._add_measurement_if_required(pe_circuit) @@ -121,18 +122,19 @@ def construct_circuit(self, def _add_measurement_if_required(self, pe_circuit): if not self._quantum_instance.is_statevector: # Measure only the evaluation qubits. - regname = 'meas' + regname = "meas" creg = ClassicalRegister(self._num_evaluation_qubits, regname) pe_circuit.add_register(creg) pe_circuit.barrier() - pe_circuit.measure(range(self._num_evaluation_qubits), - range(self._num_evaluation_qubits)) + pe_circuit.measure( + range(self._num_evaluation_qubits), range(self._num_evaluation_qubits) + ) return circuit - def _compute_phases(self, - num_unitary_qubits: int, - circuit_result: Result) -> Union[numpy.ndarray, qiskit.result.Counts]: + def _compute_phases( + self, num_unitary_qubits: int, circuit_result: Result + ) -> Union[numpy.ndarray, qiskit.result.Counts]: """Compute frequencies/counts of phases from the result of running the QPE circuit. How the frequencies are computed depends on whether the backend computes amplitude or @@ -164,8 +166,9 @@ def _compute_phases(self, state_vec = circuit_result.get_statevector() evaluation_density_matrix = qiskit.quantum_info.partial_trace( state_vec, - range(self._num_evaluation_qubits, - self._num_evaluation_qubits + num_unitary_qubits) + range( + self._num_evaluation_qubits, self._num_evaluation_qubits + num_unitary_qubits + ), ) phases = evaluation_density_matrix.probabilities() else: @@ -174,16 +177,19 @@ def _compute_phases(self, counts = circuit_result.get_counts() phases = {k[::-1]: counts[k] / num_shots for k in counts.keys()} phases = _sort_phases(phases) - phases = qiskit.result.Counts(phases, memory_slots=counts.memory_slots, - creg_sizes=counts.creg_sizes) + phases = qiskit.result.Counts( + phases, memory_slots=counts.memory_slots, creg_sizes=counts.creg_sizes + ) return phases - def estimate(self, - unitary: Optional[QuantumCircuit] = None, - state_preparation: Optional[QuantumCircuit] = None, - pe_circuit: Optional[QuantumCircuit] = None, - num_unitary_qubits: Optional[int] = None) -> PhaseEstimationResult: + def estimate( + self, + unitary: Optional[QuantumCircuit] = None, + state_preparation: Optional[QuantumCircuit] = None, + pe_circuit: Optional[QuantumCircuit] = None, + num_unitary_qubits: Optional[int] = None, + ) -> PhaseEstimationResult: """Run the the phase estimation algorithm. Args: @@ -209,7 +215,7 @@ def estimate(self, if unitary is not None: if pe_circuit is not None: - raise ValueError('Only one of `pe_circuit` and `unitary` may be passed.') + raise ValueError("Only one of `pe_circuit` and `unitary` may be passed.") pe_circuit = self.construct_circuit(unitary, state_preparation) num_unitary_qubits = unitary.num_qubits @@ -217,9 +223,10 @@ def estimate(self, self._add_measurement_if_required(pe_circuit) else: - raise ValueError('One of `pe_circuit` and `unitary` must be passed.') + raise ValueError("One of `pe_circuit` and `unitary` must be passed.") circuit_result = self._quantum_instance.execute(pe_circuit) phases = self._compute_phases(num_unitary_qubits, circuit_result) - return PhaseEstimationResult(num_evaluation_qubits, circuit_result=circuit_result, - phases=phases) + return PhaseEstimationResult( + num_evaluation_qubits, circuit_result=circuit_result, phases=phases + ) diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_result.py b/qiskit/algorithms/phase_estimators/phase_estimation_result.py index ce2cef40efcd..fcadc6b0d3fe 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimation_result.py +++ b/qiskit/algorithms/phase_estimators/phase_estimation_result.py @@ -32,9 +32,12 @@ class PhaseEstimationResult(PhaseEstimatorResult): attribute `phase`, is the most likely phase. """ - def __init__(self, num_evaluation_qubits: int, - circuit_result: Result, - phases: Union[numpy.ndarray, Dict[str, float]]) -> None: + def __init__( + self, + num_evaluation_qubits: int, + circuit_result: Result, + phases: Union[numpy.ndarray, Dict[str, float]], + ) -> None: """ Args: num_evaluation_qubits: number of qubits in phase-readout register. @@ -65,9 +68,11 @@ def circuit_result(self) -> Result: return self._circuit_result @property - @deprecate_function("""The 'PhaseEstimationResult.most_likely_phase' attribute + @deprecate_function( + """The 'PhaseEstimationResult.most_likely_phase' attribute is deprecated as of 0.18.0 and will be removed no earlier than 3 months - after the release date. It has been renamed as the 'phase' attribute.""") + after the release date. It has been renamed as the 'phase' attribute.""" + ) def most_likely_phase(self) -> float: r"""DEPRECATED - Return the most likely phase as a number in :math:`[0.0, 1.0)`. @@ -92,8 +97,7 @@ def phase(self) -> float: phase = _bit_string_to_phase(binary_phase_string) return phase - def filter_phases(self, cutoff: float = 0.0, - as_float: bool = True) -> Dict: + def filter_phases(self, cutoff: float = 0.0, as_float: bool = True) -> Dict: """Return a filtered dict of phases (keys) and frequencies (values). Only phases with frequencies (counts) larger than `cutoff` are included. @@ -116,8 +120,9 @@ def filter_phases(self, cutoff: float = 0.0, if isinstance(self.phases, dict): counts = self.phases if as_float: - phases = {_bit_string_to_phase(k): counts[k] - for k in counts.keys() if counts[k] > cutoff} + phases = { + _bit_string_to_phase(k): counts[k] for k in counts.keys() if counts[k] > cutoff + } else: phases = {k: counts[k] for k in counts.keys() if counts[k] > cutoff} diff --git a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py b/qiskit/algorithms/phase_estimators/phase_estimation_scale.py index bf00ed57b3c0..0ae1d01af129 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimation_scale.py +++ b/qiskit/algorithms/phase_estimators/phase_estimation_scale.py @@ -17,7 +17,7 @@ from qiskit.opflow import SummedOp -class PhaseEstimationScale(): +class PhaseEstimationScale: """Set and use a bound on eigenvalues of a Hermitian operator in order to ensure phases are in the desired range and to convert measured phases into eigenvectors. @@ -92,8 +92,9 @@ def scale_phase(self, phi: float, id_coefficient: float = 0.0) -> float: return (phi - 1) * w + id_coefficient # pylint: disable=unsubscriptable-object - def scale_phases(self, phases: Union[List, Dict], id_coefficient: float = 0.0 - ) -> Union[Dict, List]: + def scale_phases( + self, phases: Union[List, Dict], id_coefficient: float = 0.0 + ) -> Union[Dict, List]: """Convert a list or dict of phases to eigenvalues. The values in the list, or keys in the dict, are values of ``phi` and @@ -115,7 +116,7 @@ def scale_phases(self, phases: Union[List, Dict], id_coefficient: float = 0.0 return phases @classmethod - def from_pauli_sum(cls, pauli_sum: SummedOp) -> 'PhaseEstimationScale': + def from_pauli_sum(cls, pauli_sum: SummedOp) -> "PhaseEstimationScale": """Create a PhaseEstimationScale from a `SummedOp` representing a sum of Pauli Operators. It is assumed that the ``pauli_sum`` is the sum of ``PauliOp`` objects. The bound on @@ -132,10 +133,12 @@ def from_pauli_sum(cls, pauli_sum: SummedOp) -> 'PhaseEstimationScale': Returns: A ``PhaseEstimationScale`` object """ - if pauli_sum.primitive_strings() != {'Pauli'}: + if pauli_sum.primitive_strings() != {"Pauli"}: raise ValueError( - '`pauli_sum` must be a sum of Pauli operators. Got primitives {}.'.format( - pauli_sum.primitive_strings())) + "`pauli_sum` must be a sum of Pauli operators. Got primitives {}.".format( + pauli_sum.primitive_strings() + ) + ) bound = abs(pauli_sum.coeff) * sum(abs(pauli.coeff) for pauli in pauli_sum) return PhaseEstimationScale(bound) diff --git a/qiskit/algorithms/phase_estimators/phase_estimator.py b/qiskit/algorithms/phase_estimators/phase_estimator.py index b975c0b6d09d..a1131e4e8f95 100644 --- a/qiskit/algorithms/phase_estimators/phase_estimator.py +++ b/qiskit/algorithms/phase_estimators/phase_estimator.py @@ -30,11 +30,13 @@ class PhaseEstimator(ABC): """ @abstractmethod - def estimate(self, - unitary: Optional[QuantumCircuit] = None, - state_preparation: Optional[QuantumCircuit] = None, - pe_circuit: Optional[QuantumCircuit] = None, - num_unitary_qubits: Optional[int] = None) -> 'PhaseEstimatorResult': + def estimate( + self, + unitary: Optional[QuantumCircuit] = None, + state_preparation: Optional[QuantumCircuit] = None, + pe_circuit: Optional[QuantumCircuit] = None, + num_unitary_qubits: Optional[int] = None, + ) -> "PhaseEstimatorResult": """Estimate the phase.""" raise NotImplementedError diff --git a/qiskit/algorithms/variational_algorithm.py b/qiskit/algorithms/variational_algorithm.py index ff7cb02c14c2..20493b72e666 100644 --- a/qiskit/algorithms/variational_algorithm.py +++ b/qiskit/algorithms/variational_algorithm.py @@ -40,14 +40,15 @@ class VariationalAlgorithm: """The Variational Algorithm Base Class.""" - def __init__(self, - ansatz: QuantumCircuit, - optimizer: Optimizer, - cost_fn: Optional[Callable] = None, - gradient: Optional[Union[GradientBase, Callable]] = None, - initial_point: Optional[np.ndarray] = None, - quantum_instance: Optional[ - Union[QuantumInstance, BaseBackend, Backend]] = None) -> None: + def __init__( + self, + ansatz: QuantumCircuit, + optimizer: Optimizer, + cost_fn: Optional[Callable] = None, + gradient: Optional[Union[GradientBase, Callable]] = None, + initial_point: Optional[np.ndarray] = None, + quantum_instance: Optional[Union[QuantumInstance, BaseBackend, Backend]] = None, + ) -> None: """ Args: ansatz: An optional parameterized ansatz (a.k.a. variational form). @@ -67,7 +68,7 @@ def __init__(self, self.quantum_instance = quantum_instance if optimizer is None: - logger.info('No optimizer provided, setting it to SLSPQ.') + logger.info("No optimizer provided, setting it to SLSPQ.") optimizer = SLSQP() self._optimizer = optimizer @@ -83,25 +84,26 @@ def __init__(self, @property def quantum_instance(self) -> Optional[QuantumInstance]: - """ Returns quantum instance. """ + """Returns quantum instance.""" return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - BaseBackend, Backend]) -> None: - """ Sets quantum instance. """ + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, BaseBackend, Backend] + ) -> None: + """Sets quantum instance.""" if isinstance(quantum_instance, (BaseBackend, Backend)): quantum_instance = QuantumInstance(quantum_instance) self._quantum_instance = quantum_instance @property def ansatz(self) -> Optional[QuantumCircuit]: - """ Returns the ansatz """ + """Returns the ansatz""" return self._ansatz @ansatz.setter def ansatz(self, ansatz: Optional[QuantumCircuit]): - """ Sets the ansatz """ + """Sets the ansatz""" if isinstance(ansatz, QuantumCircuit): # store the parameters self._ansatz_params = sorted(ansatz.parameters, key=lambda p: p.name) @@ -114,30 +116,32 @@ def ansatz(self, ansatz: Optional[QuantumCircuit]): @property def optimizer(self) -> Optional[Optimizer]: - """ Returns optimizer """ + """Returns optimizer""" return self._optimizer @optimizer.setter def optimizer(self, optimizer: Optimizer): - """ Sets optimizer """ + """Sets optimizer""" self._optimizer = optimizer @property def initial_point(self) -> Optional[np.ndarray]: - """ Returns initial point """ + """Returns initial point""" return self._initial_point @initial_point.setter def initial_point(self, initial_point: np.ndarray): - """ Sets initial point """ + """Sets initial point""" self._initial_point = initial_point - def find_minimum(self, - initial_point: Optional[np.ndarray] = None, - ansatz: Optional[QuantumCircuit] = None, - cost_fn: Optional[Callable] = None, - optimizer: Optional[Optimizer] = None, - gradient_fn: Optional[Callable] = None) -> 'VariationalResult': + def find_minimum( + self, + initial_point: Optional[np.ndarray] = None, + ansatz: Optional[QuantumCircuit] = None, + cost_fn: Optional[Callable] = None, + optimizer: Optional[Optimizer] = None, + gradient_fn: Optional[Callable] = None, + ) -> "VariationalResult": """Optimize to find the minimum cost value. Args: @@ -163,40 +167,42 @@ def find_minimum(self, optimizer = optimizer if optimizer is not None else self.optimizer if ansatz is None: - raise ValueError('Ansatz neither supplied to constructor nor find minimum.') + raise ValueError("Ansatz neither supplied to constructor nor find minimum.") if cost_fn is None: - raise ValueError('Cost function neither supplied to constructor nor find minimum.') + raise ValueError("Cost function neither supplied to constructor nor find minimum.") if optimizer is None: - raise ValueError('Optimizer neither supplied to constructor nor find minimum.') + raise ValueError("Optimizer neither supplied to constructor nor find minimum.") nparms = ansatz.num_parameters - if hasattr(ansatz, 'parameter_bounds') and ansatz.parameter_bounds is not None: + if hasattr(ansatz, "parameter_bounds") and ansatz.parameter_bounds is not None: bounds = ansatz.parameter_bounds else: bounds = [(None, None)] * nparms if initial_point is not None and len(initial_point) != nparms: raise ValueError( - 'Initial point size {} and parameter size {} mismatch'.format( - len(initial_point), nparms)) + "Initial point size {} and parameter size {} mismatch".format( + len(initial_point), nparms + ) + ) if len(bounds) != nparms: - raise ValueError('Ansatz bounds size does not match parameter size') + raise ValueError("Ansatz bounds size does not match parameter size") # If *any* value is *equal* in bounds array to None then the problem does *not* have bounds problem_has_bounds = not np.any(np.equal(bounds, None)) # Check capabilities of the optimizer if problem_has_bounds: if not optimizer.is_bounds_supported: - raise ValueError('Problem has bounds but optimizer does not support bounds') + raise ValueError("Problem has bounds but optimizer does not support bounds") else: if optimizer.is_bounds_required: - raise ValueError('Problem does not have bounds but optimizer requires bounds') + raise ValueError("Problem does not have bounds but optimizer requires bounds") if initial_point is not None: if not optimizer.is_initial_point_supported: - raise ValueError('Optimizer does not support initial point') + raise ValueError("Optimizer does not support initial point") else: if optimizer.is_initial_point_required: - if hasattr(ansatz, 'preferred_init_points'): + if hasattr(ansatz, "preferred_init_points"): # Note: default implementation returns None, hence check again after below initial_point = ansatz.preferred_init_points @@ -212,12 +218,14 @@ def find_minimum(self, if not gradient_fn: gradient_fn = self._gradient - logger.info('Starting optimizer.\nbounds=%s\ninitial point=%s', bounds, initial_point) - opt_params, opt_val, num_optimizer_evals = optimizer.optimize(nparms, - cost_fn, - variable_bounds=bounds, - initial_point=initial_point, - gradient_function=gradient_fn) + logger.info("Starting optimizer.\nbounds=%s\ninitial point=%s", bounds, initial_point) + opt_params, opt_val, num_optimizer_evals = optimizer.optimize( + nparms, + cost_fn, + variable_bounds=bounds, + initial_point=initial_point, + gradient_function=gradient_fn, + ) eval_time = time.time() - start result = VariationalResult() @@ -229,9 +237,10 @@ def find_minimum(self, return result - def get_prob_vector_for_params(self, construct_circuit_fn, params_s, - quantum_instance, construct_circuit_args=None): - """ Helper function to get probability vectors for a set of params """ + def get_prob_vector_for_params( + self, construct_circuit_fn, params_s, quantum_instance, construct_circuit_args=None + ): + """Helper function to get probability vectors for a set of params""" circuits = [] for params in params_s: circuit = construct_circuit_fn(params, **construct_circuit_args) @@ -250,7 +259,7 @@ def get_prob_vector_for_params(self, construct_circuit_fn, params_s, return np.array(probs_s) def get_probabilities_for_counts(self, counts): - """ get probabilities for counts """ + """get probabilities for counts""" shots = sum(counts.values()) states = int(2 ** len(list(counts.keys())[0])) probs = np.zeros(states) @@ -260,32 +269,32 @@ def get_probabilities_for_counts(self, counts): @abstractmethod def get_optimal_cost(self): - """ get optimal cost """ + """get optimal cost""" raise NotImplementedError() @abstractmethod def get_optimal_circuit(self): - """ get optimal circuit """ + """get optimal circuit""" raise NotImplementedError() @abstractmethod def get_optimal_vector(self): - """ get optimal vector """ + """get optimal vector""" raise NotImplementedError() @property @abstractmethod def optimal_params(self): - """ returns optimal parameters """ + """returns optimal parameters""" raise NotImplementedError() def cleanup_parameterized_circuits(self): - """ set parameterized circuits to None """ + """set parameterized circuits to None""" self._parameterized_circuits = None class VariationalResult(AlgorithmResult): - """ Variation Algorithm Result.""" + """Variation Algorithm Result.""" def __init__(self) -> None: super().__init__() @@ -297,50 +306,50 @@ def __init__(self) -> None: @property def optimizer_evals(self) -> Optional[int]: - """ Returns number of optimizer evaluations """ + """Returns number of optimizer evaluations""" return self._optimizer_evals @optimizer_evals.setter def optimizer_evals(self, value: int) -> None: - """ Sets number of optimizer evaluations """ + """Sets number of optimizer evaluations""" self._optimizer_evals = value @property def optimizer_time(self) -> Optional[float]: - """ Returns time taken for optimization """ + """Returns time taken for optimization""" return self._optimizer_time @optimizer_time.setter def optimizer_time(self, value: float) -> None: - """ Sets time taken for optimization """ + """Sets time taken for optimization""" self._optimizer_time = value @property def optimal_value(self) -> Optional[float]: - """ Returns optimal value """ + """Returns optimal value""" return self._optimal_value @optimal_value.setter def optimal_value(self, value: int) -> None: - """ Sets optimal value """ + """Sets optimal value""" self._optimal_value = value @property def optimal_point(self) -> Optional[np.ndarray]: - """ Returns optimal point """ + """Returns optimal point""" return self._optimal_point @optimal_point.setter def optimal_point(self, value: np.ndarray) -> None: - """ Sets optimal point """ + """Sets optimal point""" self._optimal_point = value @property def optimal_parameters(self) -> Optional[Dict]: - """ Returns the optimal parameters in a dictionary """ + """Returns the optimal parameters in a dictionary""" return self._optimal_parameters @optimal_parameters.setter def optimal_parameters(self, value: Dict) -> None: - """ Sets optimal parameters """ + """Sets optimal parameters""" self._optimal_parameters = value diff --git a/qiskit/assembler/assemble_circuits.py b/qiskit/assembler/assemble_circuits.py index dff05d8797a5..2e125a1989b6 100644 --- a/qiskit/assembler/assemble_circuits.py +++ b/qiskit/assembler/assemble_circuits.py @@ -18,10 +18,20 @@ from qiskit.assembler.assemble_schedules import _assemble_instructions as _assemble_schedule from qiskit.circuit import QuantumCircuit from qiskit.exceptions import QiskitError -from qiskit.qobj import (QasmQobj, QobjExperimentHeader, - QasmQobjInstruction, QasmQobjExperimentConfig, QasmQobjExperiment, - QasmQobjConfig, QasmExperimentCalibrations, GateCalibration, - PulseQobjInstruction, PulseLibraryItem, converters, QobjHeader) +from qiskit.qobj import ( + QasmQobj, + QobjExperimentHeader, + QasmQobjInstruction, + QasmQobjExperimentConfig, + QasmQobjExperiment, + QasmQobjConfig, + QasmExperimentCalibrations, + GateCalibration, + PulseQobjInstruction, + PulseLibraryItem, + converters, + QobjHeader, +) from qiskit.tools.parallel import parallel_map @@ -29,8 +39,7 @@ def _assemble_circuit( - circuit: QuantumCircuit, - run_config: RunConfig + circuit: QuantumCircuit, run_config: RunConfig ) -> Tuple[QasmQobjExperiment, Optional[PulseLibrary]]: """Assemble one circuit. @@ -44,9 +53,10 @@ def _assemble_circuit( Raises: QiskitError: when the circuit has unit other than 'dt'. """ - if circuit.unit != 'dt': - raise QiskitError("Unable to assemble circuit with unit '{}', which must be 'dt'." - .format(circuit.unit)) + if circuit.unit != "dt": + raise QiskitError( + "Unable to assemble circuit with unit '{}', which must be 'dt'.".format(circuit.unit) + ) # header data num_qubits = 0 @@ -75,15 +85,17 @@ def _assemble_circuit( metadata = circuit.metadata if metadata is None: metadata = {} - header = QobjExperimentHeader(qubit_labels=qubit_labels, - n_qubits=num_qubits, - qreg_sizes=qreg_sizes, - clbit_labels=clbit_labels, - memory_slots=memory_slots, - creg_sizes=creg_sizes, - name=circuit.name, - global_phase=float(circuit.global_phase), - metadata=metadata) + header = QobjExperimentHeader( + qubit_labels=qubit_labels, + n_qubits=num_qubits, + qreg_sizes=qreg_sizes, + clbit_labels=clbit_labels, + memory_slots=memory_slots, + creg_sizes=creg_sizes, + name=circuit.name, + global_phase=float(circuit.global_phase), + metadata=metadata, + ) # TODO: why do we need n_qubits and memory_slots in both the header and the config config = QasmQobjExperimentConfig(n_qubits=num_qubits, memory_slots=memory_slots) @@ -120,21 +132,23 @@ def _assemble_circuit( # To convert to a qobj-style conditional, insert a bfunc prior # to the conditional instruction to map the creg ?= val condition # onto a gating register bit. - if hasattr(instruction, '_condition'): + if hasattr(instruction, "_condition"): ctrl_reg, ctrl_val = instruction._condition mask = 0 val = 0 for clbit in clbit_labels: if clbit[0] == ctrl_reg.name: - mask |= (1 << clbit_labels.index(clbit)) - val |= (((ctrl_val >> clbit[1]) & 1) << clbit_labels.index(clbit)) + mask |= 1 << clbit_labels.index(clbit) + val |= ((ctrl_val >> clbit[1]) & 1) << clbit_labels.index(clbit) conditional_reg_idx = memory_slots + max_conditional_idx - conversion_bfunc = QasmQobjInstruction(name='bfunc', - mask="0x%X" % mask, - relation='==', - val="0x%X" % val, - register=conditional_reg_idx) + conversion_bfunc = QasmQobjInstruction( + name="bfunc", + mask="0x%X" % mask, + relation="==", + val="0x%X" % val, + register=conditional_reg_idx, + ) instructions.append(conversion_bfunc) instruction.conditional = conditional_reg_idx max_conditional_idx += 1 @@ -143,13 +157,14 @@ def _assemble_circuit( del instruction._condition instructions.append(instruction) - return (QasmQobjExperiment(instructions=instructions, header=header, config=config), - pulse_library) + return ( + QasmQobjExperiment(instructions=instructions, header=header, config=config), + pulse_library, + ) def _assemble_pulse_gates( - circuit: QuantumCircuit, - run_config: RunConfig + circuit: QuantumCircuit, run_config: RunConfig ) -> Tuple[Optional[QasmExperimentCalibrations], Optional[PulseLibrary]]: """Assemble and return the circuit calibrations and associated pulse library, if there are any. The calibrations themselves may reference the pulse library which is returned as a dict. @@ -163,7 +178,7 @@ def _assemble_pulse_gates( """ if not circuit.calibrations: return None, None - if not hasattr(run_config, 'parametric_pulses'): + if not hasattr(run_config, "parametric_pulses"): run_config.parametric_pulses = [] calibrations = [] pulse_library = {} @@ -173,14 +188,16 @@ def _assemble_pulse_gates( schedule, converters.InstructionToQobjConverter(PulseQobjInstruction), run_config, - pulse_library) + pulse_library, + ) calibrations.append( - GateCalibration(str(gate), list(qubits), list(params), qobj_instructions)) + GateCalibration(str(gate), list(qubits), list(params), qobj_instructions) + ) return QasmExperimentCalibrations(gates=calibrations), pulse_library def _extract_common_calibrations( - experiments: List[QasmQobjExperiment] + experiments: List[QasmQobjExperiment], ) -> Tuple[List[QasmQobjExperiment], Optional[QasmExperimentCalibrations]]: """Given a list of ``QasmQobjExperiment``s, each of which may have calibrations in their ``config``, collect common calibrations into a global ``QasmExperimentCalibrations`` @@ -193,6 +210,7 @@ def _extract_common_calibrations( The input experiments with modified calibrations, and common calibrations, if there are any """ + def index_calibrations() -> Dict[int, List[Tuple[int, GateCalibration]]]: """Map each calibration to all experiments that contain it.""" exp_indices = defaultdict(list) @@ -219,7 +237,7 @@ def remove_common_gate_calibrations(exps: List[QasmQobjExperiment]) -> None: for exp_idx, gate_cal in exps_w_cal: exps[exp_idx].config.calibrations.gates.remove(gate_cal) - if not (experiments and all(hasattr(exp.config, 'calibrations') for exp in experiments)): + if not (experiments and all(hasattr(exp.config, "calibrations") for exp in experiments)): # No common calibrations return experiments, None @@ -236,10 +254,7 @@ def remove_common_gate_calibrations(exps: List[QasmQobjExperiment]) -> None: def assemble_circuits( - circuits: List[QuantumCircuit], - run_config: RunConfig, - qobj_id: int, - qobj_header: QobjHeader + circuits: List[QuantumCircuit], run_config: RunConfig, qobj_id: int, qobj_header: QobjHeader ) -> QasmQobj: """Assembles a list of circuits into a qobj that can be run on the backend. @@ -277,13 +292,13 @@ def assemble_circuits( if lib: pulse_library.update(lib) if pulse_library: - qobj_config.pulse_library = [PulseLibraryItem(name=name, samples=samples) - for name, samples in pulse_library.items()] + qobj_config.pulse_library = [ + PulseLibraryItem(name=name, samples=samples) for name, samples in pulse_library.items() + ] experiments, calibrations = _extract_common_calibrations(experiments) if calibrations and calibrations.gates: qobj_config.calibrations = calibrations - return QasmQobj(qobj_id=qobj_id, - config=qobj_config, - experiments=experiments, - header=qobj_header) + return QasmQobj( + qobj_id=qobj_id, config=qobj_config, experiments=experiments, header=qobj_header + ) diff --git a/qiskit/assembler/assemble_schedules.py b/qiskit/assembler/assemble_schedules.py index 5474b3223407..b81755a179bd 100644 --- a/qiskit/assembler/assemble_schedules.py +++ b/qiskit/assembler/assemble_schedules.py @@ -26,12 +26,17 @@ def assemble_schedules( - schedules: List[Union['schedule.ScheduleBlock', - 'schedule.ScheduleComponent', - Tuple[int, 'schedule.ScheduleComponent']]], - qobj_id: int, - qobj_header: qobj.QobjHeader, - run_config: RunConfig) -> qobj.PulseQobj: + schedules: List[ + Union[ + "schedule.ScheduleBlock", + "schedule.ScheduleComponent", + Tuple[int, "schedule.ScheduleComponent"], + ] + ], + qobj_id: int, + qobj_header: qobj.QobjHeader, + run_config: RunConfig, +) -> qobj.PulseQobj: """Assembles a list of schedules into a qobj that can be run on the backend. Args: @@ -46,29 +51,26 @@ def assemble_schedules( Raises: QiskitError: when frequency settings are not supplied. """ - if not hasattr(run_config, 'qubit_lo_freq'): - raise QiskitError('qubit_lo_freq must be supplied.') - if not hasattr(run_config, 'meas_lo_freq'): - raise QiskitError('meas_lo_freq must be supplied.') - - lo_converter = converters.LoConfigConverter(qobj.PulseQobjExperimentConfig, - **run_config.to_dict()) - experiments, experiment_config = _assemble_experiments(schedules, - lo_converter, - run_config) + if not hasattr(run_config, "qubit_lo_freq"): + raise QiskitError("qubit_lo_freq must be supplied.") + if not hasattr(run_config, "meas_lo_freq"): + raise QiskitError("meas_lo_freq must be supplied.") + + lo_converter = converters.LoConfigConverter( + qobj.PulseQobjExperimentConfig, **run_config.to_dict() + ) + experiments, experiment_config = _assemble_experiments(schedules, lo_converter, run_config) qobj_config = _assemble_config(lo_converter, experiment_config, run_config) - return qobj.PulseQobj(experiments=experiments, - qobj_id=qobj_id, - header=qobj_header, - config=qobj_config) + return qobj.PulseQobj( + experiments=experiments, qobj_id=qobj_id, header=qobj_header, config=qobj_config + ) def _assemble_experiments( - schedules: List[Union['schedule.ScheduleComponent', - Tuple[int, 'schedule.ScheduleComponent']]], - lo_converter: converters.LoConfigConverter, - run_config: RunConfig + schedules: List[Union["schedule.ScheduleComponent", Tuple[int, "schedule.ScheduleComponent"]]], + lo_converter: converters.LoConfigConverter, + run_config: RunConfig, ) -> Tuple[List[qobj.PulseQobjExperiment], Dict[str, Any]]: """Assembles a list of schedules into PulseQobjExperiments, and returns related metadata that will be assembled into the Qobj configuration. @@ -84,19 +86,20 @@ def _assemble_experiments( Raises: QiskitError: when frequency settings are not compatible with the experiments. """ - freq_configs = [lo_converter(lo_dict) for lo_dict in getattr(run_config, 'schedule_los', [])] + freq_configs = [lo_converter(lo_dict) for lo_dict in getattr(run_config, "schedule_los", [])] if len(schedules) > 1 and len(freq_configs) not in [0, 1, len(schedules)]: - raise QiskitError('Invalid frequency setting is specified. If the frequency is specified, ' - 'it should be configured the same for all schedules, configured for each ' - 'schedule, or a list of frequencies should be provided for a single ' - 'frequency sweep schedule.') - - instruction_converter = getattr(run_config, - 'instruction_converter', - converters.InstructionToQobjConverter) - instruction_converter = instruction_converter(qobj.PulseQobjInstruction, - **run_config.to_dict()) + raise QiskitError( + "Invalid frequency setting is specified. If the frequency is specified, " + "it should be configured the same for all schedules, configured for each " + "schedule, or a list of frequencies should be provided for a single " + "frequency sweep schedule." + ) + + instruction_converter = getattr( + run_config, "instruction_converter", converters.InstructionToQobjConverter + ) + instruction_converter = instruction_converter(qobj.PulseQobjInstruction, **run_config.to_dict()) formatted_schedules = [transforms.target_qobj_transform(sched) for sched in schedules] compressed_schedules = transforms.compress_pulses(formatted_schedules) @@ -105,10 +108,8 @@ def _assemble_experiments( experiments = [] for idx, sched in enumerate(compressed_schedules): qobj_instructions, max_memory_slot = _assemble_instructions( - sched, - instruction_converter, - run_config, - user_pulselib) + sched, instruction_converter, run_config, user_pulselib + ) metadata = sched.metadata if metadata is None: @@ -116,12 +117,13 @@ def _assemble_experiments( # TODO: add other experimental header items (see circuit assembler) qobj_experiment_header = qobj.QobjExperimentHeader( memory_slots=max_memory_slot + 1, # Memory slots are 0 indexed - name=sched.name or 'Experiment-%d' % idx, - metadata=metadata) + name=sched.name or "Experiment-%d" % idx, + metadata=metadata, + ) experiment = qobj.PulseQobjExperiment( - header=qobj_experiment_header, - instructions=qobj_instructions) + header=qobj_experiment_header, instructions=qobj_instructions + ) if freq_configs: # This handles the cases where one frequency setting applies to all experiments and # where each experiment has a different frequency @@ -135,26 +137,31 @@ def _assemble_experiments( experiment = experiments[0] experiments = [] for freq_config in freq_configs: - experiments.append(qobj.PulseQobjExperiment( - header=experiment.header, - instructions=experiment.instructions, - config=freq_config)) + experiments.append( + qobj.PulseQobjExperiment( + header=experiment.header, + instructions=experiment.instructions, + config=freq_config, + ) + ) # Top level Qobj configuration experiment_config = { - 'pulse_library': [qobj.PulseLibraryItem(name=name, samples=samples) - for name, samples in user_pulselib.items()], - 'memory_slots': max([exp.header.memory_slots for exp in experiments]) + "pulse_library": [ + qobj.PulseLibraryItem(name=name, samples=samples) + for name, samples in user_pulselib.items() + ], + "memory_slots": max([exp.header.memory_slots for exp in experiments]), } return experiments, experiment_config def _assemble_instructions( - sched: Union[pulse.Schedule, pulse.ScheduleBlock], - instruction_converter: converters.InstructionToQobjConverter, - run_config: RunConfig, - user_pulselib: Dict[str, List[complex]] + sched: Union[pulse.Schedule, pulse.ScheduleBlock], + instruction_converter: converters.InstructionToQobjConverter, + run_config: RunConfig, + user_pulselib: Dict[str, List[complex]], ) -> Tuple[List[qobj.PulseQobjInstruction], int]: """Assembles the instructions in a schedule into a list of PulseQobjInstructions and returns related metadata that will be assembled into the Qobj configuration. Lookup table for @@ -181,27 +188,31 @@ def _assemble_instructions( acquire_instruction_map = defaultdict(list) for time, instruction in sched.instructions: - if (isinstance(instruction, instructions.Play) and - isinstance(instruction.pulse, library.ParametricPulse)): + if isinstance(instruction, instructions.Play) and isinstance( + instruction.pulse, library.ParametricPulse + ): pulse_shape = ParametricPulseShapes(type(instruction.pulse)).name if pulse_shape not in run_config.parametric_pulses: - instruction = instructions.Play(instruction.pulse.get_waveform(), - instruction.channel, - name=instruction.name) + instruction = instructions.Play( + instruction.pulse.get_waveform(), instruction.channel, name=instruction.name + ) - if (isinstance(instruction, instructions.Play) and - isinstance(instruction.pulse, library.Waveform)): + if isinstance(instruction, instructions.Play) and isinstance( + instruction.pulse, library.Waveform + ): name = hashlib.sha256(instruction.pulse.samples).hexdigest() instruction = instructions.Play( library.Waveform(name=name, samples=instruction.pulse.samples), channel=instruction.channel, - name=name) + name=name, + ) user_pulselib[name] = instruction.pulse.samples # ignore explicit delay instrs on acq channels as they are invalid on IBMQ backends; # timing of other instrs will still be shifted appropriately - if (isinstance(instruction, instructions.Delay) and - isinstance(instruction.channel, channels.AcquireChannel)): + if isinstance(instruction, instructions.Delay) and isinstance( + instruction.channel, channels.AcquireChannel + ): continue if isinstance(instruction, instructions.Acquire): @@ -215,22 +226,20 @@ def _assemble_instructions( qobj_instructions.append(instruction_converter(time, instruction)) if acquire_instruction_map: - if hasattr(run_config, 'meas_map'): + if hasattr(run_config, "meas_map"): _validate_meas_map(acquire_instruction_map, run_config.meas_map) for (time, _), instrs in acquire_instruction_map.items(): qobj_instructions.append( - instruction_converter.convert_bundled_acquires( - time, - instrs - ), + instruction_converter.convert_bundled_acquires(time, instrs), ) return qobj_instructions, max_memory_slot -def _validate_meas_map(instruction_map: Dict[Tuple[int, instructions.Acquire], - List[instructions.Acquire]], - meas_map: List[List[int]]) -> None: +def _validate_meas_map( + instruction_map: Dict[Tuple[int, instructions.Acquire], List[instructions.Acquire]], + meas_map: List[List[int]], +) -> None: """Validate all qubits tied in ``meas_map`` are to be acquired. Args: @@ -247,7 +256,7 @@ def _validate_meas_map(instruction_map: Dict[Tuple[int, instructions.Acquire], # error if there is time overlap between qubits in the same meas_map for idx, inst in enumerate(sorted_inst_map[:-1]): inst_end_time = inst[0][0] + inst[0][1] - next_inst = sorted_inst_map[idx+1] + next_inst = sorted_inst_map[idx + 1] next_inst_time = next_inst[0][0] if next_inst_time < inst_end_time: inst_qubits = {inst.channel.index for inst in inst[1]} @@ -256,19 +265,27 @@ def _validate_meas_map(instruction_map: Dict[Tuple[int, instructions.Acquire], common_instr_qubits = inst_qubits.intersection(meas_set) common_next = next_inst_qubits.intersection(meas_set) if common_instr_qubits and common_next: - raise QiskitError('Qubits {} and {} are in the same measurement grouping: {}. ' - 'They must either be acquired at the same time, or disjointly' - '. Instead, they were acquired at times: {}-{} and ' - '{}-{}'.format(common_instr_qubits, - common_next, meas_map, - inst[0][0], inst_end_time, - next_inst_time, - next_inst_time + next_inst[0][1])) - - -def _assemble_config(lo_converter: converters.LoConfigConverter, - experiment_config: Dict[str, Any], - run_config: RunConfig) -> qobj.PulseQobjConfig: + raise QiskitError( + "Qubits {} and {} are in the same measurement grouping: {}. " + "They must either be acquired at the same time, or disjointly" + ". Instead, they were acquired at times: {}-{} and " + "{}-{}".format( + common_instr_qubits, + common_next, + meas_map, + inst[0][0], + inst_end_time, + next_inst_time, + next_inst_time + next_inst[0][1], + ) + ) + + +def _assemble_config( + lo_converter: converters.LoConfigConverter, + experiment_config: Dict[str, Any], + run_config: RunConfig, +) -> qobj.PulseQobjConfig: """Assembles the QobjConfiguration from experimental config and runtime config. Args: @@ -283,33 +300,33 @@ def _assemble_config(lo_converter: converters.LoConfigConverter, qobj_config.update(experiment_config) # Run config not needed in qobj config - qobj_config.pop('meas_map', None) - qobj_config.pop('qubit_lo_range', None) - qobj_config.pop('meas_lo_range', None) + qobj_config.pop("meas_map", None) + qobj_config.pop("qubit_lo_range", None) + qobj_config.pop("meas_lo_range", None) # convert enums to serialized values - meas_return = qobj_config.get('meas_return', 'avg') + meas_return = qobj_config.get("meas_return", "avg") if isinstance(meas_return, qobj_utils.MeasReturnType): - qobj_config['meas_return'] = meas_return.value + qobj_config["meas_return"] = meas_return.value - meas_level = qobj_config.get('meas_level', 2) + meas_level = qobj_config.get("meas_level", 2) if isinstance(meas_level, qobj_utils.MeasLevel): - qobj_config['meas_level'] = meas_level.value + qobj_config["meas_level"] = meas_level.value # convert lo frequencies to Hz - qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in qobj_config['qubit_lo_freq']] - qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in qobj_config['meas_lo_freq']] + qobj_config["qubit_lo_freq"] = [freq / 1e9 for freq in qobj_config["qubit_lo_freq"]] + qobj_config["meas_lo_freq"] = [freq / 1e9 for freq in qobj_config["meas_lo_freq"]] # frequency sweep config - schedule_los = qobj_config.pop('schedule_los', []) + schedule_los = qobj_config.pop("schedule_los", []) if len(schedule_los) == 1: lo_dict = schedule_los[0] q_los = lo_converter.get_qubit_los(lo_dict) # Hz -> GHz if q_los: - qobj_config['qubit_lo_freq'] = [freq / 1e9 for freq in q_los] + qobj_config["qubit_lo_freq"] = [freq / 1e9 for freq in q_los] m_los = lo_converter.get_meas_los(lo_dict) if m_los: - qobj_config['meas_lo_freq'] = [freq / 1e9 for freq in m_los] + qobj_config["meas_lo_freq"] = [freq / 1e9 for freq in m_los] return qobj.PulseQobjConfig(**qobj_config) diff --git a/qiskit/assembler/disassemble.py b/qiskit/assembler/disassemble.py index 3612f84dd93a..49b6f2ed2348 100644 --- a/qiskit/assembler/disassemble.py +++ b/qiskit/assembler/disassemble.py @@ -25,25 +25,13 @@ # It is currently a list of quantum circuits to execute, a run Qobj dictionary # and a header dictionary. CircuitModule = NewType( - 'CircuitModule', - Tuple[ - List[QuantumCircuit], - Dict[str, Any], - Dict[str, Any] - ] + "CircuitModule", Tuple[List[QuantumCircuit], Dict[str, Any], Dict[str, Any]] ) # A ``PulseModule`` is a representation of a pulse execution on the backend. # It is currently a list of pulse schedules to execute, a run Qobj dictionary # and a header dictionary. -PulseModule = NewType( - 'PulseModule', - Tuple[ - List[pulse.Schedule], - Dict[str, Any], - Dict[str, Any] - ] -) +PulseModule = NewType("PulseModule", Tuple[List[pulse.Schedule], Dict[str, Any], Dict[str, Any]]) def disassemble(qobj) -> Union[CircuitModule, PulseModule]: @@ -59,7 +47,7 @@ def disassemble(qobj) -> Union[CircuitModule, PulseModule]: * run_config: The dict of the run config * user_qobj_header: The dict of any user headers in the qobj """ - if qobj.type == 'PULSE': + if qobj.type == "PULSE": return _disassemble_pulse_schedule(qobj) else: return _disassemble_circuit(qobj) @@ -98,40 +86,36 @@ def _experiments_to_circuits(qobj): for i in exp.instructions: name = i.name qubits = [] - params = getattr(i, 'params', []) + params = getattr(i, "params", []) try: for qubit in i.qubits: qubit_label = exp.header.qubit_labels[qubit] - qubits.append( - qreg_dict[qubit_label[0]][qubit_label[1]]) + qubits.append(qreg_dict[qubit_label[0]][qubit_label[1]]) except Exception: # pylint: disable=broad-except pass clbits = [] try: for clbit in i.memory: clbit_label = exp.header.clbit_labels[clbit] - clbits.append( - creg_dict[clbit_label[0]][clbit_label[1]]) + clbits.append(creg_dict[clbit_label[0]][clbit_label[1]]) except Exception: # pylint: disable=broad-except pass if hasattr(circuit, name): instr_method = getattr(circuit, name) - if i.name in ['snapshot']: + if i.name in ["snapshot"]: _inst = instr_method( - i.label, - snapshot_type=i.snapshot_type, - qubits=qubits, - params=params) - elif i.name == 'initialize': + i.label, snapshot_type=i.snapshot_type, qubits=qubits, params=params + ) + elif i.name == "initialize": _inst = instr_method(params, qubits) - elif i.name == 'isometry': + elif i.name == "isometry": _inst = instr_method(*params, qubits, clbits) - elif i.name in ['mcx', 'mcu1', 'mcp']: + elif i.name in ["mcx", "mcu1", "mcp"]: _inst = instr_method(*params, qubits[:-1], qubits[-1], *clbits) else: _inst = instr_method(*params, *qubits, *clbits) - elif name == 'bfunc': - conditional['value'] = int(i.val, 16) + elif name == "bfunc": + conditional["value"] = int(i.val, 16) full_bit_size = sum([creg_dict[x].size for x in creg_dict]) mask_map = {} raw_map = {} @@ -151,22 +135,22 @@ def _experiments_to_circuits(qobj): raw_map[creg] = mask mask_map[int("".join(str(x) for x in mask), 2)] = creg creg = mask_map[int(i.mask, 16)] - conditional['register'] = creg_dict[creg] + conditional["register"] = creg_dict[creg] val = int(i.val, 16) mask = raw_map[creg] for j in reversed(mask): if j == 0: val = val >> 1 else: - conditional['value'] = val + conditional["value"] = val break else: _inst = temp_opaque_instruction = Instruction( - name=name, num_qubits=len(qubits), - num_clbits=len(clbits), params=params) + name=name, num_qubits=len(qubits), num_clbits=len(clbits), params=params + ) circuit.append(temp_opaque_instruction, qubits, clbits) - if conditional and name != 'bfunc': - _inst.c_if(conditional['register'], conditional['value']) + if conditional and name != "bfunc": + _inst.c_if(conditional["register"], conditional["value"]) conditional = {} circuits.append(circuit) return circuits @@ -174,15 +158,15 @@ def _experiments_to_circuits(qobj): def _disassemble_pulse_schedule(qobj) -> PulseModule: run_config = qobj.config.to_dict() - run_config.pop('pulse_library') + run_config.pop("pulse_library") - qubit_lo_freq = run_config.get('qubit_lo_freq') + qubit_lo_freq = run_config.get("qubit_lo_freq") if qubit_lo_freq: - run_config['qubit_lo_freq'] = [freq*1e9 for freq in qubit_lo_freq] + run_config["qubit_lo_freq"] = [freq * 1e9 for freq in qubit_lo_freq] - meas_lo_freq = run_config.get('meas_lo_freq') + meas_lo_freq = run_config.get("meas_lo_freq") if meas_lo_freq: - run_config['meas_lo_freq'] = [freq*1e9 for freq in meas_lo_freq] + run_config["meas_lo_freq"] = [freq * 1e9 for freq in meas_lo_freq] user_qobj_header = qobj.header.to_dict() @@ -190,19 +174,19 @@ def _disassemble_pulse_schedule(qobj) -> PulseModule: schedule_los = [] for program in qobj.experiments: program_los = {} - if hasattr(program, 'config'): - if hasattr(program.config, 'qubit_lo_freq'): + if hasattr(program, "config"): + if hasattr(program.config, "qubit_lo_freq"): for i, lo in enumerate(program.config.qubit_lo_freq): - program_los[pulse.DriveChannel(i)] = lo*1e9 + program_los[pulse.DriveChannel(i)] = lo * 1e9 - if hasattr(program.config, 'meas_lo_freq'): + if hasattr(program.config, "meas_lo_freq"): for i, lo in enumerate(program.config.meas_lo_freq): - program_los[pulse.MeasureChannel(i)] = lo*1e9 + program_los[pulse.MeasureChannel(i)] = lo * 1e9 schedule_los.append(program_los) if any(schedule_los): - run_config['schedule_los'] = schedule_los + run_config["schedule_los"] = schedule_los return PulseModule((_experiments_to_schedules(qobj), run_config, user_qobj_header)) diff --git a/qiskit/assembler/run_config.py b/qiskit/assembler/run_config.py index b7d7e76a7abf..dd675923a18a 100644 --- a/qiskit/assembler/run_config.py +++ b/qiskit/assembler/run_config.py @@ -27,8 +27,15 @@ class RunConfig(SimpleNamespace): parameter_binds (list[dict]): List of parameter bindings """ - def __init__(self, shots=None, max_credits=None, seed_simulator=None, - memory=None, parameter_binds=None, **kwargs): + def __init__( + self, + shots=None, + max_credits=None, + seed_simulator=None, + memory=None, + parameter_binds=None, + **kwargs, + ): """Initialize a RunConfig object Args: diff --git a/qiskit/circuit/__init__.py b/qiskit/circuit/__init__.py index 16ea4045f800..0608765a1248 100644 --- a/qiskit/circuit/__init__.py +++ b/qiskit/circuit/__init__.py @@ -215,6 +215,7 @@ from .classicalregister import ClassicalRegister, Clbit from .quantumregister import QuantumRegister, Qubit, AncillaRegister, AncillaQubit from .gate import Gate + # pylint: disable=cyclic-import from .controlledgate import ControlledGate from .instruction import Instruction diff --git a/qiskit/circuit/_utils.py b/qiskit/circuit/_utils.py index 5b093e7dca9b..642e777d451a 100644 --- a/qiskit/circuit/_utils.py +++ b/qiskit/circuit/_utils.py @@ -44,21 +44,21 @@ def _compute_control_matrix(base_mat, num_ctrl_qubits, ctrl_state=None): QiskitError: unrecognized mode or invalid ctrl_state """ num_target = int(numpy.log2(base_mat.shape[0])) - ctrl_dim = 2**num_ctrl_qubits - ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim-1]) + ctrl_dim = 2 ** num_ctrl_qubits + ctrl_grnd = numpy.repeat([[1], [0]], [1, ctrl_dim - 1]) if ctrl_state is None: ctrl_state = ctrl_dim - 1 elif isinstance(ctrl_state, str): ctrl_state = int(ctrl_state, 2) if isinstance(ctrl_state, int): if not 0 <= ctrl_state < ctrl_dim: - raise QiskitError('Invalid control state value specified.') + raise QiskitError("Invalid control state value specified.") else: - raise QiskitError('Invalid control state type specified.') + raise QiskitError("Invalid control state type specified.") ctrl_proj = numpy.diag(numpy.roll(ctrl_grnd, ctrl_state)) - full_mat = (numpy.kron(numpy.eye(2**num_target), - numpy.eye(ctrl_dim) - ctrl_proj) - + numpy.kron(base_mat, ctrl_proj)) + full_mat = numpy.kron(numpy.eye(2 ** num_target), numpy.eye(ctrl_dim) - ctrl_proj) + numpy.kron( + base_mat, ctrl_proj + ) return full_mat @@ -82,18 +82,16 @@ def _ctrl_state_to_int(ctrl_state, num_ctrl_qubits): assert len(ctrl_state) == num_ctrl_qubits ctrl_state = int(ctrl_state, 2) except ValueError as ex: - raise CircuitError('invalid control bit string: ' + ctrl_state) from ex + raise CircuitError("invalid control bit string: " + ctrl_state) from ex except AssertionError as ex: - raise CircuitError('invalid control bit string: length != ' - 'num_ctrl_qubits') from ex + raise CircuitError("invalid control bit string: length != " "num_ctrl_qubits") from ex if isinstance(ctrl_state, int): - if 0 <= ctrl_state < 2**num_ctrl_qubits: + if 0 <= ctrl_state < 2 ** num_ctrl_qubits: ctrl_state_std = ctrl_state else: - raise CircuitError('invalid control state specification') + raise CircuitError("invalid control state specification") elif ctrl_state is None: - ctrl_state_std = 2**num_ctrl_qubits - 1 + ctrl_state_std = 2 ** num_ctrl_qubits - 1 else: - raise CircuitError('invalid control state specification: {}'.format( - repr(ctrl_state))) + raise CircuitError("invalid control state specification: {}".format(repr(ctrl_state))) return ctrl_state_std diff --git a/qiskit/circuit/add_control.py b/qiskit/circuit/add_control.py index ccf8304afb23..9023280eca39 100644 --- a/qiskit/circuit/add_control.py +++ b/qiskit/circuit/add_control.py @@ -19,10 +19,12 @@ from . import ControlledGate, Gate, QuantumRegister, QuantumCircuit -def add_control(operation: Union[Gate, ControlledGate], - num_ctrl_qubits: int, - label: Union[str, None], - ctrl_state: Union[int, str, None]) -> ControlledGate: +def add_control( + operation: Union[Gate, ControlledGate], + num_ctrl_qubits: int, + label: Union[str, None], + ctrl_state: Union[int, str, None], +) -> ControlledGate: """For standard gates, if the controlled version already exists in the library, it will be returned (e.g. XGate.control() = CnotGate(). @@ -50,9 +52,10 @@ def add_control(operation: Union[Gate, ControlledGate], """ import qiskit.circuit.library.standard_gates as standard + if ctrl_state is None: - ctrl_state = 2**num_ctrl_qubits - 1 - if isinstance(operation, standard.RZGate) or operation.name == 'rz': + ctrl_state = 2 ** num_ctrl_qubits - 1 + if isinstance(operation, standard.RZGate) or operation.name == "rz": # num_ctrl_qubits > 1 # the condition matching 'name' above is to catch a test case, # 'TestControlledGate.test_rotation_gates', where the rz gate @@ -69,10 +72,12 @@ def add_control(operation: Union[Gate, ControlledGate], return cgate -def control(operation: Union[Gate, ControlledGate], - num_ctrl_qubits: Optional[int] = 1, - label: Optional[Union[None, str]] = None, - ctrl_state: Optional[Union[None, int, str]] = None) -> ControlledGate: +def control( + operation: Union[Gate, ControlledGate], + num_ctrl_qubits: Optional[int] = 1, + label: Optional[Union[None, str]] = None, + ctrl_state: Optional[Union[None, int, str]] = None, +) -> ControlledGate: """Return controlled version of gate using controlled rotations. This function first checks the name of the operation to see if it knows of a method from which to generate a controlled version. Currently these are `x`, `rx`, `ry`, and `rz`. @@ -95,90 +100,121 @@ def control(operation: Union[Gate, ControlledGate], CircuitError: gate contains non-gate in definition """ from math import pi + # pylint: disable=cyclic-import import qiskit.circuit.controlledgate as controlledgate - q_control = QuantumRegister(num_ctrl_qubits, name='control') - q_target = QuantumRegister(operation.num_qubits, name='target') + q_control = QuantumRegister(num_ctrl_qubits, name="control") + q_target = QuantumRegister(operation.num_qubits, name="target") q_ancillae = None # TODO: add - controlled_circ = QuantumCircuit(q_control, q_target, - name='c_{}'.format(operation.name)) + controlled_circ = QuantumCircuit(q_control, q_target, name="c_{}".format(operation.name)) global_phase = 0 - if operation.name == 'x' or ( - isinstance(operation, controlledgate.ControlledGate) and - operation.base_gate.name == 'x'): + if operation.name == "x" or ( + isinstance(operation, controlledgate.ControlledGate) and operation.base_gate.name == "x" + ): controlled_circ.mct(q_control[:] + q_target[:-1], q_target[-1], q_ancillae) if operation.definition is not None and operation.definition.global_phase: global_phase += operation.definition.global_phase else: - basis = ['p', 'u', 'x', 'z', 'rx', 'ry', 'rz', 'cx'] + basis = ["p", "u", "x", "z", "rx", "ry", "rz", "cx"] unrolled_gate = _unroll_gate(operation, basis_gates=basis) if unrolled_gate.definition.global_phase: global_phase += unrolled_gate.definition.global_phase definition = unrolled_gate.definition - bit_indices = {bit: index - for bits in [definition.qubits, definition.clbits] - for index, bit in enumerate(bits)} + bit_indices = { + bit: index + for bits in [definition.qubits, definition.clbits] + for index, bit in enumerate(bits) + } for gate, qargs, _ in definition.data: - if gate.name == 'x': - controlled_circ.mct(q_control, q_target[bit_indices[qargs[0]]], - q_ancillae) - elif gate.name == 'rx': - controlled_circ.mcrx(gate.definition.data[0][0].params[0], - q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) - elif gate.name == 'ry': - controlled_circ.mcry(gate.definition.data[0][0].params[0], - q_control, q_target[bit_indices[qargs[0]]], - q_ancillae, mode='noancilla', - use_basis_gates=True) - elif gate.name == 'rz': - controlled_circ.mcrz(gate.definition.data[0][0].params[0], - q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) - elif gate.name == 'p': + if gate.name == "x": + controlled_circ.mct(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) + elif gate.name == "rx": + controlled_circ.mcrx( + gate.definition.data[0][0].params[0], + q_control, + q_target[bit_indices[qargs[0]]], + use_basis_gates=True, + ) + elif gate.name == "ry": + controlled_circ.mcry( + gate.definition.data[0][0].params[0], + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + mode="noancilla", + use_basis_gates=True, + ) + elif gate.name == "rz": + controlled_circ.mcrz( + gate.definition.data[0][0].params[0], + q_control, + q_target[bit_indices[qargs[0]]], + use_basis_gates=True, + ) + elif gate.name == "p": from qiskit.circuit.library import MCPhaseGate - controlled_circ.append(MCPhaseGate(gate.params[0], num_ctrl_qubits), - q_control[:] + [q_target[bit_indices[qargs[0]]]]) - elif gate.name == 'cx': - controlled_circ.mct(q_control[:] + [q_target[bit_indices[qargs[0]]]], - q_target[bit_indices[qargs[1]]], - q_ancillae) - elif gate.name == 'u': + + controlled_circ.append( + MCPhaseGate(gate.params[0], num_ctrl_qubits), + q_control[:] + [q_target[bit_indices[qargs[0]]]], + ) + elif gate.name == "cx": + controlled_circ.mct( + q_control[:] + [q_target[bit_indices[qargs[0]]]], + q_target[bit_indices[qargs[1]]], + q_ancillae, + ) + elif gate.name == "u": theta, phi, lamb = gate.params if num_ctrl_qubits == 1: if theta == 0 and phi == 0: controlled_circ.cp(lamb, q_control[0], q_target[bit_indices[qargs[0]]]) else: - controlled_circ.cu(theta, phi, lamb, 0, q_control[0], - q_target[bit_indices[qargs[0]]]) + controlled_circ.cu( + theta, phi, lamb, 0, q_control[0], q_target[bit_indices[qargs[0]]] + ) else: if phi == -pi / 2 and lamb == pi / 2: - controlled_circ.mcrx(theta, q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) + controlled_circ.mcrx( + theta, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True + ) elif phi == 0 and lamb == 0: - controlled_circ.mcry(theta, q_control, q_target[bit_indices[qargs[0]]], - q_ancillae, use_basis_gates=True) + controlled_circ.mcry( + theta, + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + use_basis_gates=True, + ) elif theta == 0 and phi == 0: - controlled_circ.mcrz(lamb, q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) + controlled_circ.mcrz( + lamb, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True + ) else: - controlled_circ.mcrz(lamb, q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) - controlled_circ.mcry(theta, q_control, q_target[bit_indices[qargs[0]]], - q_ancillae, use_basis_gates=True) - controlled_circ.mcrz(phi, q_control, q_target[bit_indices[qargs[0]]], - use_basis_gates=True) - elif gate.name == 'z': + controlled_circ.mcrz( + lamb, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True + ) + controlled_circ.mcry( + theta, + q_control, + q_target[bit_indices[qargs[0]]], + q_ancillae, + use_basis_gates=True, + ) + controlled_circ.mcrz( + phi, q_control, q_target[bit_indices[qargs[0]]], use_basis_gates=True + ) + elif gate.name == "z": controlled_circ.h(q_target[bit_indices[qargs[0]]]) - controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], - q_ancillae) + controlled_circ.mcx(q_control, q_target[bit_indices[qargs[0]]], q_ancillae) controlled_circ.h(q_target[bit_indices[qargs[0]]]) else: - raise CircuitError('gate contains non-controllable instructions: {}'.format( - gate.name)) + raise CircuitError( + "gate contains non-controllable instructions: {}".format(gate.name) + ) if gate.definition is not None and gate.definition.global_phase: global_phase += gate.definition.global_phase # apply controlled global phase @@ -186,8 +222,7 @@ def control(operation: Union[Gate, ControlledGate], if len(q_control) < 2: controlled_circ.p(global_phase, q_control) else: - controlled_circ.mcp(global_phase, - q_control[:-1], q_control[-1]) + controlled_circ.mcp(global_phase, q_control[:-1], q_control[-1]) if isinstance(operation, controlledgate.ControlledGate): new_num_ctrl_qubits = num_ctrl_qubits + operation.num_ctrl_qubits new_ctrl_state = operation.ctrl_state << num_ctrl_qubits | ctrl_state @@ -203,24 +238,27 @@ def control(operation: Union[Gate, ControlledGate], # is named like "cc", else it is named like # "c". if new_num_ctrl_qubits > 2: - ctrl_substr = 'c{:d}'.format(new_num_ctrl_qubits) + ctrl_substr = "c{:d}".format(new_num_ctrl_qubits) else: - ctrl_substr = ('{0}' * new_num_ctrl_qubits).format('c') - new_name = '{}{}'.format(ctrl_substr, base_name) - cgate = controlledgate.ControlledGate(new_name, - controlled_circ.num_qubits, - operation.params, - label=label, - num_ctrl_qubits=new_num_ctrl_qubits, - definition=controlled_circ, - ctrl_state=new_ctrl_state, - base_gate=base_gate) + ctrl_substr = ("{0}" * new_num_ctrl_qubits).format("c") + new_name = "{}{}".format(ctrl_substr, base_name) + cgate = controlledgate.ControlledGate( + new_name, + controlled_circ.num_qubits, + operation.params, + label=label, + num_ctrl_qubits=new_num_ctrl_qubits, + definition=controlled_circ, + ctrl_state=new_ctrl_state, + base_gate=base_gate, + ) return cgate def _gate_to_dag(operation): from qiskit.converters.circuit_to_dag import circuit_to_dag - if hasattr(operation, 'definition') and operation.definition is not None: + + if hasattr(operation, "definition") and operation.definition is not None: return circuit_to_dag(operation.definition) else: qr = QuantumRegister(operation.num_qubits) @@ -232,6 +270,7 @@ def _gate_to_dag(operation): def _unroll_gate(operation, basis_gates): from qiskit.converters.dag_to_circuit import dag_to_circuit from qiskit.transpiler.passes import Unroller + unroller = Unroller(basis_gates) dag = _gate_to_dag(operation) opqc = dag_to_circuit(unroller.run(dag)) diff --git a/qiskit/circuit/barrier.py b/qiskit/circuit/barrier.py index 0dba2733673c..c10b32069595 100644 --- a/qiskit/circuit/barrier.py +++ b/qiskit/circuit/barrier.py @@ -33,4 +33,4 @@ def broadcast_arguments(self, qargs, cargs): yield [qarg for sublist in qargs for qarg in sublist], [] def c_if(self, classical, val): - raise QiskitError('Barriers are compiler directives and cannot be conditional.') + raise QiskitError("Barriers are compiler directives and cannot be conditional.") diff --git a/qiskit/circuit/bit.py b/qiskit/circuit/bit.py index c3b8171329fe..b774646b0075 100644 --- a/qiskit/circuit/bit.py +++ b/qiskit/circuit/bit.py @@ -21,11 +21,10 @@ class Bit: """Implement a generic bit.""" - __slots__ = {'_register', '_index', '_hash', '_repr'} + __slots__ = {"_register", "_index", "_hash", "_repr"} def __init__(self, register=None, index=None): - """Create a new generic bit. - """ + """Create a new generic bit.""" if (register, index) == (None, None): self._register = None self._index = None @@ -51,19 +50,21 @@ def __init__(self, register=None, index=None): self._register = register self._index = index self._hash = hash((self._register, self._index)) - self._repr = "%s(%s, %s)" % (self.__class__.__name__, - self._register, self._index) + self._repr = "%s(%s, %s)" % (self.__class__.__name__, self._register, self._index) @property def register(self): """Get bit's register.""" if (self._register, self._index) == (None, None): - raise CircuitError('Attmped to query register of a new-style Bit.') + raise CircuitError("Attmped to query register of a new-style Bit.") - warnings.warn('Back-references to from Bit instances to their containing ' - 'Registers have been deprecated. Instead, inspect Registers ' - 'to find their contained Bits.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "Back-references to from Bit instances to their containing " + "Registers have been deprecated. Instead, inspect Registers " + "to find their contained Bits.", + DeprecationWarning, + stacklevel=2, + ) return self._register @@ -71,12 +72,15 @@ def register(self): def index(self): """Get bit's index.""" if (self._register, self._index) == (None, None): - raise CircuitError('Attmped to query index of a new-style Bit.') - - warnings.warn('Back-references to from Bit instances to their containing ' - 'Registers have been deprecated. Instead, inspect Registers ' - 'to find their contained Bits.', - DeprecationWarning, stacklevel=2) + raise CircuitError("Attmped to query index of a new-style Bit.") + + warnings.warn( + "Back-references to from Bit instances to their containing " + "Registers have been deprecated. Instead, inspect Registers " + "to find their contained Bits.", + DeprecationWarning, + stacklevel=2, + ) return self._index diff --git a/qiskit/circuit/classicalfunction/__init__.py b/qiskit/circuit/classicalfunction/__init__.py index 0d37949bc4b4..ed5f7749f2cc 100644 --- a/qiskit/circuit/classicalfunction/__init__.py +++ b/qiskit/circuit/classicalfunction/__init__.py @@ -96,8 +96,11 @@ def grover_oracle(a: Int1, b: Int1, c: Int1, d: Int1) -> Int1: """ from .classicalfunction import ClassicalFunction -from .exceptions import (ClassicalFunctionParseError, ClassicalFunctionCompilerError, - ClassicalFunctionCompilerTypeError) +from .exceptions import ( + ClassicalFunctionParseError, + ClassicalFunctionCompilerError, + ClassicalFunctionCompilerTypeError, +) from .boolean_expression import BooleanExpression diff --git a/qiskit/circuit/classicalfunction/boolean_expression.py b/qiskit/circuit/classicalfunction/boolean_expression.py index be07939374c7..06f8aa93911c 100644 --- a/qiskit/circuit/classicalfunction/boolean_expression.py +++ b/qiskit/circuit/classicalfunction/boolean_expression.py @@ -37,15 +37,19 @@ def __init__(self, expression: str, name: str = None) -> None: """ if not HAS_TWEEDLEDUM: raise MissingOptionalLibraryError( - libname='tweedledum', - name='BooleanExpression compiler', - pip_install='pip install tweedledum') + libname="tweedledum", + name="BooleanExpression compiler", + pip_install="pip install tweedledum", + ) from tweedledum import BoolFunction + self._tweedledum_bool_expression = BoolFunction.from_expression(expression) - short_expr_for_name = (expression[:10] + '...') if len(expression) > 13 else expression - num_qubits = (self._tweedledum_bool_expression.num_outputs() + - self._tweedledum_bool_expression.num_inputs()) + short_expr_for_name = (expression[:10] + "...") if len(expression) > 13 else expression + num_qubits = ( + self._tweedledum_bool_expression.num_outputs() + + self._tweedledum_bool_expression.num_inputs() + ) super().__init__(name or short_expr_for_name, num_qubits=num_qubits, params=[]) def simulate(self, bitstring: str) -> bool: @@ -60,13 +64,17 @@ def simulate(self, bitstring: str) -> bool: bool: result of the evaluation. """ from tweedledum import BitVec + bits = [] for bit in bitstring: bits.append(BitVec(1, bit)) return bool(self._tweedledum_bool_expression.simulate(*bits)) - def synth(self, registerless: bool = True, - synthesizer: Optional[Callable[["BooleanExpression"], QuantumCircuit]] = None): + def synth( + self, + registerless: bool = True, + synthesizer: Optional[Callable[["BooleanExpression"], QuantumCircuit]] = None, + ): """Synthesis the logic network into a :class:`~qiskit.circuit.QuantumCircuit`. Args: @@ -86,6 +94,7 @@ def synth(self, registerless: bool = True, if synthesizer is None: from tweedledum.synthesis import pkrm_synth # pylint: disable=no-name-in-module from .utils import tweedledum2qiskit + truth_table = self._tweedledum_bool_expression.truth_table(output_bit=0) return tweedledum2qiskit(pkrm_synth(truth_table), name=self.name, qregs=qregs) return synthesizer(self) @@ -109,18 +118,22 @@ def from_dimacs_file(cls, filename: str): """ if not HAS_TWEEDLEDUM: raise MissingOptionalLibraryError( - libname='tweedledum', - name='BooleanExpression compiler', - pip_install='pip install tweedledum') + libname="tweedledum", + name="BooleanExpression compiler", + pip_install="pip install tweedledum", + ) from tweedledum import BoolFunction expr_obj = cls.__new__(cls) if not isfile(filename): - raise FileNotFoundError('The file %s does not exists.' % filename) + raise FileNotFoundError("The file %s does not exists." % filename) expr_obj._tweedledum_bool_expression = BoolFunction.from_dimacs_file(filename) - num_qubits = (expr_obj._tweedledum_bool_expression.num_inputs() + - expr_obj._tweedledum_bool_expression.num_outputs()) + num_qubits = ( + expr_obj._tweedledum_bool_expression.num_inputs() + + expr_obj._tweedledum_bool_expression.num_outputs() + ) super(BooleanExpression, expr_obj).__init__( # pylint: disable=no-value-for-parameter - name=basename(filename), num_qubits=num_qubits, params=[]) + name=basename(filename), num_qubits=num_qubits, params=[] + ) return expr_obj diff --git a/qiskit/circuit/classicalfunction/classical_function_visitor.py b/qiskit/circuit/classicalfunction/classical_function_visitor.py index 267045f371b7..c4a6e1adeba5 100644 --- a/qiskit/circuit/classicalfunction/classical_function_visitor.py +++ b/qiskit/circuit/classicalfunction/classical_function_visitor.py @@ -24,21 +24,24 @@ class ClassicalFunctionVisitor(ast.NodeVisitor): """Node visitor as defined in https://docs.python.org/3/library/ast.html#ast.NodeVisitor""" + # pylint: disable=invalid-name - bitops = {_ast.BitAnd: 'create_and', - _ast.BitOr: 'create_or', - _ast.BitXor: 'create_xor', - _ast.And: 'create_and', - _ast.Or: 'create_or', - _ast.Not: 'create_not' - } + bitops = { + _ast.BitAnd: "create_and", + _ast.BitOr: "create_or", + _ast.BitXor: "create_xor", + _ast.And: "create_and", + _ast.Or: "create_or", + _ast.Not: "create_not", + } def __init__(self): if not HAS_TWEEDLEDUM: raise MissingOptionalLibraryError( - libname='tweedledum', - name='classical function compiler', - pip_install='pip install tweedledum') + libname="tweedledum", + name="classical function compiler", + pip_install="pip install tweedledum", + ) self.scopes = [] self.args = [] self._network = None @@ -58,15 +61,16 @@ def visit_FunctionDef(self, node): from tweedledum.classical import LogicNetwork # pylint: disable=no-name-in-module else: raise MissingOptionalLibraryError( - libname='tweedledum', - name='classical function compiler', - pip_install='pip install tweedledum') + libname="tweedledum", + name="classical function compiler", + pip_install="pip install tweedledum", + ) if node.returns is None: raise ClassicalFunctionParseError("return type is needed") - scope = {'return': (node.returns.id, None), node.returns.id: ('type', None)} + scope = {"return": (node.returns.id, None), node.returns.id: ("type", None)} # Extend scope with the decorator's names - scope.update({decorator.id: ('decorator', None) for decorator in node.decorator_list}) + scope.update({decorator.id: ("decorator", None) for decorator in node.decorator_list}) self.scopes.append(scope) self._network = LogicNetwork() @@ -76,7 +80,7 @@ def visit_FunctionDef(self, node): def visit_Return(self, node): """The return type should match the return type hint.""" _type, signal = self.visit(node.value) - if _type != self.scopes[-1]['return'][0]: + if _type != self.scopes[-1]["return"][0]: raise ClassicalFunctionParseError("return type error") self._network.create_po(signal) @@ -95,48 +99,61 @@ def bit_binop(self, op, values): binop = getattr(self._network, bitop) left_type, left_signal = values[0] - if left_type != 'Int1': + if left_type != "Int1": raise ClassicalFunctionParseError("binop type error") for right_type, right_signal in values[1:]: - if right_type != 'Int1': + if right_type != "Int1": raise ClassicalFunctionParseError("binop type error") left_signal = binop(left_signal, right_signal) - return 'Int1', left_signal + return "Int1", left_signal def visit_BoolOp(self, node): """Handles ``and`` and ``or``. - node.left=Int1 and node.right=Int1 return Int1 """ + node.left=Int1 and node.right=Int1 return Int1""" return self.bit_binop(node.op, [self.visit(value) for value in node.values]) def visit_BinOp(self, node): """Handles ``&``, ``^``, and ``|``. - node.left=Int1 and node.right=Int1 return Int1 """ + node.left=Int1 and node.right=Int1 return Int1""" return self.bit_binop(node.op, [self.visit(node.left), self.visit(node.right)]) def visit_UnaryOp(self, node): - """Handles ``~``. Cannot operate on Int1s. """ + """Handles ``~``. Cannot operate on Int1s.""" operand_type, operand_signal = self.visit(node.operand) - if operand_type != 'Int1': + if operand_type != "Int1": raise ClassicalFunctionCompilerTypeError( - "UntaryOp.op %s only support operation on Int1s for now" % node.op) + "UntaryOp.op %s only support operation on Int1s for now" % node.op + ) bitop = ClassicalFunctionVisitor.bitops.get(type(node.op)) if not bitop: raise ClassicalFunctionCompilerTypeError( - "UntaryOp.op %s does not operate with Int1 type " % node.op) - return 'Int1', getattr(self._network, bitop)(operand_signal) + "UntaryOp.op %s does not operate with Int1 type " % node.op + ) + return "Int1", getattr(self._network, bitop)(operand_signal) def visit_Name(self, node): - """Reduce variable names. """ + """Reduce variable names.""" if node.id not in self.scopes[-1]: - raise ClassicalFunctionParseError('out of scope: %s' % node.id) + raise ClassicalFunctionParseError("out of scope: %s" % node.id) return self.scopes[-1][node.id] def generic_visit(self, node): """Catch all for the unhandled nodes.""" - if isinstance(node, (_ast.arguments, _ast.arg, _ast.Load, _ast.BitAnd, - _ast.BitOr, _ast.BitXor, _ast.BoolOp, _ast.Or)): + if isinstance( + node, + ( + _ast.arguments, + _ast.arg, + _ast.Load, + _ast.BitAnd, + _ast.BitOr, + _ast.BitXor, + _ast.BoolOp, + _ast.Or, + ), + ): return super().generic_visit(node) raise ClassicalFunctionParseError("Unknown node: %s" % type(node)) @@ -146,5 +163,5 @@ def extend_scope(self, args_node: _ast.arguments) -> None: if arg.annotation is None: raise ClassicalFunctionParseError("argument type is needed") self.args.append(arg.arg) - self.scopes[-1][arg.annotation.id] = ('type', None) + self.scopes[-1][arg.annotation.id] = ("type", None) self.scopes[-1][arg.arg] = (arg.annotation.id, self._network.create_pi()) diff --git a/qiskit/circuit/classicalfunction/classicalfunction.py b/qiskit/circuit/classicalfunction/classicalfunction.py index 0b0b2c0519f4..330b6a30464a 100644 --- a/qiskit/circuit/classicalfunction/classicalfunction.py +++ b/qiskit/circuit/classicalfunction/classicalfunction.py @@ -39,20 +39,23 @@ def __init__(self, source, name=None): QiskitError: If source is not a string. """ if not isinstance(source, str): - raise QiskitError('ClassicalFunction needs a source code as a string.') + raise QiskitError("ClassicalFunction needs a source code as a string.") if not HAS_TWEEDLEDUM: raise MissingOptionalLibraryError( - libname='tweedledum', - name='classical function compiler', - pip_install='pip install tweedledum') + libname="tweedledum", + name="classical function compiler", + pip_install="pip install tweedledum", + ) self._ast = ast.parse(source) self._network = None self._scopes = None self._args = None self._truth_table = None - super().__init__(name or '*classicalfunction*', - num_qubits=sum([qreg.size for qreg in self.qregs]), - params=[]) + super().__init__( + name or "*classicalfunction*", + num_qubits=sum([qreg.size for qreg in self.qregs]), + params=[], + ) def compile(self): """Parses and creates the logical circuit""" @@ -107,8 +110,9 @@ def simulate(self, bitstring: str) -> bool: Returns: bool: result of the evaluation. - """ + """ from tweedledum.classical import simulate # pylint: disable=no-name-in-module + return simulate(self._network, bitstring) def simulate_all(self): @@ -120,22 +124,25 @@ def simulate_all(self): """ result = list() for position in range(2 ** self._network.num_pis()): - sim_result = ''.join([str(int(tt[position])) for tt in self.truth_table]) + sim_result = "".join([str(int(tt[position])) for tt in self.truth_table]) result.append(sim_result) - return ''.join(reversed(result)) + return "".join(reversed(result)) @property def truth_table(self): """Returns (and computes) the truth table""" if self._truth_table is None: from tweedledum.classical import simulate # pylint: disable=no-name-in-module + self._truth_table = simulate(self._network) return self._truth_table - def synth(self, registerless: bool = True, - synthesizer: Optional[Callable[[ClassicalElement], QuantumCircuit]] = None)\ - -> QuantumCircuit: + def synth( + self, + registerless: bool = True, + synthesizer: Optional[Callable[[ClassicalElement], QuantumCircuit]] = None, + ) -> QuantumCircuit: """Synthesis the logic network into a :class:`~qiskit.circuit.QuantumCircuit`. Args: @@ -157,8 +164,7 @@ def synth(self, registerless: bool = True, from .utils import tweedledum2qiskit from tweedledum.synthesis import pkrm_synth # pylint: disable=no-name-in-module - return tweedledum2qiskit(pkrm_synth(self.truth_table[0]), - name=self.name, qregs=qregs) + return tweedledum2qiskit(pkrm_synth(self.truth_table[0]), name=self.name, qregs=qregs) def _define(self): """The definition of the classical function is its synthesis""" @@ -167,8 +173,8 @@ def _define(self): @property def qregs(self): """The list of qregs used by the classicalfunction""" - qregs = [QuantumRegister(1, name=arg) for arg in self.args if self.types[0][arg] == 'Int1'] + qregs = [QuantumRegister(1, name=arg) for arg in self.args if self.types[0][arg] == "Int1"] qregs.reverse() - if self.types[0]['return'] == 'Int1': - qregs.append(QuantumRegister(1, name='return')) + if self.types[0]["return"] == "Int1": + qregs.append(QuantumRegister(1, name="return")) return qregs diff --git a/qiskit/circuit/classicalfunction/exceptions.py b/qiskit/circuit/classicalfunction/exceptions.py index 3e016399045e..a9da94a98990 100644 --- a/qiskit/circuit/classicalfunction/exceptions.py +++ b/qiskit/circuit/classicalfunction/exceptions.py @@ -17,16 +17,19 @@ class ClassicalFunctionCompilerError(QiskitError): """ClassicalFunction compiler generic error.""" + pass class ClassicalFunctionParseError(ClassicalFunctionCompilerError): """ClassicalFunction compiler parse error. The classicalfunction function fails at parsing time.""" + pass class ClassicalFunctionCompilerTypeError(ClassicalFunctionCompilerError): """ClassicalFunction compiler type error. The classicalfunction function fails at type checking time.""" + pass diff --git a/qiskit/circuit/classicalfunction/types.py b/qiskit/circuit/classicalfunction/types.py index 17077fee5b03..095fdb867c62 100644 --- a/qiskit/circuit/classicalfunction/types.py +++ b/qiskit/circuit/classicalfunction/types.py @@ -14,5 +14,5 @@ from typing import NewType -Int1 = NewType('Int1', bool) -Int2 = NewType('Int2', int) +Int1 = NewType("Int1", bool) +Int2 = NewType("Int2", int) diff --git a/qiskit/circuit/classicalfunction/utils.py b/qiskit/circuit/classicalfunction/utils.py index 00625d868bee..b8b8fc697eae 100644 --- a/qiskit/circuit/classicalfunction/utils.py +++ b/qiskit/circuit/classicalfunction/utils.py @@ -15,42 +15,58 @@ try: from tweedledum.ir import Qubit # pylint: disable=no-name-in-module from tweedledum.passes import parity_decomp # pylint: disable=no-name-in-module + HAS_TWEEDLEDUM = True except Exception: # pylint: disable=broad-except HAS_TWEEDLEDUM = False from qiskit.circuit import QuantumCircuit -from qiskit.circuit.library.standard_gates import (HGate, SGate, SdgGate, SwapGate, TGate, TdgGate, - XGate, YGate, ZGate) +from qiskit.circuit.library.standard_gates import ( + HGate, + SGate, + SdgGate, + SwapGate, + TGate, + TdgGate, + XGate, + YGate, + ZGate, +) _QISKIT_OPS = { - 'std.h': HGate, 'std.s': SGate, 'std.sdg': SdgGate, 'std.swap': SwapGate, - 'std.t': TGate, 'std.tdg': TdgGate, 'std.x': XGate, 'std.y': YGate, - 'std.z': ZGate + "std.h": HGate, + "std.s": SGate, + "std.sdg": SdgGate, + "std.swap": SwapGate, + "std.t": TGate, + "std.tdg": TdgGate, + "std.x": XGate, + "std.y": YGate, + "std.z": ZGate, } def _convert_tweedledum_operator(op): base_gate = _QISKIT_OPS.get(op.kind()) if base_gate is None: - if op.kind() == 'py_operator': + if op.kind() == "py_operator": return op.py_op() else: - raise RuntimeError('Unrecognized operator: %s' % op.kind()) + raise RuntimeError("Unrecognized operator: %s" % op.kind()) # TODO: need to deal with cbits too! if op.num_controls() > 0: qubits = op.qubits() - ctrl_state = '' - for qubit in qubits[:op.num_controls()]: - ctrl_state += '{}'.format(int(qubit.polarity() == Qubit.Polarity.positive)) + ctrl_state = "" + for qubit in qubits[: op.num_controls()]: + ctrl_state += "{}".format(int(qubit.polarity() == Qubit.Polarity.positive)) return base_gate().control(len(ctrl_state), ctrl_state=ctrl_state[::-1]) return base_gate() def tweedledum2qiskit(tweedledum_circuit, name=None, qregs=None): - """ Converts a `Tweedledum `_ + """Converts a `Tweedledum `_ circuit into a Qiskit circuit. Args: tweedledum_circuit (tweedledum.ir.Circuit): Tweedledum circuit. diff --git a/qiskit/circuit/classicalregister.py b/qiskit/circuit/classicalregister.py index 8ceb59a9bced..4b85095be29a 100644 --- a/qiskit/circuit/classicalregister.py +++ b/qiskit/circuit/classicalregister.py @@ -37,8 +37,9 @@ def __init__(self, register=None, index=None): if register is None or isinstance(register, ClassicalRegister): super().__init__(register, index) else: - raise CircuitError('Clbit needs a ClassicalRegister and %s was provided' % - type(register).__name__) + raise CircuitError( + "Clbit needs a ClassicalRegister and %s was provided" % type(register).__name__ + ) class ClassicalRegister(Register): @@ -47,7 +48,7 @@ class ClassicalRegister(Register): # Counter for the number of instances in this class. instances_counter = itertools.count() # Prefix to use for auto naming. - prefix = 'c' + prefix = "c" bit_type = Clbit def qasm(self): diff --git a/qiskit/circuit/controlledgate.py b/qiskit/circuit/controlledgate.py index 1f1a76e2f41f..e9fb69ccf614 100644 --- a/qiskit/circuit/controlledgate.py +++ b/qiskit/circuit/controlledgate.py @@ -27,11 +27,17 @@ class ControlledGate(Gate): """Controlled unitary gate.""" - def __init__(self, name: str, num_qubits: int, params: List, - label: Optional[str] = None, num_ctrl_qubits: Optional[int] = 1, - definition: Optional['QuantumCircuit'] = None, - ctrl_state: Optional[Union[int, str]] = None, - base_gate: Optional[Gate] = None): + def __init__( + self, + name: str, + num_qubits: int, + params: List, + label: Optional[str] = None, + num_ctrl_qubits: Optional[int] = 1, + definition: Optional["QuantumCircuit"] = None, + ctrl_state: Optional[Union[int, str]] = None, + base_gate: Optional[Gate] = None, + ): """Create a new ControlledGate. In the new gate the first ``num_ctrl_qubits`` of the gate are the controls. @@ -105,21 +111,21 @@ def definition(self) -> List: closed_gate = self.copy() closed_gate.ctrl_state = None bit_ctrl_state = bin(self.ctrl_state)[2:].zfill(self.num_ctrl_qubits) - qreg = QuantumRegister(self.num_qubits, 'q') + qreg = QuantumRegister(self.num_qubits, "q") qc_open_ctrl = QuantumCircuit(qreg) for qind, val in enumerate(bit_ctrl_state[::-1]): - if val == '0': + if val == "0": qc_open_ctrl.x(qind) qc_open_ctrl.append(closed_gate, qargs=qreg[:]) for qind, val in enumerate(bit_ctrl_state[::-1]): - if val == '0': + if val == "0": qc_open_ctrl.x(qind) return qc_open_ctrl else: return super().definition @definition.setter - def definition(self, excited_def: 'QuantumCircuit'): + def definition(self, excited_def: "QuantumCircuit"): """Set controlled gate definition with closed controls. Args: @@ -138,7 +144,7 @@ def name(self) -> str: the control state for the gate. """ if self._open_ctrl: - return f'{self._name}_o{self.ctrl_state}' + return f"{self._name}_o{self.ctrl_state}" else: return self._name @@ -168,11 +174,10 @@ def num_ctrl_qubits(self, num_ctrl_qubits): Raises: CircuitError: num_ctrl_qubits is not an integer in [1, num_qubits - 1]. """ - if (num_ctrl_qubits == int(num_ctrl_qubits) and - 1 <= num_ctrl_qubits < self.num_qubits): + if num_ctrl_qubits == int(num_ctrl_qubits) and 1 <= num_ctrl_qubits < self.num_qubits: self._num_ctrl_qubits = num_ctrl_qubits else: - raise CircuitError('The number of control qubits must be in [1, num_qubits-1]') + raise CircuitError("The number of control qubits must be in [1, num_qubits-1]") @property def ctrl_state(self) -> int: @@ -204,8 +209,7 @@ def params(self): if self.base_gate: return self.base_gate.params else: - raise CircuitError('Controlled gate does not define base gate ' - 'for extracting params') + raise CircuitError("Controlled gate does not define base gate " "for extracting params") @params.setter def params(self, parameters): @@ -220,8 +224,7 @@ def params(self, parameters): if self.base_gate: self.base_gate.params = parameters else: - raise CircuitError('Controlled gate does not define base gate ' - 'for extracting params') + raise CircuitError("Controlled gate does not define base gate " "for extracting params") def __deepcopy__(self, _memo=None): cpy = copy.copy(self) @@ -233,18 +236,19 @@ def __deepcopy__(self, _memo=None): @property def _open_ctrl(self) -> bool: """Return whether gate has any open controls""" - return self.ctrl_state < 2**self.num_ctrl_qubits - 1 + return self.ctrl_state < 2 ** self.num_ctrl_qubits - 1 def __eq__(self, other) -> bool: - return (isinstance(other, ControlledGate) and - self.num_ctrl_qubits == other.num_ctrl_qubits and - self.ctrl_state == other.ctrl_state and - self.base_gate == other.base_gate and - self.num_qubits == other.num_qubits and - self.num_clbits == other.num_clbits and - self.definition == other.definition) - - def inverse(self) -> 'ControlledGate': + return ( + isinstance(other, ControlledGate) + and self.num_ctrl_qubits == other.num_ctrl_qubits + and self.ctrl_state == other.ctrl_state + and self.base_gate == other.base_gate + and self.num_qubits == other.num_qubits + and self.num_clbits == other.num_clbits + and self.definition == other.definition + ) + + def inverse(self) -> "ControlledGate": """Invert this gate by calling inverse on the base gate.""" - return self.base_gate.inverse().control(self.num_ctrl_qubits, - ctrl_state=self.ctrl_state) + return self.base_gate.inverse().control(self.num_ctrl_qubits, ctrl_state=self.ctrl_state) diff --git a/qiskit/circuit/delay.py b/qiskit/circuit/delay.py index 0c7d843a63c9..624455428101 100644 --- a/qiskit/circuit/delay.py +++ b/qiskit/circuit/delay.py @@ -22,10 +22,10 @@ class Delay(Instruction): """Do nothing and just delay/wait/idle for a specified duration.""" - def __init__(self, duration, unit='dt'): + def __init__(self, duration, unit="dt"): """Create new delay instruction.""" - if unit not in {'s', 'ms', 'us', 'ns', 'ps', 'dt'}: - raise CircuitError('Unknown unit %s is specified.' % unit) + if unit not in {"s", "ms", "us", "ns", "ps", "dt"}: + raise CircuitError("Unknown unit %s is specified." % unit) super().__init__("delay", 1, 0, params=[duration], unit=unit) @@ -37,7 +37,7 @@ def broadcast_arguments(self, qargs, cargs): yield [qarg for sublist in qargs for qarg in sublist], [] def c_if(self, classical, val): - raise CircuitError('Conditional Delay is not yet implemented.') + raise CircuitError("Conditional Delay is not yet implemented.") @property def duration(self): @@ -51,8 +51,7 @@ def duration(self, duration): def __array__(self, dtype=None): """Return the identity matrix.""" - return np.array([[1, 0], - [0, 1]], dtype=dtype) + return np.array([[1, 0], [0, 1]], dtype=dtype) def to_matrix(self) -> np.ndarray: """Return a Numpy.array for the unitary matrix. This has been @@ -65,15 +64,14 @@ def to_matrix(self) -> np.ndarray: def __repr__(self): """Return the official string representing the delay.""" - return "%s(duration=%s[unit=%s])" % \ - (self.__class__.__name__, self.params[0], self.unit) + return "%s(duration=%s[unit=%s])" % (self.__class__.__name__, self.params[0], self.unit) def validate_parameter(self, parameter): """Delay parameter (i.e. duration) must be int, float or ParameterExpression.""" if isinstance(parameter, int): return parameter elif isinstance(parameter, float): - if self.unit == 'dt': + if self.unit == "dt": raise CircuitError("Integer duration is expected for 'dt' unit.") return parameter elif isinstance(parameter, ParameterExpression): @@ -82,7 +80,7 @@ def validate_parameter(self, parameter): if not parameter._symbol_expr.is_real: raise CircuitError(f"Bound parameter expression is complex in delay {self.name}") fval = float(parameter) - if self.unit == 'dt': + if self.unit == "dt": ival = int(parameter) rounding_error = abs(fval - ival) if rounding_error > 1e-15: diff --git a/qiskit/circuit/duration.py b/qiskit/circuit/duration.py index df4fae2baf4e..d8a14eb8aa37 100644 --- a/qiskit/circuit/duration.py +++ b/qiskit/circuit/duration.py @@ -34,9 +34,11 @@ def duration_in_dt(duration_in_sec: float, dt_in_sec: float) -> int: res = round(duration_in_sec / dt_in_sec) rounding_error = abs(duration_in_sec - res * dt_in_sec) if rounding_error > 1e-15: - warnings.warn("Duration is rounded to %d [dt] = %e [s] from %e [s]" - % (res, res * dt_in_sec, duration_in_sec), - UserWarning) + warnings.warn( + "Duration is rounded to %d [dt] = %e [s] from %e [s]" + % (res, res * dt_in_sec, duration_in_sec), + UserWarning, + ) return res @@ -62,22 +64,22 @@ def convert_durations_to_dt(qc: QuantumCircuit, dt_in_sec: float, inplace=True): circ = qc.copy() for inst, _, _ in circ.data: - if inst.unit == 'dt' or inst.duration is None: + if inst.unit == "dt" or inst.duration is None: continue - if not inst.unit.endswith('s'): + if not inst.unit.endswith("s"): raise CircuitError("Invalid time unit: '{0}'".format(inst.unit)) duration = inst.duration - if inst.unit != 's': + if inst.unit != "s": duration = apply_prefix(duration, inst.unit) inst.duration = duration_in_dt(duration, dt_in_sec) - inst.unit = 'dt' + inst.unit = "dt" if circ.duration is not None: circ.duration = duration_in_dt(circ.duration, dt_in_sec) - circ.unit = 'dt' + circ.unit = "dt" if not inplace: return circ diff --git a/qiskit/circuit/equivalence.py b/qiskit/circuit/equivalence.py index 0233e70b102e..e68aadb17a1f 100644 --- a/qiskit/circuit/equivalence.py +++ b/qiskit/circuit/equivalence.py @@ -21,17 +21,14 @@ from .parameterexpression import ParameterExpression -Key = namedtuple('Key', ['name', - 'num_qubits']) +Key = namedtuple("Key", ["name", "num_qubits"]) -Entry = namedtuple('Entry', ['search_base', - 'equivalences']) +Entry = namedtuple("Entry", ["search_base", "equivalences"]) -Equivalence = namedtuple('Equivalence', ['params', # Ordered to match Gate.params - 'circuit']) +Equivalence = namedtuple("Equivalence", ["params", "circuit"]) # Ordered to match Gate.params -class EquivalenceLibrary(): +class EquivalenceLibrary: """A library providing a one-way mapping of Gates to their equivalent implementations as QuantumCircuits.""" @@ -64,11 +61,9 @@ def add_equivalence(self, gate, equivalent_circuit): _raise_if_shape_mismatch(gate, equivalent_circuit) _raise_if_param_mismatch(gate.params, equivalent_circuit.parameters) - key = Key(name=gate.name, - num_qubits=gate.num_qubits) + key = Key(name=gate.name, num_qubits=gate.num_qubits) - equiv = Equivalence(params=gate.params.copy(), - circuit=equivalent_circuit.copy()) + equiv = Equivalence(params=gate.params.copy(), circuit=equivalent_circuit.copy()) if key not in self._map: self._map[key] = Entry(search_base=True, equivalences=[]) @@ -86,11 +81,9 @@ def has_entry(self, gate): False otherwise. """ - key = Key(name=gate.name, - num_qubits=gate.num_qubits) + key = Key(name=gate.name, num_qubits=gate.num_qubits) - return (key in self._map or - (self._base.has_entry(gate) if self._base is not None else False)) + return key in self._map or (self._base.has_entry(gate) if self._base is not None else False) def set_entry(self, gate, entry): """Set the equivalence record for a Gate. Future queries for the Gate @@ -110,15 +103,11 @@ def set_entry(self, gate, entry): _raise_if_shape_mismatch(gate, equiv) _raise_if_param_mismatch(gate.params, equiv.parameters) - key = Key(name=gate.name, - num_qubits=gate.num_qubits) + key = Key(name=gate.name, num_qubits=gate.num_qubits) - equivs = [Equivalence(params=gate.params.copy(), - circuit=equiv.copy()) - for equiv in entry] + equivs = [Equivalence(params=gate.params.copy(), circuit=equiv.copy()) for equiv in entry] - self._map[key] = Entry(search_base=False, - equivalences=equivs) + self._map[key] = Entry(search_base=False, equivalences=equivs) def get_entry(self, gate): """Gets the set of QuantumCircuits circuits from the library which @@ -140,13 +129,11 @@ def get_entry(self, gate): consistent across Qiskit versions. """ - key = Key(name=gate.name, - num_qubits=gate.num_qubits) + key = Key(name=gate.name, num_qubits=gate.num_qubits) query_params = gate.params - return [_rebind_equiv(equiv, query_params) - for equiv in self._get_equivalences(key)] + return [_rebind_equiv(equiv, query_params) for equiv in self._get_equivalences(key)] def draw(self, filename=None): """Draws the equivalence relations available in the library. @@ -164,41 +151,49 @@ def draw(self, filename=None): """ try: import pydot + has_pydot = True except ImportError: has_pydot = False try: from PIL import Image + has_pil = True except ImportError: has_pil = False if not has_pydot: - raise ImportError('EquivalenceLibrary.draw requires pydot. ' - "You can use 'pip install pydot' to install") + raise ImportError( + "EquivalenceLibrary.draw requires pydot. " + "You can use 'pip install pydot' to install" + ) if not has_pil and not filename: - raise ImportError('EquivalenceLibrary.draw requires pillow. ' - "You can use 'pip install pillow' to install") + raise ImportError( + "EquivalenceLibrary.draw requires pillow. " + "You can use 'pip install pillow' to install" + ) try: from IPython.display import SVG + has_ipython = True except ImportError: has_ipython = False dot_str = self._build_basis_graph().to_dot( - lambda node: {'label': node['label']}, lambda edge: edge) + lambda node: {"label": node["label"]}, lambda edge: edge + ) dot = pydot.graph_from_dot_data(dot_str)[0] if filename: - extension = filename.split('.')[-1] + extension = filename.split(".")[-1] dot.write(filename, format=extension) return None if has_ipython: - svg = dot.create_svg(prog='dot') + svg = dot.create_svg(prog="dot") return SVG(svg) - png = dot.create_png(prog='dot') + png = dot.create_png(prog="dot") return Image.open(io.BytesIO(png)) def _build_basis_graph(self): @@ -209,26 +204,29 @@ def _build_basis_graph(self): name, num_qubits = key equivalences = self._get_equivalences(key) - basis = frozenset(['{}/{}'.format(name, num_qubits)]) + basis = frozenset(["{}/{}".format(name, num_qubits)]) for params, decomp in equivalences: - decomp_basis = frozenset('{}/{}'.format(name, num_qubits) - for name, num_qubits in - {(inst.name, inst.num_qubits) - for inst, _, __ in decomp.data}) + decomp_basis = frozenset( + "{}/{}".format(name, num_qubits) + for name, num_qubits in { + (inst.name, inst.num_qubits) for inst, _, __ in decomp.data + } + ) if basis not in node_map: - basis_node = graph.add_node({'basis': basis, - 'label': str(set(basis))}) + basis_node = graph.add_node({"basis": basis, "label": str(set(basis))}) node_map[basis] = basis_node if decomp_basis not in node_map: - decomp_basis_node = graph.add_node({'basis': decomp_basis, - 'label': str(set(decomp_basis))}) + decomp_basis_node = graph.add_node( + {"basis": decomp_basis, "label": str(set(decomp_basis))} + ) node_map[decomp_basis] = decomp_basis_node - label = "%s\n%s" % ( - str(params), str(decomp) if num_qubits <= 5 else '...') - graph.add_edge(node_map[basis], - node_map[decomp_basis], - dict(label=label, fontname='Courier', fontsize=str(8))) + label = "%s\n%s" % (str(params), str(decomp) if num_qubits <= 5 else "...") + graph.add_edge( + node_map[basis], + node_map[decomp_basis], + dict(label=label, fontname="Courier", fontsize=str(8)), + ) return graph @@ -237,10 +235,11 @@ def _get_all_keys(self): self_keys = set(self._map.keys()) - return self_keys | {base_key - for base_key in base_keys - if base_key not in self._map - or self._map[base_key].search_base} + return self_keys | { + base_key + for base_key in base_keys + if base_key not in self._map or self._map[base_key].search_base + } def _get_equivalences(self, key): search_base, equivalences = self._map.get(key, (True, [])) @@ -251,25 +250,25 @@ def _get_equivalences(self, key): def _raise_if_param_mismatch(gate_params, circuit_parameters): - gate_parameters = [p for p in gate_params - if isinstance(p, ParameterExpression)] + gate_parameters = [p for p in gate_params if isinstance(p, ParameterExpression)] if set(gate_parameters) != circuit_parameters: - raise CircuitError('Cannot add equivalence between circuit and gate ' - 'of different parameters. Gate params: {}. ' - 'Circuit params: {}.'.format( - gate_parameters, - circuit_parameters)) + raise CircuitError( + "Cannot add equivalence between circuit and gate " + "of different parameters. Gate params: {}. " + "Circuit params: {}.".format(gate_parameters, circuit_parameters) + ) def _raise_if_shape_mismatch(gate, circuit): - if (gate.num_qubits != circuit.num_qubits - or gate.num_clbits != circuit.num_clbits): - raise CircuitError('Cannot add equivalence between circuit and gate ' - 'of different shapes. Gate: {} qubits and {} clbits. ' - 'Circuit: {} qubits and {} clbits.'.format( - gate.num_qubits, gate.num_clbits, - circuit.num_qubits, circuit.num_clbits)) + if gate.num_qubits != circuit.num_qubits or gate.num_clbits != circuit.num_clbits: + raise CircuitError( + "Cannot add equivalence between circuit and gate " + "of different shapes. Gate: {} qubits and {} clbits. " + "Circuit: {} qubits and {} clbits.".format( + gate.num_qubits, gate.num_clbits, circuit.num_qubits, circuit.num_clbits + ) + ) def _rebind_equiv(equiv, query_params): diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py index 7915f0db5b09..e4619cffea10 100644 --- a/qiskit/circuit/gate.py +++ b/qiskit/circuit/gate.py @@ -25,8 +25,9 @@ class Gate(Instruction): """Unitary gate.""" - def __init__(self, name: str, num_qubits: int, params: List, - label: Optional[str] = None) -> None: + def __init__( + self, name: str, num_qubits: int, params: List, label: Optional[str] = None + ) -> None: """Create a new gate. Args: @@ -52,7 +53,7 @@ def to_matrix(self) -> np.ndarray: CircuitError: If a Gate subclass does not implement this method an exception will be raised when this base class method is called. """ - if hasattr(self, '__array__'): + if hasattr(self, "__array__"): # pylint: disable=no-member return self.__array__(dtype=complex) raise CircuitError("to_matrix not defined for this {}".format(type(self))) @@ -71,27 +72,29 @@ def power(self, exponent: float): """ from qiskit.quantum_info.operators import Operator # pylint: disable=cyclic-import from qiskit.extensions.unitary import UnitaryGate # pylint: disable=cyclic-import + # Should be diagonalized because it's a unitary. - decomposition, unitary = schur(Operator(self).data, output='complex') + decomposition, unitary = schur(Operator(self).data, output="complex") # Raise the diagonal entries to the specified power decomposition_power = list() decomposition_diagonal = decomposition.diagonal() # assert off-diagonal are 0 if not np.allclose(np.diag(decomposition_diagonal), decomposition): - raise CircuitError('The matrix is not diagonal') + raise CircuitError("The matrix is not diagonal") for element in decomposition_diagonal: decomposition_power.append(pow(element, exponent)) # Then reconstruct the resulting gate. unitary_power = unitary @ np.diag(decomposition_power) @ unitary.conj().T - return UnitaryGate(unitary_power, label='%s^%s' % (self.name, exponent)) + return UnitaryGate(unitary_power, label="%s^%s" % (self.name, exponent)) - def _return_repeat(self, exponent: float) -> 'Gate': - return Gate(name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, - params=self.params) + def _return_repeat(self, exponent: float) -> "Gate": + return Gate( + name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, params=self.params + ) - def assemble(self) -> 'Instruction': + def assemble(self) -> "Instruction": """Assemble a QasmQobjInstruction""" instruction = super().assemble() if self.label: @@ -116,10 +119,14 @@ def label(self, name: str): if isinstance(name, (str, type(None))): self._label = name else: - raise TypeError('label expects a string or None') - - def control(self, num_ctrl_qubits: Optional[int] = 1, label: Optional[str] = None, - ctrl_state: Optional[Union[int, str]] = None): + raise TypeError("label expects a string or None") + + def control( + self, + num_ctrl_qubits: Optional[int] = 1, + label: Optional[str] = None, + ctrl_state: Optional[Union[int, str]] = None, + ): """Return controlled version of gate. See :class:`.ControlledGate` for usage. Args: @@ -138,6 +145,7 @@ def control(self, num_ctrl_qubits: Optional[int] = 1, label: Optional[str] = Non """ # pylint: disable=cyclic-import from .add_control import add_control + return add_control(self, num_ctrl_qubits, label, ctrl_state) @staticmethod @@ -169,8 +177,9 @@ def _broadcast_2_arguments(qarg0: List, qarg1: List) -> List: for arg0 in qarg0: yield [arg0, qarg1[0]], [] else: - raise CircuitError('Not sure how to combine these two-qubit arguments:\n %s\n %s' % - (qarg0, qarg1)) + raise CircuitError( + "Not sure how to combine these two-qubit arguments:\n %s\n %s" % (qarg0, qarg1) + ) @staticmethod def _broadcast_3_or_more_args(qargs: List) -> List: @@ -178,8 +187,7 @@ def _broadcast_3_or_more_args(qargs: List) -> List: for arg in zip(*qargs): yield list(arg), [] else: - raise CircuitError( - 'Not sure how to combine these qubit arguments:\n %s\n' % qargs) + raise CircuitError("Not sure how to combine these qubit arguments:\n %s\n" % qargs) def broadcast_arguments(self, qargs: List, cargs: List) -> Tuple[List, List]: """Validation and handling of the arguments and its relationship. @@ -220,11 +228,12 @@ def broadcast_arguments(self, qargs: List, cargs: List) -> Tuple[List, List]: """ if len(qargs) != self.num_qubits or cargs: raise CircuitError( - f'The amount of qubit({len(qargs)})/clbit({len(cargs)}) arguments does' - f' not match the gate expectation ({self.num_qubits}).') + f"The amount of qubit({len(qargs)})/clbit({len(cargs)}) arguments does" + f" not match the gate expectation ({self.num_qubits})." + ) if any(not qarg for qarg in qargs): - raise CircuitError('One or more of the arguments are empty') + raise CircuitError("One or more of the arguments are empty") if len(qargs) == 1: return Gate._broadcast_single_argument(qargs[0]) @@ -233,7 +242,7 @@ def broadcast_arguments(self, qargs: List, cargs: List) -> Tuple[List, List]: elif len(qargs) >= 3: return Gate._broadcast_3_or_more_args(qargs) else: - raise CircuitError('This gate cannot handle %i arguments' % len(qargs)) + raise CircuitError("This gate cannot handle %i arguments" % len(qargs)) def validate_parameter(self, parameter): """Gate parameters should be int, float, or ParameterExpression""" @@ -241,19 +250,25 @@ def validate_parameter(self, parameter): if len(parameter.parameters) > 0: return parameter # expression has free parameters, we cannot validate it if not parameter._symbol_expr.is_real: - raise CircuitError("Bound parameter expression is complex in gate {}".format( - self.name)) + raise CircuitError( + "Bound parameter expression is complex in gate {}".format(self.name) + ) return parameter # per default assume parameters must be real when bound if isinstance(parameter, (int, float)): return parameter elif isinstance(parameter, (np.integer, np.floating)): return parameter.item() elif isinstance(parameter, np.ndarray): - warn("Gate param type %s is being deprecated as of 0.16.0, and will be removed " - "no earlier than 3 months after that release date. " - "Considering creating your own Gate subclass with the method validate_parameter " - " to allow this param type." % type(parameter), DeprecationWarning, 3) + warn( + "Gate param type %s is being deprecated as of 0.16.0, and will be removed " + "no earlier than 3 months after that release date. " + "Considering creating your own Gate subclass with the method validate_parameter " + " to allow this param type." % type(parameter), + DeprecationWarning, + 3, + ) return parameter else: - raise CircuitError("Invalid param type {0} for gate {1}.".format(type(parameter), - self.name)) + raise CircuitError( + "Invalid param type {0} for gate {1}.".format(type(parameter), self.name) + ) diff --git a/qiskit/circuit/instruction.py b/qiskit/circuit/instruction.py index 8029af0dcc72..1df38cfe4a56 100644 --- a/qiskit/circuit/instruction.py +++ b/qiskit/circuit/instruction.py @@ -43,7 +43,7 @@ from qiskit.circuit.parameter import ParameterExpression from .tools import pi_check -_CUTOFF_PRECISION = 1E-10 +_CUTOFF_PRECISION = 1e-10 class Instruction: @@ -53,7 +53,7 @@ class Instruction: # NOTE: Using this attribute may change in the future (See issue # 5811) _directive = False - def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit='dt'): + def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit="dt"): """Create a new instruction. Args: @@ -72,8 +72,8 @@ def __init__(self, name, num_qubits, num_clbits, params, duration=None, unit='dt raise CircuitError("num_qubits and num_clbits must be integer.") if num_qubits < 0 or num_clbits < 0: raise CircuitError( - "bad instruction dimensions: %d qubits, %d clbits." % - num_qubits, num_clbits) + "bad instruction dimensions: %d qubits, %d clbits." % num_qubits, num_clbits + ) self.name = name self.num_qubits = num_qubits self.num_clbits = num_clbits @@ -101,11 +101,13 @@ def __eq__(self, other): Returns: bool: are self and other equal. """ - if type(self) is not type(other) or \ - self.name != other.name or \ - self.num_qubits != other.num_qubits or \ - self.num_clbits != other.num_clbits or \ - self.definition != other.definition: + if ( + type(self) is not type(other) + or self.name != other.name + or self.num_qubits != other.num_qubits + or self.num_clbits != other.num_clbits + or self.definition != other.definition + ): return False for self_param, other_param in zip_longest(self.params, other.params): @@ -116,16 +118,17 @@ def __eq__(self, other): pass try: - if numpy.shape(self_param) == numpy.shape(other_param) \ - and numpy.allclose(self_param, other_param, - atol=_CUTOFF_PRECISION, rtol=0): + if numpy.shape(self_param) == numpy.shape(other_param) and numpy.allclose( + self_param, other_param, atol=_CUTOFF_PRECISION, rtol=0 + ): continue except TypeError: pass try: - if numpy.isclose(float(self_param), float(other_param), - atol=_CUTOFF_PRECISION, rtol=0): + if numpy.isclose( + float(self_param), float(other_param), atol=_CUTOFF_PRECISION, rtol=0 + ): continue except TypeError: pass @@ -134,7 +137,7 @@ def __eq__(self, other): return True - def soft_compare(self, other: 'Instruction') -> bool: + def soft_compare(self, other: "Instruction") -> bool: """ Soft comparison between gates. Their names, number of qubits, and classical bit numbers must match. The number of parameters must match. Each parameter @@ -147,20 +150,23 @@ def soft_compare(self, other: 'Instruction') -> bool: Returns: bool: are self and other equal up to parameter expressions. """ - if self.name != other.name or \ - other.num_qubits != other.num_qubits or \ - other.num_clbits != other.num_clbits or \ - len(self.params) != len(other.params): + if ( + self.name != other.name + or other.num_qubits != other.num_qubits + or other.num_clbits != other.num_clbits + or len(self.params) != len(other.params) + ): return False for self_param, other_param in zip_longest(self.params, other.params): - if isinstance(self_param, ParameterExpression) or \ - isinstance(other_param, ParameterExpression): + if isinstance(self_param, ParameterExpression) or isinstance( + other_param, ParameterExpression + ): continue - if isinstance(self_param, numpy.ndarray) and \ - isinstance(other_param, numpy.ndarray): - if numpy.shape(self_param) == numpy.shape(other_param) \ - and numpy.allclose(self_param, other_param, atol=_CUTOFF_PRECISION): + if isinstance(self_param, numpy.ndarray) and isinstance(other_param, numpy.ndarray): + if numpy.shape(self_param) == numpy.shape(other_param) and numpy.allclose( + self_param, other_param, atol=_CUTOFF_PRECISION + ): continue else: try: @@ -197,9 +203,9 @@ def validate_parameter(self, parameter): def is_parameterized(self): """Return True .IFF. instruction is parameterized else False""" - return any(isinstance(param, ParameterExpression) - and param.parameters - for param in self.params) + return any( + isinstance(param, ParameterExpression) and param.parameters for param in self.params + ) @property def definition(self): @@ -218,6 +224,7 @@ def decompositions(self): """Get the decompositions of the instruction from the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel + return sel.get_entry(self) @decompositions.setter @@ -225,12 +232,14 @@ def decompositions(self, decompositions): """Set the decompositions of the instruction from the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel + sel.set_entry(self, decompositions) def add_decomposition(self, decomposition): """Add a decomposition of the instruction to the SessionEquivalenceLibrary.""" # pylint: disable=cyclic-import from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel + sel.add_equivalence(self, decomposition) @property @@ -258,8 +267,7 @@ def assemble(self): instruction = QasmQobjInstruction(name=self.name) # Evaluate parameters if self.params: - params = [ - x.evalf(x) if hasattr(x, 'evalf') else x for x in self.params] + params = [x.evalf(x) if hasattr(x, "evalf") else x for x in self.params] instruction.params = params # Add placeholder for qarg and carg params if self.num_qubits: @@ -280,8 +288,11 @@ def mirror(self): qiskit.circuit.Instruction: a new instruction with sub-instructions reversed. """ - warnings.warn('instruction.mirror() is deprecated. Use circuit.reverse_ops()' - 'to reverse the order of gates.', DeprecationWarning) + warnings.warn( + "instruction.mirror() is deprecated. Use circuit.reverse_ops()" + "to reverse the order of gates.", + DeprecationWarning, + ) return self.reverse_ops() def reverse_ops(self): @@ -297,9 +308,10 @@ def reverse_ops(self): if not self._definition: return self.copy() - reverse_inst = self.copy(name=self.name + '_reverse') - reverse_inst.definition._data = [(inst.reverse_ops(), qargs, cargs) - for inst, qargs, cargs in reversed(self._definition)] + reverse_inst = self.copy(name=self.name + "_reverse") + reverse_inst.definition._data = [ + (inst.reverse_ops(), qargs, cargs) for inst, qargs, cargs in reversed(self._definition) + ] return reverse_inst @@ -323,21 +335,28 @@ def inverse(self): raise CircuitError("inverse() not implemented for %s." % self.name) from qiskit.circuit import QuantumCircuit, Gate # pylint: disable=cyclic-import + if self.num_clbits: - inverse_gate = Instruction(name=self.name + '_dg', - num_qubits=self.num_qubits, - num_clbits=self.num_clbits, - params=self.params.copy()) + inverse_gate = Instruction( + name=self.name + "_dg", + num_qubits=self.num_qubits, + num_clbits=self.num_clbits, + params=self.params.copy(), + ) else: - inverse_gate = Gate(name=self.name + '_dg', - num_qubits=self.num_qubits, - params=self.params.copy()) - - inverse_gate.definition = QuantumCircuit(*self.definition.qregs, *self.definition.cregs, - global_phase=-self.definition.global_phase) - inverse_gate.definition._data = [(inst.inverse(), qargs, cargs) - for inst, qargs, cargs in reversed(self._definition)] + inverse_gate = Gate( + name=self.name + "_dg", num_qubits=self.num_qubits, params=self.params.copy() + ) + + inverse_gate.definition = QuantumCircuit( + *self.definition.qregs, + *self.definition.cregs, + global_phase=-self.definition.global_phase, + ) + inverse_gate.definition._data = [ + (inst.inverse(), qargs, cargs) for inst, qargs, cargs in reversed(self._definition) + ] return inverse_gate @@ -389,8 +408,10 @@ def qasm(self): """ name_param = self.name if self.params: - name_param = "%s(%s)" % (name_param, ",".join( - [pi_check(i, ndigits=8, output='qasm') for i in self.params])) + name_param = "%s(%s)" % ( + name_param, + ",".join([pi_check(i, ndigits=8, output="qasm") for i in self.params]), + ) return self._qasmif(name_param) @@ -411,8 +432,9 @@ def broadcast_arguments(self, qargs, cargs): """ if len(qargs) != self.num_qubits: raise CircuitError( - f'The amount of qubit arguments {len(qargs)} does not match' - f' the instruction expectation ({self.num_qubits}).') + f"The amount of qubit arguments {len(qargs)} does not match" + f" the instruction expectation ({self.num_qubits})." + ) # [[q[0], q[1]], [c[0], c[1]]] -> [q[0], c[0]], [q[1], c[1]] flat_qargs = [qarg for sublist in qargs for qarg in sublist] @@ -420,8 +442,12 @@ def broadcast_arguments(self, qargs, cargs): yield flat_qargs, flat_cargs def _return_repeat(self, exponent): - return Instruction(name="%s*%s" % (self.name, exponent), num_qubits=self.num_qubits, - num_clbits=self.num_clbits, params=self.params) + return Instruction( + name="%s*%s" % (self.name, exponent), + num_qubits=self.num_qubits, + num_clbits=self.num_clbits, + params=self.params, + ) def repeat(self, n): """Creates an instruction with `gate` repeated `n` amount of times. @@ -441,12 +467,13 @@ def repeat(self, n): n = int(n) instruction = self._return_repeat(n) - qargs = [] if self.num_qubits == 0 else QuantumRegister(self.num_qubits, 'q') - cargs = [] if self.num_clbits == 0 else ClassicalRegister(self.num_clbits, 'c') + qargs = [] if self.num_qubits == 0 else QuantumRegister(self.num_qubits, "q") + cargs = [] if self.num_clbits == 0 else ClassicalRegister(self.num_clbits, "c") if instruction.definition is None: # pylint: disable=cyclic-import from qiskit import QuantumCircuit + qc = QuantumCircuit() if qargs: qc.add_register(qargs) diff --git a/qiskit/circuit/instructionset.py b/qiskit/circuit/instructionset.py index c32e237963cd..84828712cdd2 100644 --- a/qiskit/circuit/instructionset.py +++ b/qiskit/circuit/instructionset.py @@ -41,8 +41,7 @@ def __getitem__(self, i): def add(self, gate, qargs, cargs): """Add an instruction and its context (where it is attached).""" if not isinstance(gate, Instruction): - raise CircuitError("attempt to add non-Instruction" + - " to InstructionSet") + raise CircuitError("attempt to add non-Instruction" + " to InstructionSet") self.instructions.append(gate) self.qargs.append(qargs) self.cargs.append(cargs) diff --git a/qiskit/circuit/library/__init__.py b/qiskit/circuit/library/__init__.py index 775bf9fdf67f..e17edf057746 100644 --- a/qiskit/circuit/library/__init__.py +++ b/qiskit/circuit/library/__init__.py @@ -335,7 +335,7 @@ GRX, GRY, GRZ, - RVGate + RVGate, ) from .boolean_logic import ( AND, @@ -363,17 +363,13 @@ RealAmplitudes, EfficientSU2, ExcitationPreserving, - QAOAAnsatz -) -from .data_preparation import ( - PauliFeatureMap, - ZFeatureMap, - ZZFeatureMap + QAOAAnsatz, ) +from .data_preparation import PauliFeatureMap, ZFeatureMap, ZZFeatureMap from .probability_distributions import ( LogNormalDistribution, NormalDistribution, - UniformDistribution + UniformDistribution, ) from .quantum_volume import QuantumVolume from .fourier_checking import FourierChecking diff --git a/qiskit/circuit/library/arithmetic/exact_reciprocal.py b/qiskit/circuit/library/arithmetic/exact_reciprocal.py index 7b71a5960592..176c7daeed08 100644 --- a/qiskit/circuit/library/arithmetic/exact_reciprocal.py +++ b/qiskit/circuit/library/arithmetic/exact_reciprocal.py @@ -25,7 +25,7 @@ class ExactReciprocal(QuantumCircuit): |x\rangle |0\rangle \mapsto \cos(1/x)|x\rangle|0\rangle + \sin(1/x)|x\rangle |1\rangle """ - def __init__(self, num_state_qubits: int, scaling: float, name: str = '1/x') -> None: + def __init__(self, num_state_qubits: int, scaling: float, name: str = "1/x") -> None: r""" Args: num_state_qubits: The number of qubits representing the value to invert. @@ -36,8 +36,8 @@ def __init__(self, num_state_qubits: int, scaling: float, name: str = '1/x') -> It is assumed that the binary string x represents a number < 1. """ - qr_state = QuantumRegister(num_state_qubits, 'state') - qr_flag = QuantumRegister(1, 'flag') + qr_state = QuantumRegister(num_state_qubits, "state") + qr_flag = QuantumRegister(1, "flag") super().__init__(qr_state, qr_flag, name=name) angles = [0.0] diff --git a/qiskit/circuit/library/arithmetic/functional_pauli_rotations.py b/qiskit/circuit/library/arithmetic/functional_pauli_rotations.py index eeb6bd3351dc..29409a431ca7 100644 --- a/qiskit/circuit/library/arithmetic/functional_pauli_rotations.py +++ b/qiskit/circuit/library/arithmetic/functional_pauli_rotations.py @@ -22,10 +22,9 @@ class FunctionalPauliRotations(BlueprintCircuit, ABC): """Base class for functional Pauli rotations.""" - def __init__(self, - num_state_qubits: Optional[int] = None, - basis: str = 'Y', - name: str = 'F') -> None: + def __init__( + self, num_state_qubits: Optional[int] = None, basis: str = "Y", name: str = "F" + ) -> None: r"""Create a new functional Pauli rotation circuit. Args: @@ -66,8 +65,8 @@ def basis(self, basis: str) -> None: """ basis = basis.lower() if self._basis is None or basis != self._basis: - if basis not in ['x', 'y', 'z']: - raise ValueError('The provided basis must be X, Y or Z, not {}'.format(basis)) + if basis not in ["x", "y", "z"]: + raise ValueError("The provided basis must be X, Y or Z, not {}".format(basis)) self._invalidate() self._basis = basis diff --git a/qiskit/circuit/library/arithmetic/integer_comparator.py b/qiskit/circuit/library/arithmetic/integer_comparator.py index ab1f2cf5e3e1..cdbabb9cbb8b 100644 --- a/qiskit/circuit/library/arithmetic/integer_comparator.py +++ b/qiskit/circuit/library/arithmetic/integer_comparator.py @@ -39,10 +39,13 @@ class IntegerComparator(BlueprintCircuit): (the results bit) is 1, the :math:`\geq` condition is ``True`` otherwise it is ``False``. """ - def __init__(self, num_state_qubits: Optional[int] = None, - value: Optional[int] = None, - geq: bool = True, - name: str = 'cmp') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + value: Optional[int] = None, + geq: bool = True, + name: str = "cmp", + ) -> None: """Create a new fixed value comparator circuit. Args: @@ -101,9 +104,11 @@ def geq(self, geq: bool) -> None: @property def num_ancilla_qubits(self): """Deprecated. Use num_ancillas instead.""" - warnings.warn('The IntegerComparator.num_ancilla_qubits property is deprecated ' - 'as of 0.16.0. It will be removed no earlier than 3 months after the release ' - 'date. You should use the num_ancillas property instead.') + warnings.warn( + "The IntegerComparator.num_ancilla_qubits property is deprecated " + "as of 0.16.0. It will be removed no earlier than 3 months after the release " + "date. You should use the num_ancillas property instead." + ) return self.num_ancillas @property @@ -130,8 +135,8 @@ def num_state_qubits(self, num_state_qubits: Optional[int]) -> None: if num_state_qubits is not None: # set the new qubit registers - qr_state = QuantumRegister(num_state_qubits, name='state') - q_compare = QuantumRegister(1, name='compare') + qr_state = QuantumRegister(num_state_qubits, name="state") + q_compare = QuantumRegister(1, name="compare") self.qregs = [qr_state, q_compare] @@ -148,9 +153,10 @@ def _get_twos_complement(self) -> List[int]: The 2's complement of ``self.value``. """ twos_complement = pow(2, self.num_state_qubits) - int(np.ceil(self.value)) - twos_complement = '{:b}'.format(twos_complement).rjust(self.num_state_qubits, '0') - twos_complement = \ - [1 if twos_complement[i] == '1' else 0 for i in reversed(range(len(twos_complement)))] + twos_complement = "{:b}".format(twos_complement).rjust(self.num_state_qubits, "0") + twos_complement = [ + 1 if twos_complement[i] == "1" else 0 for i in reversed(range(len(twos_complement))) + ] return twos_complement def _check_configuration(self, raise_on_failure: bool = True) -> bool: @@ -160,18 +166,18 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self._num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('Number of state qubits is not set.') + raise AttributeError("Number of state qubits is not set.") if self._value is None: valid = False if raise_on_failure: - raise AttributeError('No comparison value set.') + raise AttributeError("No comparison value set.") required_num_qubits = 2 * self.num_state_qubits if self.num_qubits != required_num_qubits: valid = False if raise_on_failure: - raise CircuitError('Number of qubits does not match required number of qubits.') + raise CircuitError("Number of qubits does not match required number of qubits.") return valid @@ -179,9 +185,9 @@ def _build(self) -> None: """Build the comparator circuit.""" super()._build() - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] q_compare = self.qubits[self.num_state_qubits] - qr_ancilla = self.qubits[self.num_state_qubits + 1:] + qr_ancilla = self.qubits[self.num_state_qubits + 1 :] if self.value <= 0: # condition always satisfied for non-positive values if self._geq: # otherwise the condition is never satisfied @@ -197,16 +203,18 @@ def _build(self) -> None: self.cx(qr_state[i], qr_ancilla[i]) elif i < self.num_state_qubits - 1: if twos[i] == 1: - self.compose(OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], - inplace=True) + self.compose( + OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], inplace=True + ) else: self.ccx(qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]) else: if twos[i] == 1: # OR needs the result argument as qubit not register, thus # access the index [0] - self.compose(OR(2), [qr_state[i], qr_ancilla[i - 1], q_compare], - inplace=True) + self.compose( + OR(2), [qr_state[i], qr_ancilla[i - 1], q_compare], inplace=True + ) else: self.ccx(qr_state[i], qr_ancilla[i - 1], q_compare) @@ -215,14 +223,15 @@ def _build(self) -> None: self.x(q_compare) # uncompute ancillas state - for i in reversed(range(self.num_state_qubits-1)): + for i in reversed(range(self.num_state_qubits - 1)): if i == 0: if twos[i] == 1: self.cx(qr_state[i], qr_ancilla[i]) else: if twos[i] == 1: - self.compose(OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], - inplace=True) + self.compose( + OR(2), [qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]], inplace=True + ) else: self.ccx(qr_state[i], qr_ancilla[i - 1], qr_ancilla[i]) else: diff --git a/qiskit/circuit/library/arithmetic/linear_amplitude_function.py b/qiskit/circuit/library/arithmetic/linear_amplitude_function.py index 0974723bb562..220b182c36d6 100644 --- a/qiskit/circuit/library/arithmetic/linear_amplitude_function.py +++ b/qiskit/circuit/library/arithmetic/linear_amplitude_function.py @@ -77,16 +77,17 @@ class LinearAmplitudeFunction(QuantumCircuit): `arXiv:2005.10780 `_ """ - def __init__(self, - num_state_qubits: int, - slope: Union[float, List[float]], - offset: Union[float, List[float]], - domain: Tuple[float, float], - image: Tuple[float, float], - rescaling_factor: float = 1, - breakpoints: Optional[List[float]] = None, - name: str = 'F', - ) -> None: + def __init__( + self, + num_state_qubits: int, + slope: Union[float, List[float]], + offset: Union[float, List[float]], + domain: Tuple[float, float], + image: Tuple[float, float], + rescaling_factor: float = 1, + breakpoints: Optional[List[float]] = None, + name: str = "F", + ) -> None: r""" Args: num_state_qubits: The number of qubits used to encode the variable :math:`x`. @@ -102,9 +103,9 @@ def __init__(self, is not piecewise. name: Name of the circuit. """ - if not hasattr(slope, '__len__'): + if not hasattr(slope, "__len__"): slope = [slope] - if not hasattr(offset, '__len__'): + if not hasattr(offset, "__len__"): offset = [offset] # ensure that the breakpoints include the first point of the domain @@ -129,13 +130,13 @@ def __init__(self, mapped_slope = [] mapped_offset = [] for i, point in enumerate(breakpoints): - mapped_breakpoint = (point - a) / (b - a) * (2**num_state_qubits - 1) + mapped_breakpoint = (point - a) / (b - a) * (2 ** num_state_qubits - 1) mapped_breakpoints += [mapped_breakpoint] # factor (upper - lower) / (2^n - 1) is for the scaling of x to [l,u] # note that the +l for mapping to [l,u] is already included in # the offsets given as parameters - mapped_slope += [slope[i] * (b - a) / (2**num_state_qubits - 1)] + mapped_slope += [slope[i] * (b - a) / (2 ** num_state_qubits - 1)] mapped_offset += [offset[i]] # approximate linear behavior by scaling and contracting around pi/4 @@ -147,10 +148,7 @@ def __init__(self, # use PWLPauliRotations to implement the function pwl_pauli_rotation = PiecewiseLinearPauliRotations( - num_state_qubits, - mapped_breakpoints, - 2 * slope_angles, - 2 * offset_angles + num_state_qubits, mapped_breakpoints, 2 * slope_angles, 2 * offset_angles ) super().__init__(*pwl_pauli_rotation.qregs, name=name) @@ -185,17 +183,18 @@ def _check_sorted_and_in_range(breakpoints, domain): # check if sorted if not np.all(np.diff(breakpoints) > 0): - raise ValueError('Breakpoints must be unique and sorted.') + raise ValueError("Breakpoints must be unique and sorted.") if breakpoints[0] < domain[0] or breakpoints[-1] > domain[1]: - raise ValueError('Breakpoints must be included in domain.') + raise ValueError("Breakpoints must be included in domain.") def _check_sizes_match(slope, offset, breakpoints): size = len(slope) if len(offset) != size: - raise ValueError('Size mismatch of slope ({}) and offset ({}).'.format(size, len(offset))) + raise ValueError("Size mismatch of slope ({}) and offset ({}).".format(size, len(offset))) if breakpoints is not None: if len(breakpoints) != size: - raise ValueError('Size mismatch of slope ({}) and breakpoints ({}).'.format( - size, len(breakpoints))) + raise ValueError( + "Size mismatch of slope ({}) and breakpoints ({}).".format(size, len(breakpoints)) + ) diff --git a/qiskit/circuit/library/arithmetic/linear_pauli_rotations.py b/qiskit/circuit/library/arithmetic/linear_pauli_rotations.py index f055a123957b..d5a6542da695 100644 --- a/qiskit/circuit/library/arithmetic/linear_pauli_rotations.py +++ b/qiskit/circuit/library/arithmetic/linear_pauli_rotations.py @@ -49,12 +49,14 @@ class LinearPauliRotations(FunctionalPauliRotations): linear functions. """ - def __init__(self, - num_state_qubits: Optional[int] = None, - slope: float = 1, - offset: float = 0, - basis: str = 'Y', - name: str = 'LinRot') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + slope: float = 1, + offset: float = 0, + basis: str = "Y", + name: str = "LinRot", + ) -> None: r"""Create a new linear rotation circuit. Args: @@ -131,8 +133,8 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None: """ if num_state_qubits: # set new register of appropriate size - qr_state = QuantumRegister(num_state_qubits, name='state') - qr_target = QuantumRegister(1, name='target') + qr_state = QuantumRegister(num_state_qubits, name="state") + qr_target = QuantumRegister(1, name="target") self.qregs = [qr_state, qr_target] else: self.qregs = [] @@ -143,13 +145,15 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") if self.num_qubits < self.num_state_qubits + 1: valid = False if raise_on_failure: - raise CircuitError('Not enough qubits in the circuit, need at least ' - '{}.'.format(self.num_state_qubits + 1)) + raise CircuitError( + "Not enough qubits in the circuit, need at least " + "{}.".format(self.num_state_qubits + 1) + ) return valid @@ -158,20 +162,20 @@ def _build(self): super()._build() # build the circuit - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] qr_target = self.qubits[self.num_state_qubits] - if self.basis == 'x': + if self.basis == "x": self.rx(self.offset, qr_target) - elif self.basis == 'y': + elif self.basis == "y": self.ry(self.offset, qr_target) else: # 'Z': self.rz(self.offset, qr_target) for i, q_i in enumerate(qr_state): - if self.basis == 'x': + if self.basis == "x": self.crx(self.slope * pow(2, i), q_i, qr_target) - elif self.basis == 'y': + elif self.basis == "y": self.cry(self.slope * pow(2, i), q_i, qr_target) else: # 'Z' self.crz(self.slope * pow(2, i), q_i, qr_target) diff --git a/qiskit/circuit/library/arithmetic/piecewise_chebyshev.py b/qiskit/circuit/library/arithmetic/piecewise_chebyshev.py index fda2a2cca49c..0fd70d23d46b 100644 --- a/qiskit/circuit/library/arithmetic/piecewise_chebyshev.py +++ b/qiskit/circuit/library/arithmetic/piecewise_chebyshev.py @@ -54,12 +54,14 @@ class PiecewiseChebyshev(BlueprintCircuit): `arXiv:1805.12445 `_ """ - def __init__(self, - f_x: Callable[[int], float], - degree: Optional[int] = None, - breakpoints: Optional[List[int]] = None, - num_state_qubits: Optional[int] = None, - name: str = 'pw_cheb') -> None: + def __init__( + self, + f_x: Callable[[int], float], + degree: Optional[int] = None, + breakpoints: Optional[List[int]] = None, + num_state_qubits: Optional[int] = None, + name: str = "pw_cheb", + ) -> None: r""" Args: f_x: the function to be approximated. @@ -90,28 +92,30 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self._f_x is None: valid = False if raise_on_failure: - raise AttributeError('The function to be approximated has not been set.') + raise AttributeError("The function to be approximated has not been set.") if self._degree is None: valid = False if raise_on_failure: - raise AttributeError('The degree of the polynomials has not been set.') + raise AttributeError("The degree of the polynomials has not been set.") if self._breakpoints is None: valid = False if raise_on_failure: - raise AttributeError('The breakpoints have not been set.') + raise AttributeError("The breakpoints have not been set.") if self.num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") if self.num_qubits < self.num_state_qubits + 1: valid = False if raise_on_failure: - raise CircuitError('Not enough qubits in the circuit, need at least ' - '{}.'.format(self.num_state_qubits + 1)) + raise CircuitError( + "Not enough qubits in the circuit, need at least " + "{}.".format(self.num_state_qubits + 1) + ) return valid @@ -119,8 +123,8 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: def f_x(self) -> Callable[[int], float]: """The function to be approximated. - Returns: - The function to be approximated. + Returns: + The function to be approximated. """ return self._f_x @@ -144,8 +148,8 @@ def f_x(self, f_x: Optional[Callable[[int], float]]) -> None: def degree(self) -> int: """The degree of the polynomials. - Returns: - The degree of the polynomials. + Returns: + The degree of the polynomials. """ return self._degree @@ -169,8 +173,8 @@ def degree(self, degree: Optional[int]) -> None: def breakpoints(self) -> List[int]: """The breakpoints for the piecewise approximation. - Returns: - The breakpoints for the piecewise approximation. + Returns: + The breakpoints for the piecewise approximation. """ breakpoints = self._breakpoints @@ -208,8 +212,8 @@ def breakpoints(self, breakpoints: Optional[List[int]]) -> None: def polynomials(self) -> List[List[float]]: """The polynomials for the piecewise approximation. - Returns: - The polynomials for the piecewise approximation. + Returns: + The polynomials for the piecewise approximation. """ if self.num_state_qubits is None: return [[]] @@ -223,9 +227,9 @@ def polynomials(self) -> List[List[float]]: polynomials = [] for i in range(0, num_intervals - 1): # Calculate the polynomial approximating the function on the current interval - poly = Chebyshev.interpolate(self._f_x, self._degree, - domain=[self._breakpoints[i], - self._breakpoints[i + 1]]) + poly = Chebyshev.interpolate( + self._f_x, self._degree, domain=[self._breakpoints[i], self._breakpoints[i + 1]] + ) # Convert polynomial to the standard basis and rescale it for the rotation gates poly = 2 * poly.convert(kind=np.polynomial.Polynomial).coef # Convert to list and append @@ -288,8 +292,8 @@ def num_state_qubits(self, num_state_qubits: Optional[int]) -> None: def _reset_registers(self, num_state_qubits: Optional[int]) -> None: if num_state_qubits is not None: - qr_state = QuantumRegister(num_state_qubits, 'state') - qr_target = QuantumRegister(1, 'target') + qr_state = QuantumRegister(num_state_qubits, "state") + qr_target = QuantumRegister(1, "target") self.qregs = [qr_state, qr_target] self._ancillas = [] self._qubits = qr_state[:] + qr_target[:] @@ -307,8 +311,7 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None: self._ancillas = [] def _build(self): - """Build the circuit. The operation is considered successful when q_objective is :math:`|1>` - """ + """Build the circuit. The operation is considered successful when q_objective is :math:`|1>`""" # do not build the circuit if _data is already populated if self._data is not None: return @@ -318,12 +321,13 @@ def _build(self): # check whether the configuration is valid self._check_configuration() - poly_r = PiecewisePolynomialPauliRotations(self.num_state_qubits, - self.breakpoints, self.polynomials) + poly_r = PiecewisePolynomialPauliRotations( + self.num_state_qubits, self.breakpoints, self.polynomials + ) - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] qr_target = [self.qubits[self.num_state_qubits]] - qr_ancillas = self.qubits[self.num_state_qubits + 1:] + qr_ancillas = self.qubits[self.num_state_qubits + 1 :] # Apply polynomial approximation self.append(poly_r.to_instruction(), qr_state[:] + qr_target + qr_ancillas[:]) diff --git a/qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py b/qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py index 412e87934c76..837d8508c7cd 100644 --- a/qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py +++ b/qiskit/circuit/library/arithmetic/piecewise_linear_pauli_rotations.py @@ -45,13 +45,15 @@ class PiecewiseLinearPauliRotations(FunctionalPauliRotations): where we implicitly assume :math:`x_{J+1} = 2^n`. """ - def __init__(self, - num_state_qubits: Optional[int] = None, - breakpoints: Optional[List[int]] = None, - slopes: Optional[List[float]] = None, - offsets: Optional[List[float]] = None, - basis: str = 'Y', - name: str = 'pw_lin') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + breakpoints: Optional[List[int]] = None, + slopes: Optional[List[float]] = None, + offsets: Optional[List[float]] = None, + basis: str = "Y", + name: str = "pw_lin", + ) -> None: """Construct piecewise-linearly-controlled Pauli rotations. Args: @@ -75,10 +77,13 @@ def __init__(self, @property def num_ancilla_qubits(self): """Deprecated. Use num_ancillas instead.""" - warnings.warn('The PiecewiseLinearPauliRotations.num_ancilla_qubits property is deprecated ' - 'as of 0.16.0. It will be removed no earlier than 3 months after the release ' - 'date. You should use the num_ancillas property instead.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "The PiecewiseLinearPauliRotations.num_ancilla_qubits property is deprecated " + "as of 0.16.0. It will be removed no earlier than 3 months after the release " + "date. You should use the num_ancillas property instead.", + DeprecationWarning, + stacklevel=2, + ) return self.num_ancillas @property @@ -162,9 +167,9 @@ def mapped_offsets(self) -> List[float]: The mapped offsets. """ mapped_offsets = np.zeros_like(self.offsets) - for i, (offset, slope, point) in enumerate(zip(self.offsets, - self.slopes, - self.breakpoints)): + for i, (offset, slope, point) in enumerate( + zip(self.offsets, self.slopes, self.breakpoints) + ): mapped_offsets[i] = offset - slope * point - sum(mapped_offsets[:i]) return mapped_offsets @@ -190,8 +195,9 @@ def evaluate(self, x: float) -> float: y = (x >= self.breakpoints[0]) * (x * self.mapped_slopes[0] + self.mapped_offsets[0]) for i in range(1, len(self.breakpoints)): - y = y + (x >= self.breakpoints[i]) * (x * self.mapped_slopes[i] + - self.mapped_offsets[i]) + y = y + (x >= self.breakpoints[i]) * ( + x * self.mapped_slopes[i] + self.mapped_offsets[i] + ) return y @@ -201,18 +207,20 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") if self.num_qubits < self.num_state_qubits + 1: valid = False if raise_on_failure: - raise CircuitError('Not enough qubits in the circuit, need at least ' - '{}.'.format(self.num_state_qubits + 1)) + raise CircuitError( + "Not enough qubits in the circuit, need at least " + "{}.".format(self.num_state_qubits + 1) + ) if len(self.breakpoints) != len(self.slopes) or len(self.breakpoints) != len(self.offsets): valid = False if raise_on_failure: - raise ValueError('Mismatching sizes of breakpoints, slopes and offsets.') + raise ValueError("Mismatching sizes of breakpoints, slopes and offsets.") return valid @@ -235,7 +243,7 @@ def _reset_registers(self, num_state_qubits: Optional[int]) -> None: def _build(self): super()._build() - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] qr_target = [self.qubits[self.num_state_qubits]] qr_ancilla = self.ancillas @@ -243,10 +251,12 @@ def _build(self): for i, point in enumerate(self.breakpoints): if i == 0 and self.contains_zero_breakpoint: # apply rotation - lin_r = LinearPauliRotations(num_state_qubits=self.num_state_qubits, - slope=self.mapped_slopes[i], - offset=self.mapped_offsets[i], - basis=self.basis) + lin_r = LinearPauliRotations( + num_state_qubits=self.num_state_qubits, + slope=self.mapped_slopes[i], + offset=self.mapped_offsets[i], + basis=self.basis, + ) self.append(lin_r.to_gate(), qr_state[:] + qr_target) else: @@ -257,15 +267,16 @@ def _build(self): comp = IntegerComparator(num_state_qubits=self.num_state_qubits, value=point) qr = qr_state[:] + qr_compare[:] # add ancilla as compare qubit - self.append(comp.to_gate(), qr[:] + qr_helper[:comp.num_ancillas]) + self.append(comp.to_gate(), qr[:] + qr_helper[: comp.num_ancillas]) # apply controlled rotation - lin_r = LinearPauliRotations(num_state_qubits=self.num_state_qubits, - slope=self.mapped_slopes[i], - offset=self.mapped_offsets[i], - basis=self.basis) - self.append(lin_r.to_gate().control(), - qr_compare[:] + qr_state[:] + qr_target) + lin_r = LinearPauliRotations( + num_state_qubits=self.num_state_qubits, + slope=self.mapped_slopes[i], + offset=self.mapped_offsets[i], + basis=self.basis, + ) + self.append(lin_r.to_gate().control(), qr_compare[:] + qr_state[:] + qr_target) # uncompute comparator - self.append(comp.to_gate().inverse(), qr[:] + qr_helper[:comp.num_ancillas]) + self.append(comp.to_gate().inverse(), qr[:] + qr_helper[: comp.num_ancillas]) diff --git a/qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py b/qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py index 5ad349669862..da13d4657a8b 100644 --- a/qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py +++ b/qiskit/circuit/library/arithmetic/piecewise_polynomial_pauli_rotations.py @@ -87,12 +87,14 @@ class PiecewisePolynomialPauliRotations(FunctionalPauliRotations): `arXiv:2009.04484 `_ """ - def __init__(self, - num_state_qubits: Optional[int] = None, - breakpoints: Optional[List[int]] = None, - coeffs: Optional[List[List[float]]] = None, - basis: str = 'Y', - name: str = 'pw_poly') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + breakpoints: Optional[List[int]] = None, + coeffs: Optional[List[List[float]]] = None, + basis: str = "Y", + name: str = "pw_poly", + ) -> None: """ Args: num_state_qubits: The number of qubits representing the state. @@ -127,8 +129,11 @@ def breakpoints(self) -> List[int]: Returns: The list of breakpoints. """ - if self.num_state_qubits is not None and len(self._breakpoints) == len(self.coeffs) and\ - self._breakpoints[-1] < 2 ** self.num_state_qubits: + if ( + self.num_state_qubits is not None + and len(self._breakpoints) == len(self.coeffs) + and self._breakpoints[-1] < 2 ** self.num_state_qubits + ): return self._breakpoints + [2 ** self.num_state_qubits] return self._breakpoints @@ -188,8 +193,8 @@ def mapped_coeffs(self) -> List[List[float]]: mapped_coeffs.append(self._hom_coeffs[0]) for i in range(1, len(self._hom_coeffs)): mapped_coeffs.append([]) - for j in range(0, self._degree+1): - mapped_coeffs[i].append(self._hom_coeffs[i][j] - self._hom_coeffs[i-1][j]) + for j in range(0, self._degree + 1): + mapped_coeffs[i].append(self._hom_coeffs[i][j] - self._hom_coeffs[i - 1][j]) return mapped_coeffs @@ -224,18 +229,20 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") if self.num_qubits < self.num_state_qubits + 1: valid = False if raise_on_failure: - raise CircuitError('Not enough qubits in the circuit, need at least ' - '{}.'.format(self.num_state_qubits + 1)) + raise CircuitError( + "Not enough qubits in the circuit, need at least " + "{}.".format(self.num_state_qubits + 1) + ) if len(self.breakpoints) != len(self.coeffs) + 1: valid = False if raise_on_failure: - raise ValueError('Mismatching number of breakpoints and polynomials.') + raise ValueError("Mismatching number of breakpoints and polynomials.") return valid @@ -274,18 +281,20 @@ def _build(self): # check whether the configuration is valid self._check_configuration() - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] qr_target = [self.qubits[self.num_state_qubits]] # Ancilla for the comparator circuit - qr_ancilla = self.qubits[self.num_state_qubits + 1:] + qr_ancilla = self.qubits[self.num_state_qubits + 1 :] # apply comparators and controlled linear rotations for i, point in enumerate(self.breakpoints[:-1]): if i == 0 and self.contains_zero_breakpoint: # apply rotation - poly_r = PolynomialPauliRotations(num_state_qubits=self.num_state_qubits, - coeffs=self.mapped_coeffs[i], - basis=self.basis) + poly_r = PolynomialPauliRotations( + num_state_qubits=self.num_state_qubits, + coeffs=self.mapped_coeffs[i], + basis=self.basis, + ) self.append(poly_r.to_gate(), qr_state[:] + qr_target) else: @@ -294,16 +303,20 @@ def _build(self): qr_state_full = qr_state[:] + [qr_ancilla[0]] # add compare qubit qr_remaining_ancilla = qr_ancilla[1:] # take remaining ancillas - self.append(comp.to_gate(), - qr_state_full[:] + qr_remaining_ancilla[:comp.num_ancillas]) + self.append( + comp.to_gate(), qr_state_full[:] + qr_remaining_ancilla[: comp.num_ancillas] + ) # apply controlled rotation - poly_r = PolynomialPauliRotations(num_state_qubits=self.num_state_qubits, - coeffs=self.mapped_coeffs[i], - basis=self.basis) - self.append(poly_r.to_gate().control(), - [qr_ancilla[0]] + qr_state[:] + qr_target) + poly_r = PolynomialPauliRotations( + num_state_qubits=self.num_state_qubits, + coeffs=self.mapped_coeffs[i], + basis=self.basis, + ) + self.append(poly_r.to_gate().control(), [qr_ancilla[0]] + qr_state[:] + qr_target) # uncompute comparator - self.append(comp.to_gate().inverse(), - qr_state_full[:] + qr_remaining_ancilla[:comp.num_ancillas]) + self.append( + comp.to_gate().inverse(), + qr_state_full[:] + qr_remaining_ancilla[: comp.num_ancillas], + ) diff --git a/qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py b/qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py index 6f40be7c4968..7465fc601eea 100644 --- a/qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py +++ b/qiskit/circuit/library/arithmetic/polynomial_pauli_rotations.py @@ -26,7 +26,7 @@ def _binomial_coefficients(n): - """"Return a dictionary of binomial coefficients + """ "Return a dictionary of binomial coefficients Based-on/forked from sympy's binomial_coefficients() function [#] @@ -42,16 +42,16 @@ def _binomial_coefficients(n): def _large_coefficients_iter(m, n): - """"Return an iterator of multinomial coefficients + """ "Return an iterator of multinomial coefficients Based-on/forked from sympy's multinomial_coefficients_iterator() function [#] .. [#] https://github.com/sympy/sympy/blob/sympy-1.5.1/sympy/ntheory/multinomial.py """ - if m < 2*n or n == 1: + if m < 2 * n or n == 1: coefficients = _multinomial_coefficients(m, n) for key, value in coefficients.items(): - yield(key, value) + yield (key, value) else: coefficients = _multinomial_coefficients(n, n) coefficients_dict = {} @@ -88,7 +88,7 @@ def _large_coefficients_iter(m, n): def _multinomial_coefficients(m, n): - """"Return an iterator of multinomial coefficients + """ "Return an iterator of multinomial coefficients Based-on/forked from sympy's multinomial_coefficients() function [#] @@ -100,7 +100,7 @@ def _multinomial_coefficients(m, n): return {(): 1} if m == 2: return _binomial_coefficients(n) - if m >= 2*n and n > 1: + if m >= 2 * n and n > 1: return dict(_large_coefficients_iter(m, n)) if n: j = 0 @@ -159,11 +159,14 @@ class PolynomialPauliRotations(FunctionalPauliRotations): where :math:`c` are the input coefficients, ``coeffs``. """ - def __init__(self, num_state_qubits: Optional[int] = None, - coeffs: Optional[List[float]] = None, - basis: str = 'Y', - reverse: bool = False, - name: str = 'poly') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + coeffs: Optional[List[float]] = None, + basis: str = "Y", + reverse: bool = False, + name: str = "poly", + ) -> None: """Prepare an approximation to a state with amplitudes specified by a polynomial. Args: @@ -179,9 +182,11 @@ def __init__(self, num_state_qubits: Optional[int] = None, self._coeffs = coeffs or [0, 1] self._reverse = reverse if self._reverse is True: - warnings.warn('The reverse flag has been deprecated. ' - 'Use circuit.reverse_bits() to reverse order of qubits.', - DeprecationWarning) + warnings.warn( + "The reverse flag has been deprecated. " + "Use circuit.reverse_bits() to reverse order of qubits.", + DeprecationWarning, + ) # initialize super (after setting coeffs) super().__init__(num_state_qubits=num_state_qubits, basis=basis, name=name) @@ -231,17 +236,20 @@ def reverse(self) -> bool: @property def num_ancilla_qubits(self): """Deprecated. Use num_ancillas instead.""" - warnings.warn('The PolynomialPauliRotations.num_ancilla_qubits property is deprecated ' - 'as of 0.16.0. It will be removed no earlier than 3 months after the release ' - 'date. You should use the num_ancillas property instead.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "The PolynomialPauliRotations.num_ancilla_qubits property is deprecated " + "as of 0.16.0. It will be removed no earlier than 3 months after the release " + "date. You should use the num_ancillas property instead.", + DeprecationWarning, + stacklevel=2, + ) return self.num_ancillas def _reset_registers(self, num_state_qubits): if num_state_qubits is not None: # set new register of appropriate size - qr_state = QuantumRegister(num_state_qubits, name='state') - qr_target = QuantumRegister(1, name='target') + qr_state = QuantumRegister(num_state_qubits, name="state") + qr_target = QuantumRegister(1, name="target") self.qregs = [qr_state, qr_target] else: @@ -253,13 +261,15 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") if self.num_qubits < self.num_state_qubits + 1: valid = False if raise_on_failure: - raise CircuitError('Not enough qubits in the circuit, need at least ' - '{}.'.format(self.num_state_qubits + 1)) + raise CircuitError( + "Not enough qubits in the circuit, need at least " + "{}.".format(self.num_state_qubits + 1) + ) return valid @@ -309,14 +319,14 @@ def _build(self): # check whether the configuration is valid self._check_configuration() - qr_state = self.qubits[:self.num_state_qubits] + qr_state = self.qubits[: self.num_state_qubits] qr_target = self.qubits[self.num_state_qubits] rotation_coeffs = self._get_rotation_coefficients() - if self.basis == 'x': + if self.basis == "x": self.rx(self.coeffs[0], qr_target) - elif self.basis == 'y': + elif self.basis == "y": self.ry(self.coeffs[0], qr_target) else: self.rz(self.coeffs[0], qr_target) @@ -334,17 +344,17 @@ def _build(self): # apply controlled rotations if len(qr_control) > 1: - if self.basis == 'x': + if self.basis == "x": self.mcrx(rotation_coeffs[c], qr_control, qr_target) - elif self.basis == 'y': + elif self.basis == "y": self.mcry(rotation_coeffs[c], qr_control, qr_target) else: self.mcrz(rotation_coeffs[c], qr_control, qr_target) elif len(qr_control) == 1: - if self.basis == 'x': + if self.basis == "x": self.crx(rotation_coeffs[c], qr_control[0], qr_target) - elif self.basis == 'y': + elif self.basis == "y": self.cry(rotation_coeffs[c], qr_control[0], qr_target) else: self.crz(rotation_coeffs[c], qr_control[0], qr_target) diff --git a/qiskit/circuit/library/arithmetic/quadratic_form.py b/qiskit/circuit/library/arithmetic/quadratic_form.py index 5cef9eb34e11..02691ac87c93 100644 --- a/qiskit/circuit/library/arithmetic/quadratic_form.py +++ b/qiskit/circuit/library/arithmetic/quadratic_form.py @@ -58,14 +58,16 @@ class QuadraticForm(QuantumCircuit): """ - def __init__(self, - num_result_qubits: Optional[int] = None, - quadratic: Optional[Union[np.ndarray, - List[List[Union[float, ParameterExpression]]]]] = None, - linear: Optional[Union[np.ndarray, - List[Union[float, ParameterExpression]]]] = None, - offset: Optional[Union[float, ParameterExpression]] = None, - little_endian: bool = True) -> None: + def __init__( + self, + num_result_qubits: Optional[int] = None, + quadratic: Optional[ + Union[np.ndarray, List[List[Union[float, ParameterExpression]]]] + ] = None, + linear: Optional[Union[np.ndarray, List[Union[float, ParameterExpression]]]] = None, + offset: Optional[Union[float, ParameterExpression]] = None, + little_endian: bool = True, + ) -> None: r""" Args: num_result_qubits: The number of qubits to encode the result. Called :math:`m` in @@ -83,7 +85,7 @@ def __init__(self, # check inputs match if quadratic is not None and linear is not None: if len(quadratic) != len(linear): - raise ValueError('Mismatching sizes of quadratic and linear.') + raise ValueError("Mismatching sizes of quadratic and linear.") # temporarily set quadratic and linear to [] instead of None so we can iterate over them if quadratic is None: @@ -101,18 +103,19 @@ def __init__(self, if num_result_qubits is None: # check no value is parameterized if ( - any(any(isinstance(q_ij, ParameterExpression) for q_ij in q_i) - for q_i in quadratic) - or any(isinstance(l_i, ParameterExpression) for l_i in linear) - or isinstance(offset, ParameterExpression) + any(any(isinstance(q_ij, ParameterExpression) for q_ij in q_i) for q_i in quadratic) + or any(isinstance(l_i, ParameterExpression) for l_i in linear) + or isinstance(offset, ParameterExpression) ): - raise ValueError('If the number of result qubits is not specified, the quadratic ' - 'form matrices/vectors/offset may not be parameterized.') + raise ValueError( + "If the number of result qubits is not specified, the quadratic " + "form matrices/vectors/offset may not be parameterized." + ) num_result_qubits = self.required_result_qubits(quadratic, linear, offset) qr_input = QuantumRegister(num_input_qubits) qr_result = QuantumRegister(num_result_qubits) - super().__init__(qr_input, qr_result, name='Q(x)') + super().__init__(qr_input, qr_result, name="Q(x)") # set quadratic and linear again to None if they were None if len(quadratic) == 0: @@ -157,9 +160,11 @@ def __init__(self, self.append(iqft, qr_result) @staticmethod - def required_result_qubits(quadratic: Union[np.ndarray, List[List[float]]], - linear: Union[np.ndarray, List[float]], - offset: float) -> int: + def required_result_qubits( + quadratic: Union[np.ndarray, List[List[float]]], + linear: Union[np.ndarray, List[float]], + offset: float, + ) -> int: """Get the number of required result qubits. Args: diff --git a/qiskit/circuit/library/arithmetic/weighted_adder.py b/qiskit/circuit/library/arithmetic/weighted_adder.py index b73268c14b9f..6aa8bce57f86 100644 --- a/qiskit/circuit/library/arithmetic/weighted_adder.py +++ b/qiskit/circuit/library/arithmetic/weighted_adder.py @@ -71,10 +71,12 @@ class WeightedAdder(BlueprintCircuit): └────────┘ """ - def __init__(self, - num_state_qubits: Optional[int] = None, - weights: Optional[List[int]] = None, - name: str = 'adder') -> None: + def __init__( + self, + num_state_qubits: Optional[int] = None, + weights: Optional[List[int]] = None, + name: str = "adder", + ) -> None: """Computes the weighted sum controlled by state qubits. Args: @@ -128,7 +130,7 @@ def weights(self, weights: List[int]) -> None: if weights: for i, weight in enumerate(weights): if not np.isclose(weight, np.round(weight)): - raise ValueError('Non-integer weights are not supported!') + raise ValueError("Non-integer weights are not supported!") weights[i] = np.round(weight) self._invalidate() @@ -158,18 +160,18 @@ def num_state_qubits(self, num_state_qubits: int) -> None: def _reset_registers(self): if self.num_state_qubits: - qr_state = QuantumRegister(self.num_state_qubits, name='state') - qr_sum = QuantumRegister(self.num_sum_qubits, name='sum') + qr_state = QuantumRegister(self.num_state_qubits, name="state") + qr_sum = QuantumRegister(self.num_sum_qubits, name="sum") self.qregs = [qr_state, qr_sum] self._ancillas = [] if self.num_carry_qubits > 0: - qr_carry = AncillaRegister(self.num_carry_qubits, name='carry') + qr_carry = AncillaRegister(self.num_carry_qubits, name="carry") self.qregs += [qr_carry] self._ancillas += qr_carry[:] if self.num_control_qubits > 0: - qr_control = AncillaRegister(self.num_control_qubits, name='control') + qr_control = AncillaRegister(self.num_control_qubits, name="control") self.qregs += [qr_control] self._ancillas += qr_control[:] @@ -204,10 +206,13 @@ def num_control_qubits(self) -> int: @property def num_ancilla_qubits(self) -> int: """Deprecated. Use num_ancillas instead.""" - warnings.warn('The WeightedAdder.num_ancilla_qubits property is deprecated ' - 'as of 0.17.0. It will be removed no earlier than 3 months after the release ' - 'date. You should use the num_ancillas property instead.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "The WeightedAdder.num_ancilla_qubits property is deprecated " + "as of 0.17.0. It will be removed no earlier than 3 months after the release " + "date. You should use the num_ancillas property instead.", + DeprecationWarning, + stacklevel=2, + ) return self.num_control_qubits + self.num_carry_qubits # return self.num_ancillas @@ -216,12 +221,12 @@ def _check_configuration(self, raise_on_failure=True): if self._num_state_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of state qubits has not been set.') + raise AttributeError("The number of state qubits has not been set.") if self._num_state_qubits != len(self.weights): valid = False if raise_on_failure: - raise ValueError('Mismatching number of state qubits and weights.') + raise ValueError("Mismatching number of state qubits and weights.") return valid @@ -230,10 +235,10 @@ def _build(self): num_result_qubits = self.num_state_qubits + self.num_sum_qubits - qr_state = self.qubits[:self.num_state_qubits] - qr_sum = self.qubits[self.num_state_qubits:num_result_qubits] - qr_carry = self.qubits[num_result_qubits:num_result_qubits + self.num_carry_qubits] - qr_control = self.qubits[num_result_qubits + self.num_carry_qubits:] + qr_state = self.qubits[: self.num_state_qubits] + qr_sum = self.qubits[self.num_state_qubits : num_result_qubits] + qr_carry = self.qubits[num_result_qubits : num_result_qubits + self.num_carry_qubits] + qr_control = self.qubits[num_result_qubits + self.num_carry_qubits :] # loop over state qubits and corresponding weights for i, weight in enumerate(self.weights): @@ -245,11 +250,11 @@ def _build(self): q_state = qr_state[i] # get bit representation of current weight - weight_binary = '{:b}'.format(int(weight)).rjust(self.num_sum_qubits, '0')[::-1] + weight_binary = "{:b}".format(int(weight)).rjust(self.num_sum_qubits, "0")[::-1] # loop over bits of current weight and add them to sum and carry registers for j, bit in enumerate(weight_binary): - if bit == '1': + if bit == "1": if self.num_sum_qubits == 1: self.cx(q_state, qr_sum[j]) elif j == 0: @@ -268,8 +273,12 @@ def _build(self): # - controlled by q_state[i] self.x(qr_sum[j]) self.x(qr_carry[j - 1]) - self.mct([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control, - mode='v-chain') + self.mct( + [q_state, qr_sum[j], qr_carry[j - 1]], + qr_carry[j], + qr_control, + mode="v-chain", + ) self.cx(q_state, qr_carry[j]) self.x(qr_sum[j]) self.x(qr_carry[j - 1]) @@ -280,7 +289,7 @@ def _build(self): pass # nothing to do, since nothing to add elif j == 0: pass # nothing to do, since nothing to add - elif j == self.num_sum_qubits-1: + elif j == self.num_sum_qubits - 1: # compute (q_sum[j] + q_carry[j-1]) into (q_sum[j]) # - controlled by q_state[i] / last qubit, # no carry needed by construction @@ -288,14 +297,18 @@ def _build(self): else: # compute (q_sum[j] + q_carry[j-1]) into (q_sum[j], q_carry[j]) # - controlled by q_state[i] - self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control, - mode='v-chain') + self.mcx( + [q_state, qr_sum[j], qr_carry[j - 1]], + qr_carry[j], + qr_control, + mode="v-chain", + ) self.ccx(q_state, qr_carry[j - 1], qr_sum[j]) # uncompute carry qubits for j in reversed(range(len(weight_binary))): bit = weight_binary[j] - if bit == '1': + if bit == "1": if self.num_sum_qubits == 1: pass elif j == 0: @@ -306,8 +319,12 @@ def _build(self): pass else: self.x(qr_carry[j - 1]) - self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control, - mode='v-chain') + self.mcx( + [q_state, qr_sum[j], qr_carry[j - 1]], + qr_carry[j], + qr_control, + mode="v-chain", + ) self.cx(q_state, qr_carry[j]) self.x(qr_carry[j - 1]) else: @@ -321,6 +338,10 @@ def _build(self): # compute (q_sum[j] + q_carry[j-1]) into (q_sum[j], q_carry[j]) # - controlled by q_state[i] self.x(qr_sum[j]) - self.mcx([q_state, qr_sum[j], qr_carry[j - 1]], qr_carry[j], qr_control, - mode='v-chain') + self.mcx( + [q_state, qr_sum[j], qr_carry[j - 1]], + qr_carry[j], + qr_control, + mode="v-chain", + ) self.x(qr_sum[j]) diff --git a/qiskit/circuit/library/basis_change/qft.py b/qiskit/circuit/library/basis_change/qft.py index 1f616332ec48..46a690210509 100644 --- a/qiskit/circuit/library/basis_change/qft.py +++ b/qiskit/circuit/library/basis_change/qft.py @@ -75,13 +75,15 @@ class QFT(BlueprintCircuit): """ - def __init__(self, - num_qubits: Optional[int] = None, - approximation_degree: int = 0, - do_swaps: bool = True, - inverse: bool = False, - insert_barriers: bool = False, - name: str = 'qft') -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + approximation_degree: int = 0, + do_swaps: bool = True, + inverse: bool = False, + insert_barriers: bool = False, + name: str = "qft", + ) -> None: """Construct a new QFT circuit. Args: @@ -126,7 +128,7 @@ def num_qubits(self, num_qubits: int) -> None: self._invalidate() if num_qubits: - self.qregs = [QuantumRegister(num_qubits, name='q')] + self.qregs = [QuantumRegister(num_qubits, name="q")] else: self.qregs = [] @@ -150,7 +152,7 @@ def approximation_degree(self, approximation_degree: int) -> None: ValueError: If the approximation degree is smaller than 0. """ if approximation_degree < 0: - raise ValueError('Approximation degree cannot be smaller than 0.') + raise ValueError("Approximation degree cannot be smaller than 0.") if approximation_degree != self._approximation_degree: self._invalidate() @@ -208,22 +210,23 @@ def _invalidate(self) -> None: """Invalidate the current build of the circuit.""" self._data = None - def inverse(self) -> 'QFT': + def inverse(self) -> "QFT": """Invert this circuit. Returns: The inverted circuit. """ - if self.name in ('qft', 'iqft'): - name = 'qft' if self._inverse else 'iqft' + if self.name in ("qft", "iqft"): + name = "qft" if self._inverse else "iqft" else: - name = self.name + '_dg' + name = self.name + "_dg" inverted = self.copy(name=name) inverted._data = [] from qiskit.circuit.parametertable import ParameterTable + inverted._parameter_table = ParameterTable() for inst, qargs, cargs in reversed(self._data): @@ -242,7 +245,7 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_qubits is None: valid = False if raise_on_failure: - raise AttributeError('The number of qubits has not been set.') + raise AttributeError("The number of qubits has not been set.") return valid @@ -252,7 +255,9 @@ def _build(self) -> None: for j in reversed(range(self.num_qubits)): self.h(j) - num_entanglements = max(0, j-max(0, self.approximation_degree - (self.num_qubits-j-1))) + num_entanglements = max( + 0, j - max(0, self.approximation_degree - (self.num_qubits - j - 1)) + ) for k in reversed(range(j - num_entanglements, j)): lam = np.pi / (2 ** (j - k)) self.cp(lam, j, k) diff --git a/qiskit/circuit/library/boolean_logic/quantum_and.py b/qiskit/circuit/library/boolean_logic/quantum_and.py index 7a9de44607e2..7557296ee42e 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_and.py +++ b/qiskit/circuit/library/boolean_logic/quantum_and.py @@ -52,8 +52,12 @@ class AND(QuantumCircuit): """ - def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, - mcx_mode: str = 'noancilla') -> None: + def __init__( + self, + num_variable_qubits: int, + flags: Optional[List[int]] = None, + mcx_mode: str = "noancilla", + ) -> None: """Create a new logical AND circuit. Args: @@ -67,10 +71,10 @@ def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, self.flags = flags # add registers - qr_variable = QuantumRegister(num_variable_qubits, name='variable') - qr_result = QuantumRegister(1, name='result') + qr_variable = QuantumRegister(num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") - super().__init__(qr_variable, qr_result, name='and') + super().__init__(qr_variable, qr_result, name="and") # determine the control qubits: all that have a nonzero flag flags = flags or [1] * num_variable_qubits @@ -82,7 +86,7 @@ def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, # determine the number of ancillas self.num_ancilla_qubits = MCXGate.get_num_ancilla_qubits(len(control_qubits), mode=mcx_mode) if self.num_ancilla_qubits > 0: - qr_ancilla = QuantumRegister(self.num_ancilla_qubits, 'ancilla') + qr_ancilla = QuantumRegister(self.num_ancilla_qubits, "ancilla") self.add_register(qr_ancilla) else: qr_ancilla = [] diff --git a/qiskit/circuit/library/boolean_logic/quantum_or.py b/qiskit/circuit/library/boolean_logic/quantum_or.py index 0864affb7958..fc4abd8d2d70 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_or.py +++ b/qiskit/circuit/library/boolean_logic/quantum_or.py @@ -52,8 +52,12 @@ class OR(QuantumCircuit): """ - def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, - mcx_mode: str = 'noancilla') -> None: + def __init__( + self, + num_variable_qubits: int, + flags: Optional[List[int]] = None, + mcx_mode: str = "noancilla", + ) -> None: """Create a new logical OR circuit. Args: @@ -67,10 +71,10 @@ def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, self.flags = flags # add registers - qr_variable = QuantumRegister(num_variable_qubits, name='variable') - qr_result = QuantumRegister(1, name='result') + qr_variable = QuantumRegister(num_variable_qubits, name="variable") + qr_result = QuantumRegister(1, name="result") - super().__init__(qr_variable, qr_result, name='or') + super().__init__(qr_variable, qr_result, name="or") # determine the control qubits: all that have a nonzero flag flags = flags or [1] * num_variable_qubits @@ -82,7 +86,7 @@ def __init__(self, num_variable_qubits: int, flags: Optional[List[int]] = None, # determine the number of ancillas self.num_ancilla_qubits = MCXGate.get_num_ancilla_qubits(len(control_qubits), mode=mcx_mode) if self.num_ancilla_qubits > 0: - qr_ancilla = QuantumRegister(self.num_ancilla_qubits, 'ancilla') + qr_ancilla = QuantumRegister(self.num_ancilla_qubits, "ancilla") self.add_register(qr_ancilla) else: qr_ancilla = [] diff --git a/qiskit/circuit/library/boolean_logic/quantum_xor.py b/qiskit/circuit/library/boolean_logic/quantum_xor.py index 8f85b40b89ca..605548d98379 100644 --- a/qiskit/circuit/library/boolean_logic/quantum_xor.py +++ b/qiskit/circuit/library/boolean_logic/quantum_xor.py @@ -28,11 +28,12 @@ class XOR(QuantumCircuit): This circuit can also represent addition by ``amount`` over the finite field GF(2). """ - def __init__(self, - num_qubits: int, - amount: Optional[int] = None, - seed: Optional[int] = None, - ) -> None: + def __init__( + self, + num_qubits: int, + amount: Optional[int] = None, + seed: Optional[int] = None, + ) -> None: """Return a circuit implementing bitwise xor. Args: @@ -59,7 +60,7 @@ def __init__(self, raise CircuitError("Bits in 'amount' exceed circuit width") else: rng = np.random.default_rng(seed) - amount = rng.integers(0, 2**num_qubits) + amount = rng.integers(0, 2 ** num_qubits) for i in range(num_qubits): bit = amount & 1 diff --git a/qiskit/circuit/library/data_preparation/__init__.py b/qiskit/circuit/library/data_preparation/__init__.py index 03bc802a996b..e387d10d2664 100644 --- a/qiskit/circuit/library/data_preparation/__init__.py +++ b/qiskit/circuit/library/data_preparation/__init__.py @@ -43,7 +43,7 @@ from .zz_feature_map import ZZFeatureMap __all__ = [ - 'PauliFeatureMap', - 'ZFeatureMap', - 'ZZFeatureMap', + "PauliFeatureMap", + "ZFeatureMap", + "ZZFeatureMap", ] diff --git a/qiskit/circuit/library/data_preparation/pauli_feature_map.py b/qiskit/circuit/library/data_preparation/pauli_feature_map.py index b2be66ec8ea8..e9a89736c0dd 100644 --- a/qiskit/circuit/library/data_preparation/pauli_feature_map.py +++ b/qiskit/circuit/library/data_preparation/pauli_feature_map.py @@ -103,16 +103,17 @@ class PauliFeatureMap(NLocal): `arXiv:1804.11326 `_ """ - def __init__(self, - feature_dimension: Optional[int] = None, - reps: int = 2, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - alpha: float = 2.0, - paulis: Optional[List[str]] = None, - data_map_func: Optional[Callable[[np.ndarray], float]] = None, - parameter_prefix: str = 'x', - insert_barriers: bool = False, - ) -> None: + def __init__( + self, + feature_dimension: Optional[int] = None, + reps: int = 2, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + alpha: float = 2.0, + paulis: Optional[List[str]] = None, + data_map_func: Optional[Callable[[np.ndarray], float]] = None, + parameter_prefix: str = "x", + insert_barriers: bool = False, + ) -> None: """Create a new Pauli expansion circuit. Args: @@ -131,20 +132,23 @@ def __init__(self, """ - super().__init__(num_qubits=feature_dimension, - reps=reps, - rotation_blocks=HGate(), - entanglement=entanglement, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers, - skip_final_rotation_layer=True) + super().__init__( + num_qubits=feature_dimension, + reps=reps, + rotation_blocks=HGate(), + entanglement=entanglement, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers, + skip_final_rotation_layer=True, + ) self._data_map_func = data_map_func or self_product - self._paulis = paulis or ['Z', 'ZZ'] + self._paulis = paulis or ["Z", "ZZ"] self._alpha = alpha - def _parameter_generator(self, rep: int, block: int, indices: List[int] - ) -> Optional[List[Parameter]]: + def _parameter_generator( + self, rep: int, block: int, indices: List[int] + ) -> Optional[List[Parameter]]: """If certain blocks should use certain parameters this method can be overriden.""" params = [self.ordered_parameters[i] for i in indices] return params @@ -219,13 +223,13 @@ def feature_dimension(self, feature_dimension: int) -> None: self.num_qubits = feature_dimension def _extract_data_for_rotation(self, pauli, x): - where_non_i = np.where(np.asarray(list(pauli[::-1])) != 'I')[0] + where_non_i = np.where(np.asarray(list(pauli[::-1])) != "I")[0] x = np.asarray(x) return x[where_non_i] def pauli_block(self, pauli_string): """Get the Pauli block for the feature map circuit.""" - params = ParameterVector('_', length=len(pauli_string)) + params = ParameterVector("_", length=len(pauli_string)) time = self._data_map_func(np.asarray(params)) return self.pauli_evolution(pauli_string, time) @@ -238,7 +242,7 @@ def pauli_evolution(self, pauli_string, time): trimmed = [] indices = [] for i, pauli in enumerate(pauli_string): - if pauli != 'I': + if pauli != "I": trimmed += [pauli] indices += [i] @@ -249,9 +253,9 @@ def pauli_evolution(self, pauli_string, time): def basis_change(circuit, inverse=False): for i, pauli in enumerate(pauli_string): - if pauli == 'X': + if pauli == "X": circuit.h(i) - elif pauli == 'Y': + elif pauli == "Y": circuit.rx(-np.pi / 2 if inverse else np.pi / 2, i) def cx_chain(circuit, inverse=False): diff --git a/qiskit/circuit/library/data_preparation/z_feature_map.py b/qiskit/circuit/library/data_preparation/z_feature_map.py index 6044a2a31419..07bedfd98ee6 100644 --- a/qiskit/circuit/library/data_preparation/z_feature_map.py +++ b/qiskit/circuit/library/data_preparation/z_feature_map.py @@ -72,12 +72,13 @@ class ZFeatureMap(PauliFeatureMap): """ - def __init__(self, - feature_dimension: int, - reps: int = 2, - data_map_func: Optional[Callable[[np.ndarray], float]] = None, - insert_barriers: bool = False, - ) -> None: + def __init__( + self, + feature_dimension: int, + reps: int = 2, + data_map_func: Optional[Callable[[np.ndarray], float]] = None, + insert_barriers: bool = False, + ) -> None: """Create a new first-order Pauli-Z expansion circuit. Args: @@ -89,8 +90,10 @@ def __init__(self, and hadamard layers. """ - super().__init__(feature_dimension=feature_dimension, - paulis=['Z'], - reps=reps, - data_map_func=data_map_func, - insert_barriers=insert_barriers) + super().__init__( + feature_dimension=feature_dimension, + paulis=["Z"], + reps=reps, + data_map_func=data_map_func, + insert_barriers=insert_barriers, + ) diff --git a/qiskit/circuit/library/data_preparation/zz_feature_map.py b/qiskit/circuit/library/data_preparation/zz_feature_map.py index 32f4bbb399c8..4d5586a64cdb 100644 --- a/qiskit/circuit/library/data_preparation/zz_feature_map.py +++ b/qiskit/circuit/library/data_preparation/zz_feature_map.py @@ -57,13 +57,14 @@ class ZZFeatureMap(PauliFeatureMap): OrderedDict([('u1', 12), ('cx', 12), ('ry', 12), ('cz', 9), ('h', 6)]) """ - def __init__(self, - feature_dimension: int, - reps: int = 2, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - data_map_func: Optional[Callable[[np.ndarray], float]] = None, - insert_barriers: bool = False, - ) -> None: + def __init__( + self, + feature_dimension: int, + reps: int = 2, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + data_map_func: Optional[Callable[[np.ndarray], float]] = None, + insert_barriers: bool = False, + ) -> None: """Create a new second-order Pauli-Z expansion. Args: @@ -79,12 +80,16 @@ def __init__(self, ValueError: If the feature dimension is smaller than 2. """ if feature_dimension < 2: - raise ValueError('The ZZFeatureMap contains 2-local interactions and cannot be ' - f'defined for less than 2 qubits. You provided {feature_dimension}.') + raise ValueError( + "The ZZFeatureMap contains 2-local interactions and cannot be " + f"defined for less than 2 qubits. You provided {feature_dimension}." + ) - super().__init__(feature_dimension=feature_dimension, - reps=reps, - entanglement=entanglement, - paulis=['Z', 'ZZ'], - data_map_func=data_map_func, - insert_barriers=insert_barriers) + super().__init__( + feature_dimension=feature_dimension, + reps=reps, + entanglement=entanglement, + paulis=["Z", "ZZ"], + data_map_func=data_map_func, + insert_barriers=insert_barriers, + ) diff --git a/qiskit/circuit/library/fourier_checking.py b/qiskit/circuit/library/fourier_checking.py index 2d49e8adc409..ac2028b5cfc5 100644 --- a/qiskit/circuit/library/fourier_checking.py +++ b/qiskit/circuit/library/fourier_checking.py @@ -52,9 +52,7 @@ class FourierChecking(QuantumCircuit): `arXiv:1411.5729 `_ """ - def __init__(self, - f: List[int], - g: List[int]) -> None: + def __init__(self, f: List[int], g: List[int]) -> None: """Create Fourier checking circuit. Args: @@ -78,9 +76,11 @@ def __init__(self, num_qubits = math.log2(len(f)) if len(f) != len(g) or num_qubits == 0 or not num_qubits.is_integer(): - raise CircuitError("The functions f and g must be given as truth " - "tables, each as a list of 2**n entries of " - "{1, -1}.") + raise CircuitError( + "The functions f and g must be given as truth " + "tables, each as a list of 2**n entries of " + "{1, -1}." + ) super().__init__(num_qubits, name="fc: %s, %s" % (f, g)) diff --git a/qiskit/circuit/library/generalized_gates/diagonal.py b/qiskit/circuit/library/generalized_gates/diagonal.py index a5691df87725..b611cacc29a8 100644 --- a/qiskit/circuit/library/generalized_gates/diagonal.py +++ b/qiskit/circuit/library/generalized_gates/diagonal.py @@ -73,8 +73,7 @@ class Diagonal(QuantumCircuit): `arXiv:0406176 `_ """ - def __init__(self, - diag: Union[List, np.array]) -> None: + def __init__(self, diag: Union[List, np.array]) -> None: """Create a new Diagonal circuit. Args: @@ -105,7 +104,7 @@ def __init__(self, diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) angles_rz.append(rz_angle) num_act_qubits = int(np.log2(n)) - ctrl_qubits = list(range(num_qubits-num_act_qubits+1, num_qubits)) + ctrl_qubits = list(range(num_qubits - num_act_qubits + 1, num_qubits)) target_qubit = num_qubits - num_act_qubits self.ucrz(angles_rz, ctrl_qubits, target_qubit) n //= 2 diff --git a/qiskit/circuit/library/generalized_gates/gms.py b/qiskit/circuit/library/generalized_gates/gms.py index 7a375cd78d50..d1653c872ffb 100644 --- a/qiskit/circuit/library/generalized_gates/gms.py +++ b/qiskit/circuit/library/generalized_gates/gms.py @@ -75,9 +75,7 @@ class GMS(QuantumCircuit): `arXiv:1707.06356 `_ """ - def __init__(self, - num_qubits: int, - theta: Union[List[List[float]], np.ndarray]) -> None: + def __init__(self, num_qubits: int, theta: Union[List[List[float]], np.ndarray]) -> None: """Create a new Global Mølmer–Sørensen (GMS) gate. Args: @@ -88,7 +86,7 @@ def __init__(self, """ super().__init__(num_qubits, name="gms") if not isinstance(theta, list): - theta = [theta] * int((num_qubits**2 - 1) / 2) + theta = [theta] * int((num_qubits ** 2 - 1) / 2) gms = QuantumCircuit(num_qubits, name="gms") for i in range(self.num_qubits): for j in range(i + 1, self.num_qubits): @@ -111,11 +109,11 @@ class MSGate(Gate): def __init__(self, num_qubits, theta, label=None): """Create new MS gate.""" - super().__init__('ms', num_qubits, [theta], label=label) + super().__init__("ms", num_qubits, [theta], label=label) def _define(self): theta = self.params[0] - q = QuantumRegister(self.num_qubits, 'q') + q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) for i in range(self.num_qubits): for j in range(i + 1, self.num_qubits): diff --git a/qiskit/circuit/library/generalized_gates/gr.py b/qiskit/circuit/library/generalized_gates/gr.py index f375ab5fba91..df4b9c990532 100644 --- a/qiskit/circuit/library/generalized_gates/gr.py +++ b/qiskit/circuit/library/generalized_gates/gr.py @@ -55,10 +55,7 @@ class GR(QuantumCircuit): """ - def __init__(self, - num_qubits: int, - theta: float, - phi: float) -> None: + def __init__(self, num_qubits: int, theta: float, phi: float) -> None: """Create a new Global R (GR) gate. Args: @@ -109,9 +106,7 @@ class GRX(GR): """ - def __init__(self, - num_qubits: int, - theta: float) -> None: + def __init__(self, num_qubits: int, theta: float) -> None: """Create a new Global RX (GRX) gate. Args: @@ -160,16 +155,14 @@ class GRY(GR): """ - def __init__(self, - num_qubits: int, - theta: float) -> None: + def __init__(self, num_qubits: int, theta: float) -> None: """Create a new Global RY (GRY) gate. Args: num_qubits: number of qubits. theta: rotation angle about y-axis """ - super().__init__(num_qubits, theta, phi=np.pi/2) + super().__init__(num_qubits, theta, phi=np.pi / 2) class GRZ(QuantumCircuit): @@ -211,9 +204,7 @@ class GRZ(QuantumCircuit): """ - def __init__(self, - num_qubits: int, - phi: float) -> None: + def __init__(self, num_qubits: int, phi: float) -> None: """Create a new Global RZ (GRZ) gate. Args: diff --git a/qiskit/circuit/library/generalized_gates/mcmt.py b/qiskit/circuit/library/generalized_gates/mcmt.py index d8feac683bd1..7bc944cd9d38 100644 --- a/qiskit/circuit/library/generalized_gates/mcmt.py +++ b/qiskit/circuit/library/generalized_gates/mcmt.py @@ -16,9 +16,7 @@ from qiskit.circuit import ControlledGate, Gate, Instruction, Qubit, QuantumRegister, QuantumCircuit from qiskit.exceptions import QiskitError -from ..standard_gates import ( - XGate, YGate, ZGate, HGate, TGate, TdgGate, SGate, SdgGate -) +from ..standard_gates import XGate, YGate, ZGate, HGate, TGate, TdgGate, SGate, SdgGate class MCMT(QuantumCircuit): @@ -46,10 +44,13 @@ class MCMT(QuantumCircuit): :class:`~qiskit.circuit.library.MCMTVChain`. """ - def __init__(self, gate: Union[Gate, Callable[[QuantumCircuit, Qubit, Qubit], Instruction]], - num_ctrl_qubits: int, - num_target_qubits: int, - label: Optional[str] = None) -> None: + def __init__( + self, + gate: Union[Gate, Callable[[QuantumCircuit, Qubit, Qubit], Instruction]], + num_ctrl_qubits: int, + num_target_qubits: int, + label: Optional[str] = None, + ) -> None: """Create a new multi-control multi-target gate. Args: @@ -66,7 +67,7 @@ def __init__(self, gate: Union[Gate, Callable[[QuantumCircuit, Qubit, Qubit], In AttributeError: If the number of controls or targets is 0. """ if num_ctrl_qubits == 0 or num_target_qubits == 0: - raise AttributeError('Need at least one control and one target qubit.') + raise AttributeError("Need at least one control and one target qubit.") # set the internal properties and determine the number of qubits self.gate = self._identify_gate(gate) @@ -75,10 +76,10 @@ def __init__(self, gate: Union[Gate, Callable[[QuantumCircuit, Qubit, Qubit], In num_qubits = num_ctrl_qubits + num_target_qubits + self.num_ancilla_qubits # initialize the circuit object - super().__init__(num_qubits, name='mcmt') + super().__init__(num_qubits, name="mcmt") if label is None: - self.label = '{}-{}'.format(num_target_qubits, self.gate.name.capitalize()) + self.label = "{}-{}".format(num_target_qubits, self.gate.name.capitalize()) else: self.label = label @@ -107,29 +108,30 @@ def num_ancilla_qubits(self): def _identify_gate(self, gate): """Case the gate input to a gate.""" valid_gates = { - 'ch': HGate(), - 'cx': XGate(), - 'cy': YGate(), - 'cz': ZGate(), - 'h': HGate(), - 's': SGate(), - 'sdg': SdgGate(), - 'x': XGate(), - 'y': YGate(), - 'z': ZGate(), - 't': TGate(), - 'tdg': TdgGate(), + "ch": HGate(), + "cx": XGate(), + "cy": YGate(), + "cz": ZGate(), + "h": HGate(), + "s": SGate(), + "sdg": SdgGate(), + "x": XGate(), + "y": YGate(), + "z": ZGate(), + "t": TGate(), + "tdg": TdgGate(), } if isinstance(gate, ControlledGate): base_gate = gate.base_gate elif isinstance(gate, Gate): if gate.num_qubits != 1: - raise AttributeError('Base gate must act on one qubit only.') + raise AttributeError("Base gate must act on one qubit only.") base_gate = gate elif isinstance(gate, QuantumCircuit): if gate.num_qubits != 1: - raise AttributeError('The circuit you specified as control gate can only have ' - 'one qubit!') + raise AttributeError( + "The circuit you specified as control gate can only have " "one qubit!" + ) base_gate = gate.to_gate() # raises error if circuit contains non-unitary instructions else: if callable(gate): # identify via name of the passed function @@ -137,7 +139,7 @@ def _identify_gate(self, gate): elif isinstance(gate, str): name = gate else: - raise AttributeError('Invalid gate specified: {}'.format(gate)) + raise AttributeError("Invalid gate specified: {}".format(gate)) base_gate = valid_gates[name] return base_gate @@ -145,10 +147,9 @@ def _identify_gate(self, gate): def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return the controlled version of the MCMT circuit.""" if ctrl_state is None: # TODO add ctrl state implementation by adding X gates - return MCMT(self.gate, - self.num_ctrl_qubits + num_ctrl_qubits, - self.num_target_qubits, - label) + return MCMT( + self.gate, self.num_ctrl_qubits + num_ctrl_qubits, self.num_target_qubits, label + ) return super().control(num_ctrl_qubits, label, ctrl_state) def inverse(self): @@ -195,10 +196,11 @@ class MCMTVChain(MCMT): def _build(self): """Define the MCMT gate.""" - control_qubits = self.qubits[:self.num_ctrl_qubits] - target_qubits = self.qubits[self.num_ctrl_qubits: - self.num_ctrl_qubits + self.num_target_qubits] - ancilla_qubits = self.qubits[self.num_ctrl_qubits + self.num_target_qubits:] + control_qubits = self.qubits[: self.num_ctrl_qubits] + target_qubits = self.qubits[ + self.num_ctrl_qubits : self.num_ctrl_qubits + self.num_target_qubits + ] + ancilla_qubits = self.qubits[self.num_ctrl_qubits + self.num_target_qubits :] if len(ancilla_qubits) > 0: master_control = ancilla_qubits[-1] @@ -215,9 +217,12 @@ def num_ancilla_qubits(self): """Return the number of ancilla qubits required.""" return max(0, self.num_ctrl_qubits - 1) - def _ccx_v_chain_rule(self, control_qubits: Union[QuantumRegister, List[Qubit]], - ancilla_qubits: Union[QuantumRegister, List[Qubit]], - reverse: bool = False) -> List[Tuple[Gate, List[Qubit], List]]: + def _ccx_v_chain_rule( + self, + control_qubits: Union[QuantumRegister, List[Qubit]], + ancilla_qubits: Union[QuantumRegister, List[Qubit]], + reverse: bool = False, + ) -> List[Tuple[Gate, List[Qubit], List]]: """Get the rule for the CCX V-chain. The CCX V-chain progressively computes the CCX of the control qubits and puts the final @@ -238,7 +243,7 @@ def _ccx_v_chain_rule(self, control_qubits: Union[QuantumRegister, List[Qubit]], return if len(ancilla_qubits) < len(control_qubits) - 1: - raise QiskitError('Insufficient number of ancilla qubits.') + raise QiskitError("Insufficient number of ancilla qubits.") iterations = list(enumerate(range(2, len(control_qubits)))) if not reverse: diff --git a/qiskit/circuit/library/generalized_gates/pauli.py b/qiskit/circuit/library/generalized_gates/pauli.py index 88e603c178ee..62db1ec90ae0 100644 --- a/qiskit/circuit/library/generalized_gates/pauli.py +++ b/qiskit/circuit/library/generalized_gates/pauli.py @@ -26,17 +26,17 @@ class PauliGate(Gate): r"""A multi-qubit Pauli gate. - This gate exists for optimization purposes for the - quantum statevector simulation, since applying multiple - pauli gates to different qubits at once can be done via - a single pass on the statevector. + This gate exists for optimization purposes for the + quantum statevector simulation, since applying multiple + pauli gates to different qubits at once can be done via + a single pass on the statevector. - The functionality is equivalent to applying - the pauli gates sequentially using standard Qiskit gates - """ + The functionality is equivalent to applying + the pauli gates sequentially using standard Qiskit gates + """ def __init__(self, label): - super().__init__('pauli', len(label), [label]) + super().__init__("pauli", len(label), [label]) def _define(self): """ @@ -44,13 +44,12 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - gates = {'I': IGate, 'X': XGate, 'Y': YGate, 'Z': ZGate} - q = QuantumRegister(len(self.params[0]), 'q') - qc = QuantumCircuit(q, - name='{}({})'.format(self.name, self.params[0])) - rules = [(gates[p](), [q[i]], []) - for (i, p) in enumerate(reversed(self.params[0]))] + gates = {"I": IGate, "X": XGate, "Y": YGate, "Z": ZGate} + q = QuantumRegister(len(self.params[0]), "q") + qc = QuantumCircuit(q, name="{}({})".format(self.name, self.params[0])) + + rules = [(gates[p](), [q[i]], []) for (i, p) in enumerate(reversed(self.params[0]))] qc._data = rules self.definition = qc @@ -63,6 +62,7 @@ def __array__(self, dtype=None): i.e. tensor product of the paulis""" # pylint: disable=cyclic-import from qiskit.quantum_info.operators import Pauli + return Pauli(self.params[0]).__array__(dtype=dtype) def validate_parameter(self, parameter): @@ -70,8 +70,10 @@ def validate_parameter(self, parameter): if all(c in ["I", "X", "Y", "Z"] for c in parameter): return parameter else: - raise CircuitError("Parameter string {0} should contain only " - "'I', 'X', 'Y', 'Z' characters") + raise CircuitError( + "Parameter string {0} should contain only " "'I', 'X', 'Y', 'Z' characters" + ) else: - raise CircuitError("Parameter {0} should be a string of " - "'I', 'X', 'Y', 'Z' characters") + raise CircuitError( + "Parameter {0} should be a string of " "'I', 'X', 'Y', 'Z' characters" + ) diff --git a/qiskit/circuit/library/generalized_gates/permutation.py b/qiskit/circuit/library/generalized_gates/permutation.py index 5c3ed090f4bc..cf4c868b058e 100644 --- a/qiskit/circuit/library/generalized_gates/permutation.py +++ b/qiskit/circuit/library/generalized_gates/permutation.py @@ -23,11 +23,12 @@ class Permutation(QuantumCircuit): """An n_qubit circuit that permutes qubits.""" - def __init__(self, - num_qubits: int, - pattern: Optional[List[int]] = None, - seed: Optional[int] = None, - ) -> None: + def __init__( + self, + num_qubits: int, + pattern: Optional[List[int]] = None, + seed: Optional[int] = None, + ) -> None: """Return an n_qubit permutation circuit implemented using SWAPs. Args: @@ -60,15 +61,16 @@ def __init__(self, """ if pattern is not None: if sorted(pattern) != list(range(num_qubits)): - raise CircuitError("Permutation pattern must be some " - "ordering of 0..num_qubits-1 in a list.") + raise CircuitError( + "Permutation pattern must be some " "ordering of 0..num_qubits-1 in a list." + ) pattern = np.array(pattern) else: rng = np.random.default_rng(seed) pattern = np.arange(num_qubits) rng.shuffle(pattern) - name = "permutation_" + np.array_str(pattern).replace(' ', ',') + name = "permutation_" + np.array_str(pattern).replace(" ", ",") inner = QuantumCircuit(num_qubits, name=name) diff --git a/qiskit/circuit/library/generalized_gates/rv.py b/qiskit/circuit/library/generalized_gates/rv.py index 3bc816a60ca2..48e9c9b44166 100644 --- a/qiskit/circuit/library/generalized_gates/rv.py +++ b/qiskit/circuit/library/generalized_gates/rv.py @@ -42,7 +42,7 @@ class RVGate(Gate): \end{pmatrix} """ - def __init__(self, v_x, v_y, v_z, basis='U'): + def __init__(self, v_x, v_y, v_z, basis="U"): """Create new rv single-qubit gate. Args: @@ -54,15 +54,17 @@ def __init__(self, v_x, v_y, v_z, basis='U'): """ # pylint: disable=cyclic-import from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer - super().__init__('rv', 1, [v_x, v_y, v_z]) + + super().__init__("rv", 1, [v_x, v_y, v_z]) self._decomposer = OneQubitEulerDecomposer(basis=basis) def _define(self): try: self.definition = self._decomposer(self.to_matrix()) except TypeError as ex: - raise CircuitError(f'The {self.name} gate cannot be decomposed ' - 'with unbound parameters') from ex + raise CircuitError( + f"The {self.name} gate cannot be decomposed " "with unbound parameters" + ) from ex def inverse(self): """Invert this gate.""" @@ -78,5 +80,9 @@ def to_matrix(self): nx, ny, nz = v / angle sin = numpy.sin(angle / 2) cos = numpy.cos(angle / 2) - return numpy.array([[cos - 1j * nz * sin, (-ny - 1j * nx) * sin], - [(ny - 1j * nx) * sin, cos + 1j * nz * sin]]) + return numpy.array( + [ + [cos - 1j * nz * sin, (-ny - 1j * nx) * sin], + [(ny - 1j * nx) * sin, cos + 1j * nz * sin], + ] + ) diff --git a/qiskit/circuit/library/graph_state.py b/qiskit/circuit/library/graph_state.py index 749665d39ba3..a309da7a95bb 100644 --- a/qiskit/circuit/library/graph_state.py +++ b/qiskit/circuit/library/graph_state.py @@ -59,8 +59,7 @@ class GraphState(QuantumCircuit): `arXiv:1512.07892 `_ """ - def __init__(self, - adjacency_matrix: Union[List, np.array]) -> None: + def __init__(self, adjacency_matrix: Union[List, np.array]) -> None: """Create graph state preparation circuit. Args: @@ -82,6 +81,6 @@ def __init__(self, self.h(range(num_qubits)) for i in range(num_qubits): - for j in range(i+1, num_qubits): + for j in range(i + 1, num_qubits): if adjacency_matrix[i][j] == 1: self.cz(i, j) diff --git a/qiskit/circuit/library/grover_operator.py b/qiskit/circuit/library/grover_operator.py index 49e173dd3dc1..b9bfb09b164e 100644 --- a/qiskit/circuit/library/grover_operator.py +++ b/qiskit/circuit/library/grover_operator.py @@ -157,13 +157,16 @@ class GroverOperator(QuantumCircuit): `arXiv:quant-ph/0005055 `_. """ - def __init__(self, oracle: Union[QuantumCircuit, Statevector], - state_preparation: Optional[QuantumCircuit] = None, - zero_reflection: Optional[Union[QuantumCircuit, DensityMatrix, Operator]] = None, - reflection_qubits: Optional[List[int]] = None, - insert_barriers: bool = False, - mcx_mode: str = 'noancilla', - name: str = 'Q') -> None: + def __init__( + self, + oracle: Union[QuantumCircuit, Statevector], + state_preparation: Optional[QuantumCircuit] = None, + zero_reflection: Optional[Union[QuantumCircuit, DensityMatrix, Operator]] = None, + reflection_qubits: Optional[List[int]] = None, + insert_barriers: bool = False, + mcx_mode: str = "noancilla", + name: str = "Q", + ) -> None: r""" Args: oracle: The phase oracle implementing a reflection about the bad state. Note that this @@ -182,11 +185,13 @@ def __init__(self, oracle: Union[QuantumCircuit, Statevector], # store inputs if isinstance(oracle, Statevector): from qiskit.circuit.library import Diagonal # pylint: disable=cyclic-import + oracle = Diagonal((-1) ** oracle.data) self._oracle = oracle if isinstance(zero_reflection, (Operator, DensityMatrix)): from qiskit.circuit.library import Diagonal # pylint: disable=cyclic-import + zero_reflection = Diagonal(zero_reflection.data.diagonal()) self._zero_reflection = zero_reflection @@ -223,7 +228,7 @@ def state_preparation(self) -> QuantumCircuit: return self._state_preparation num_state_qubits = self.oracle.num_qubits - self.oracle.num_ancillas - hadamards = QuantumCircuit(num_state_qubits, name='H') + hadamards = QuantumCircuit(num_state_qubits, name="H") # apply Hadamards only on reflection qubits, rest will cancel out hadamards.h(self.reflection_qubits) return hadamards @@ -235,41 +240,50 @@ def oracle(self): def _build(self): num_state_qubits = self.oracle.num_qubits - self.oracle.num_ancillas - self.add_register(QuantumRegister(num_state_qubits, name='state')) - num_ancillas = numpy.max([self.oracle.num_ancillas, - self.zero_reflection.num_ancillas, - self.state_preparation.num_ancillas]) + self.add_register(QuantumRegister(num_state_qubits, name="state")) + num_ancillas = numpy.max( + [ + self.oracle.num_ancillas, + self.zero_reflection.num_ancillas, + self.state_preparation.num_ancillas, + ] + ) if num_ancillas > 0: - self.add_register(AncillaRegister(num_ancillas, name='ancilla')) + self.add_register(AncillaRegister(num_ancillas, name="ancilla")) self.compose(self.oracle, list(range(self.oracle.num_qubits)), inplace=True) if self._insert_barriers: self.barrier() - self.compose(self.state_preparation.inverse(), - list(range(self.state_preparation.num_qubits)), - inplace=True) + self.compose( + self.state_preparation.inverse(), + list(range(self.state_preparation.num_qubits)), + inplace=True, + ) if self._insert_barriers: self.barrier() - self.compose(self.zero_reflection, list(range(self.zero_reflection.num_qubits)), - inplace=True) + self.compose( + self.zero_reflection, list(range(self.zero_reflection.num_qubits)), inplace=True + ) if self._insert_barriers: self.barrier() - self.compose(self.state_preparation, list(range(self.state_preparation.num_qubits)), - inplace=True) + self.compose( + self.state_preparation, list(range(self.state_preparation.num_qubits)), inplace=True + ) # minus sign self.global_phase = numpy.pi # TODO use the oracle compiler or the bit string oracle -def _zero_reflection(num_state_qubits: int, qubits: List[int], mcx_mode: Optional[str] = None - ) -> QuantumCircuit: - qr_state = QuantumRegister(num_state_qubits, 'state') - reflection = QuantumCircuit(qr_state, name='S_0') +def _zero_reflection( + num_state_qubits: int, qubits: List[int], mcx_mode: Optional[str] = None +) -> QuantumCircuit: + qr_state = QuantumRegister(num_state_qubits, "state") + reflection = QuantumCircuit(qr_state, name="S_0") num_ancillas = MCXGate.get_num_ancilla_qubits(len(qubits) - 1, mcx_mode) if num_ancillas > 0: - qr_ancilla = AncillaRegister(num_ancillas, 'ancilla') + qr_ancilla = AncillaRegister(num_ancillas, "ancilla") reflection.add_register(qr_ancilla) else: qr_ancilla = [] diff --git a/qiskit/circuit/library/hidden_linear_function.py b/qiskit/circuit/library/hidden_linear_function.py index faf2baad9c88..5cdbfe8709e4 100644 --- a/qiskit/circuit/library/hidden_linear_function.py +++ b/qiskit/circuit/library/hidden_linear_function.py @@ -69,8 +69,7 @@ class HiddenLinearFunction(QuantumCircuit): `arXiv:1704.00690 `_ """ - def __init__(self, - adjacency_matrix: Union[List[List[int]], np.ndarray]) -> None: + def __init__(self, adjacency_matrix: Union[List[List[int]], np.ndarray]) -> None: """Create new HLF circuit. Args: @@ -85,12 +84,11 @@ def __init__(self, raise CircuitError("The adjacency matrix must be symmetric.") num_qubits = len(adjacency_matrix) - super().__init__(num_qubits, - name="hlf: %s" % adjacency_matrix) + super().__init__(num_qubits, name="hlf: %s" % adjacency_matrix) self.h(range(num_qubits)) for i in range(num_qubits): - for j in range(i+1, num_qubits): + for j in range(i + 1, num_qubits): if adjacency_matrix[i][j]: self.cz(i, j) for i in range(num_qubits): diff --git a/qiskit/circuit/library/iqp.py b/qiskit/circuit/library/iqp.py index b1b17dfb99be..8d1c8536e6b5 100644 --- a/qiskit/circuit/library/iqp.py +++ b/qiskit/circuit/library/iqp.py @@ -78,15 +78,15 @@ def __init__(self, interactions: Union[List, np.array]) -> None: raise CircuitError("The interactions matrix is not symmetric") a_str = np.array_str(interactions) - a_str.replace('\n', ';') - name = "iqp:" + a_str.replace('\n', ';') + a_str.replace("\n", ";") + name = "iqp:" + a_str.replace("\n", ";") inner = QuantumCircuit(num_qubits, name=name) super().__init__(num_qubits, name=name) inner.h(range(num_qubits)) for i in range(num_qubits): - for j in range(i+1, num_qubits): + for j in range(i + 1, num_qubits): if interactions[i][j] % 4 != 0: inner.cp(interactions[i][j] * np.pi / 2, i, j) diff --git a/qiskit/circuit/library/n_local/__init__.py b/qiskit/circuit/library/n_local/__init__.py index b2e7b2fd6ae9..7c1846dc88e9 100644 --- a/qiskit/circuit/library/n_local/__init__.py +++ b/qiskit/circuit/library/n_local/__init__.py @@ -21,11 +21,11 @@ from .qaoa_ansatz import QAOAAnsatz __all__ = [ - 'NLocal', - 'TwoLocal', - 'RealAmplitudes', - 'PauliTwoDesign', - 'EfficientSU2', - 'ExcitationPreserving', - 'QAOAAnsatz' + "NLocal", + "TwoLocal", + "RealAmplitudes", + "PauliTwoDesign", + "EfficientSU2", + "ExcitationPreserving", + "QAOAAnsatz", ] diff --git a/qiskit/circuit/library/n_local/efficient_su2.py b/qiskit/circuit/library/n_local/efficient_su2.py index 4c71642f83c5..08a7df8e44b8 100644 --- a/qiskit/circuit/library/n_local/efficient_su2.py +++ b/qiskit/circuit/library/n_local/efficient_su2.py @@ -74,20 +74,26 @@ class EfficientSU2(TwoLocal): """ - def __init__(self, - num_qubits: Optional[int] = None, - su2_gates: Optional[Union[ - str, type, Instruction, QuantumCircuit, - List[Union[str, type, Instruction, QuantumCircuit]] - ]] = None, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + su2_gates: Optional[ + Union[ + str, + type, + Instruction, + QuantumCircuit, + List[Union[str, type, Instruction, QuantumCircuit]], + ] + ] = None, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = "θ", + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: """Create a new EfficientSU2 2-local circuit. Args: @@ -118,16 +124,18 @@ def __init__(self, """ if su2_gates is None: su2_gates = [RYGate, RZGate] - super().__init__(num_qubits=num_qubits, - rotation_blocks=su2_gates, - entanglement_blocks=CXGate, - entanglement=entanglement, - reps=reps, - skip_unentangled_qubits=skip_unentangled_qubits, - skip_final_rotation_layer=skip_final_rotation_layer, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers, - initial_state=initial_state) + super().__init__( + num_qubits=num_qubits, + rotation_blocks=su2_gates, + entanglement_blocks=CXGate, + entanglement=entanglement, + reps=reps, + skip_unentangled_qubits=skip_unentangled_qubits, + skip_final_rotation_layer=skip_final_rotation_layer, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers, + initial_state=initial_state, + ) @property def parameter_bounds(self) -> List[Tuple[float, float]]: diff --git a/qiskit/circuit/library/n_local/excitation_preserving.py b/qiskit/circuit/library/n_local/excitation_preserving.py index 7cc1511f8959..bad4cc2dc7be 100644 --- a/qiskit/circuit/library/n_local/excitation_preserving.py +++ b/qiskit/circuit/library/n_local/excitation_preserving.py @@ -88,17 +88,18 @@ class ExcitationPreserving(TwoLocal): └──────────┘ ░ └────────────┘└────────────┘ ░ └──────────┘ """ - def __init__(self, - num_qubits: Optional[int] = None, - mode: str = 'iswap', - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + mode: str = "iswap", + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = "θ", + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: """Create a new ExcitationPreserving 2-local circuit. Args: @@ -129,28 +130,30 @@ def __init__(self, Raises: ValueError: If the selected mode is not supported. """ - supported_modes = ['iswap', 'fsim'] + supported_modes = ["iswap", "fsim"] if mode not in supported_modes: - raise ValueError('Unsupported mode {}, choose one of {}'.format(mode, supported_modes)) + raise ValueError("Unsupported mode {}, choose one of {}".format(mode, supported_modes)) - theta = Parameter('θ') - swap = QuantumCircuit(2, name='Interaction') + theta = Parameter("θ") + swap = QuantumCircuit(2, name="Interaction") swap.rxx(theta, 0, 1) swap.ryy(theta, 0, 1) - if mode == 'fsim': - phi = Parameter('φ') + if mode == "fsim": + phi = Parameter("φ") swap.cp(phi, 0, 1) - super().__init__(num_qubits=num_qubits, - rotation_blocks=RZGate, - entanglement_blocks=swap, - entanglement=entanglement, - reps=reps, - skip_unentangled_qubits=skip_unentangled_qubits, - skip_final_rotation_layer=skip_final_rotation_layer, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers, - initial_state=initial_state) + super().__init__( + num_qubits=num_qubits, + rotation_blocks=RZGate, + entanglement_blocks=swap, + entanglement=entanglement, + reps=reps, + skip_unentangled_qubits=skip_unentangled_qubits, + skip_final_rotation_layer=skip_final_rotation_layer, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers, + initial_state=initial_state, + ) @property def parameter_bounds(self) -> List[Tuple[float, float]]: diff --git a/qiskit/circuit/library/n_local/n_local.py b/qiskit/circuit/library/n_local/n_local.py index b69e9be0908d..f22f8788b3ec 100644 --- a/qiskit/circuit/library/n_local/n_local.py +++ b/qiskit/circuit/library/n_local/n_local.py @@ -63,21 +63,25 @@ class NLocal(BlueprintCircuit): If an initial state object of Qiskit Aqua is provided, it is added in front of the NLocal. """ - def __init__(self, - num_qubits: Optional[int] = None, - rotation_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit], - Instruction, List[Instruction]]] = None, - entanglement_blocks: Optional[Union[QuantumCircuit, List[QuantumCircuit], - Instruction, List[Instruction]]] = None, - entanglement: Optional[Union[List[int], List[List[int]]]] = None, - reps: int = 1, - insert_barriers: bool = False, - parameter_prefix: str = 'θ', - overwrite_block_parameters: Union[bool, List[List[Parameter]]] = True, - skip_final_rotation_layer: bool = False, - skip_unentangled_qubits: bool = False, - initial_state: Optional[Any] = None, - name: Optional[str] = 'nlocal') -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + rotation_blocks: Optional[ + Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]] + ] = None, + entanglement_blocks: Optional[ + Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]] + ] = None, + entanglement: Optional[Union[List[int], List[List[int]]]] = None, + reps: int = 1, + insert_barriers: bool = False, + parameter_prefix: str = "θ", + overwrite_block_parameters: Union[bool, List[List[Parameter]]] = True, + skip_final_rotation_layer: bool = False, + skip_unentangled_qubits: bool = False, + initial_state: Optional[Any] = None, + name: Optional[str] = "nlocal", + ) -> None: """Create a new n-local circuit. Args: @@ -131,7 +135,7 @@ def __init__(self, self._bounds = None if reps < 0: - raise ValueError('The value of reps should be larger than or equal to 0') + raise ValueError("The value of reps should be larger than or equal to 0") if num_qubits is not None: self.num_qubits = num_qubits @@ -168,7 +172,7 @@ def num_qubits(self, num_qubits: int) -> None: # invalidate the circuit self._invalidate() self._num_qubits = num_qubits - self.qregs = [QuantumRegister(num_qubits, name='q')] + self.qregs = [QuantumRegister(num_qubits, name="q")] def _convert_to_block(self, layer: Any) -> QuantumCircuit: """Try to convert ``layer`` to a QuantumCircuit. @@ -197,7 +201,7 @@ def _convert_to_block(self, layer: Any) -> QuantumCircuit: except AttributeError: pass - raise TypeError('Adding a {} to an NLocal is not supported.'.format(type(layer))) + raise TypeError("Adding a {} to an NLocal is not supported.".format(type(layer))) @property def rotation_blocks(self) -> List[Instruction]: @@ -209,8 +213,9 @@ def rotation_blocks(self) -> List[Instruction]: return self._rotation_blocks @rotation_blocks.setter - def rotation_blocks(self, blocks: Union[QuantumCircuit, List[QuantumCircuit], - Instruction, List[Instruction]]) -> None: + def rotation_blocks( + self, blocks: Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]] + ) -> None: """Set the blocks in the rotation layers. Args: @@ -233,8 +238,9 @@ def entanglement_blocks(self) -> List[Instruction]: return self._entanglement_blocks @entanglement_blocks.setter - def entanglement_blocks(self, blocks: Union[QuantumCircuit, List[QuantumCircuit], - Instruction, List[Instruction]]) -> None: + def entanglement_blocks( + self, blocks: Union[QuantumCircuit, List[QuantumCircuit], Instruction, List[Instruction]] + ) -> None: """Set the blocks in the entanglement layers. Args: @@ -248,9 +254,19 @@ def entanglement_blocks(self, blocks: Union[QuantumCircuit, List[QuantumCircuit] self._entanglement_blocks = [self._convert_to_block(block) for block in blocks] @property - def entanglement(self) -> Union[str, List[str], List[List[str]], List[int], List[List[int]], - List[List[List[int]]], List[List[List[List[int]]]], - Callable[[int], str], Callable[[int], List[List[int]]]]: + def entanglement( + self, + ) -> Union[ + str, + List[str], + List[List[str]], + List[int], + List[List[int]], + List[List[List[int]]], + List[List[List[List[int]]]], + Callable[[int], str], + Callable[[int], List[List[int]]], + ]: """Get the entanglement strategy. Returns: @@ -260,11 +276,22 @@ def entanglement(self) -> Union[str, List[str], List[List[str]], List[int], List return self._entanglement @entanglement.setter - def entanglement(self, entanglement: Optional[Union[str, List[str], List[List[str]], List[int], - List[List[int]], List[List[List[int]]], - List[List[List[List[int]]]], - Callable[[int], str], - Callable[[int], List[List[int]]]]]) -> None: + def entanglement( + self, + entanglement: Optional[ + Union[ + str, + List[str], + List[List[str]], + List[int], + List[List[int]], + List[List[List[int]]], + List[List[List[List[int]]]], + Callable[[int], str], + Callable[[int], List[List[int]]], + ] + ], + ) -> None: """Set the entanglement strategy. Args: @@ -308,13 +335,13 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self.num_qubits is None: valid = False if raise_on_failure: - raise ValueError('No number of qubits specified.') + raise ValueError("No number of qubits specified.") # check no needed parameters are None if self.entanglement_blocks is None and self.rotation_blocks is None: valid = False if raise_on_failure: - raise ValueError('The blocks are not set.') + raise ValueError("The blocks are not set.") return valid @@ -357,12 +384,16 @@ def ordered_parameters(self, parameters: Union[ParameterVector, List[Parameter]] parameters in the circuit and they are not a ``ParameterVector`` (which could be resized to fit the number of parameters). """ - if not isinstance(parameters, ParameterVector) \ - and len(parameters) != self.num_parameters_settable: - raise ValueError('The length of ordered parameters must be equal to the number of ' - 'settable parameters in the circuit ({}), but is {}'.format( - self.num_parameters_settable, len(parameters) - )) + if ( + not isinstance(parameters, ParameterVector) + and len(parameters) != self.num_parameters_settable + ): + raise ValueError( + "The length of ordered parameters must be equal to the number of " + "settable parameters in the circuit ({}), but is {}".format( + self.num_parameters_settable, len(parameters) + ) + ) self._ordered_parameters = parameters self._invalidate() @@ -434,8 +465,11 @@ def num_parameters_settable(self) -> int: for j in range(self.num_qubits // block.num_qubits) ] if self._skip_unentangled_qubits: - block_indices = [indices for indices in block_indices - if set(indices).isdisjoint(unentangled_qubits)] + block_indices = [ + indices + for indices in block_indices + if set(indices).isdisjoint(unentangled_qubits) + ] num_rot += len(block_indices) * len(get_parameters(block)) num += num_rot * (self._reps + int(not self._skip_final_rotation_layer)) @@ -465,7 +499,7 @@ def reps(self, repetitions: int) -> None: ValueError: If reps setter has parameter repetitions < 0. """ if repetitions < 0: - raise ValueError('The repetitions should be larger than or equal to 0') + raise ValueError("The repetitions should be larger than or equal to 0") if repetitions != self._reps: self._invalidate() self._reps = repetitions @@ -476,12 +510,12 @@ def print_settings(self) -> str: Returns: The class name and the attributes/parameters of the instance as ``str``. """ - ret = 'NLocal: {}\n'.format(self.__class__.__name__) - params = '' + ret = "NLocal: {}\n".format(self.__class__.__name__) + params = "" for key, value in self.__dict__.items(): - if key[0] == '_': - params += '-- {}: {}\n'.format(key[1:], value) - ret += '{}'.format(params) + if key[0] == "_": + params += "-- {}: {}\n".format(key[1:], value) + ret += "{}".format(params) return ret @property @@ -494,8 +528,9 @@ def preferred_init_points(self) -> Optional[List[float]]: return None # pylint: disable=too-many-return-statements - def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int - ) -> List[List[int]]: + def get_entangler_map( + self, rep_num: int, block_num: int, num_block_qubits: int + ) -> List[List[int]]: """Get the entangler map for in the repetition ``rep_num`` and the block ``block_num``. The entangler map for the current block is derived from the value of ``self.entanglement``. @@ -548,7 +583,7 @@ def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int # check if entanglement is list of something if not isinstance(entanglement, (tuple, list)): - raise ValueError('Invalid value of entanglement: {}'.format(entanglement)) + raise ValueError("Invalid value of entanglement: {}".format(entanglement)) num_i = len(entanglement) # entanglement is List[str] @@ -561,13 +596,14 @@ def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int # check if entanglement is List[List] if not all(isinstance(e, (tuple, list)) for e in entanglement): - raise ValueError('Invalid value of entanglement: {}'.format(entanglement)) + raise ValueError("Invalid value of entanglement: {}".format(entanglement)) num_j = len(entanglement[i % num_i]) # entanglement is List[List[str]] if all(isinstance(e2, str) for e in entanglement for e2 in e): - return get_entangler_map(n, self.num_qubits, entanglement[i % num_i][j % num_j], - offset=i) + return get_entangler_map( + n, self.num_qubits, entanglement[i % num_i][j % num_j], offset=i + ) # entanglement is List[List[int]] if all(isinstance(e2, int) for e in entanglement for e2 in e): @@ -575,7 +611,7 @@ def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int # check if entanglement is List[List[List]] if not all(isinstance(e2, (tuple, list)) for e in entanglement for e2 in e): - raise ValueError('Invalid value of entanglement: {}'.format(entanglement)) + raise ValueError("Invalid value of entanglement: {}".format(entanglement)) # entanglement is List[List[List[int]]] if all(isinstance(e3, int) for e in entanglement for e2 in e for e3 in e2): @@ -583,13 +619,13 @@ def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int # check if entanglement is List[List[List[List]]] if not all(isinstance(e3, (tuple, list)) for e in entanglement for e2 in e for e3 in e2): - raise ValueError('Invalid value of entanglement: {}'.format(entanglement)) + raise ValueError("Invalid value of entanglement: {}".format(entanglement)) # entanglement is List[List[List[List[int]]]] if all(isinstance(e4, int) for e in entanglement for e2 in e for e3 in e2 for e4 in e3): return entanglement[i % num_i][j % num_j] - raise ValueError('Invalid value of entanglement: {}'.format(entanglement)) + raise ValueError("Invalid value of entanglement: {}".format(entanglement)) @property def initial_state(self) -> Any: @@ -617,13 +653,15 @@ def initial_state(self, initial_state: Any) -> None: self._initial_state = initial_state # construct the circuit of the initial state - self._initial_state_circuit = initial_state.construct_circuit(mode='circuit') + self._initial_state_circuit = initial_state.construct_circuit(mode="circuit") # the initial state dictates the number of qubits since we do not have information # about on which qubits the initial state acts - if self._num_qubits is not None and \ - self._initial_state_circuit.num_qubits != self._num_qubits: - raise ValueError('Mismatching number of qubits in initial state and n-local circuit.') + if ( + self._num_qubits is not None + and self._initial_state_circuit.num_qubits != self._num_qubits + ): + raise ValueError("Mismatching number of qubits in initial state and n-local circuit.") self._invalidate() @@ -653,11 +691,12 @@ def _invalidate(self): self._data = None self._parameter_table = ParameterTable() - def add_layer(self, - other: Union['NLocal', Instruction, QuantumCircuit], - entanglement: Optional[Union[List[int], str, List[List[int]]]] = None, - front: bool = False, - ) -> 'NLocal': + def add_layer( + self, + other: Union["NLocal", Instruction, QuantumCircuit], + entanglement: Optional[Union[List[int], str, List[List[int]]]] = None, + front: bool = False, + ) -> "NLocal": """Append another layer to the NLocal. Args: @@ -705,7 +744,7 @@ def add_layer(self, layer = QuantumCircuit(self.num_qubits) for i in entangler_map: - params = self.ordered_parameters[-len(get_parameters(block)):] + params = self.ordered_parameters[-len(get_parameters(block)) :] parameterized_block = self._parameterize_block(block, params=params) layer.compose(parameterized_block, i) @@ -716,12 +755,13 @@ def add_layer(self, return self - @deprecate_arguments({'param_dict': 'parameters'}) - def assign_parameters(self, parameters: Union[dict, List[float], List[Parameter], - ParameterVector], - inplace: bool = False, - param_dict: Optional[dict] = None # pylint: disable=unused-argument - ) -> Optional[QuantumCircuit]: + @deprecate_arguments({"param_dict": "parameters"}) + def assign_parameters( + self, + parameters: Union[dict, List[float], List[Parameter], ParameterVector], + inplace: bool = False, + param_dict: Optional[dict] = None, # pylint: disable=unused-argument + ) -> Optional[QuantumCircuit]: """Assign parameters to the n-local circuit. This method also supports passing a list instead of a dictionary. If a list @@ -741,12 +781,17 @@ def assign_parameters(self, parameters: Union[dict, List[float], List[Parameter] if not isinstance(parameters, dict): if len(parameters) != self.num_parameters: - raise AttributeError('If the parameters are provided as list, the size must match ' - 'the number of parameters ({}), but {} are given.'.format( - self.num_parameters, len(parameters) - )) - unbound_parameters = [param for param in self._ordered_parameters if - isinstance(param, ParameterExpression)] + raise AttributeError( + "If the parameters are provided as list, the size must match " + "the number of parameters ({}), but {} are given.".format( + self.num_parameters, len(parameters) + ) + ) + unbound_parameters = [ + param + for param in self._ordered_parameters + if isinstance(param, ParameterExpression) + ] # to get a sorted list of unique parameters, keep track of the already used parameters # in a set and add the parameters to the unique list only if not existing in the set @@ -765,8 +810,9 @@ def assign_parameters(self, parameters: Union[dict, List[float], List[Parameter] return super().assign_parameters(parameters, inplace=inplace) - def _parameterize_block(self, block, param_iter=None, rep_num=None, block_num=None, - indices=None, params=None): + def _parameterize_block( + self, block, param_iter=None, rep_num=None, block_num=None, indices=None, params=None + ): """Convert ``block`` to a circuit of correct width and parameterized using the iterator.""" if self._overwrite_block_parameters: # check if special parameters should be used @@ -802,8 +848,11 @@ def _build_rotation_layer(self, param_iter, i): # if unentangled qubits should not be acted on, remove all operations that # touch an unentangled qubit if self._skip_unentangled_qubits: - block_indices = [indices for indices in block_indices - if set(indices).isdisjoint(unentangled_qubits)] + block_indices = [ + indices + for indices in block_indices + if set(indices).isdisjoint(unentangled_qubits) + ] # apply the operations in the layer for indices in block_indices: @@ -830,14 +879,14 @@ def _build_entanglement_layer(self, param_iter, i): self.compose(layer, inplace=True) def _build_additional_layers(self, which): - if which == 'appended': + if which == "appended": blocks = self._appended_blocks entanglements = self._appended_entanglement - elif which == 'prepended': + elif which == "prepended": blocks = reversed(self._prepended_blocks) entanglements = reversed(self._prepended_entanglement) else: - raise ValueError('`which` must be either `appended` or `prepended`.') + raise ValueError("`which` must be either `appended` or `prepended`.") for block, ent in zip(blocks, entanglements): layer = QuantumCircuit(*self.qregs) @@ -862,13 +911,13 @@ def _build(self) -> None: # use the initial state circuit if it is not None if self._initial_state: - circuit = self._initial_state.construct_circuit('circuit', register=self.qregs[0]) + circuit = self._initial_state.construct_circuit("circuit", register=self.qregs[0]) self.compose(circuit, inplace=True) param_iter = iter(self.ordered_parameters) # build the prepended layers - self._build_additional_layers('prepended') + self._build_additional_layers("prepended") # main loop to build the entanglement and rotation layers for i in range(self.reps): @@ -893,7 +942,7 @@ def _build(self) -> None: self._build_rotation_layer(param_iter, self.reps) # add the appended layers - self._build_additional_layers('appended') + self._build_additional_layers("appended") # pylint: disable=unused-argument def _parameter_generator(self, rep: int, block: int, indices: List[int]) -> Optional[Parameter]: @@ -907,11 +956,43 @@ def __str__(self) -> str: A single string representing this NLocal. """ from qiskit.compiler import transpile - basis_gates = ['id', 'x', 'y', 'z', 'h', 's', 't', 'sdg', 'tdg', 'rx', 'ry', 'rz', - 'rxx', 'ryy', 'cx', 'cy', 'cz', 'ch', 'crx', 'cry', 'crz', 'swap', - 'cswap', 'ccx', 'cu1', 'cu3', 'u1', 'u2', 'u3'] - return transpile(self, basis_gates=basis_gates, - optimization_level=0).draw(output='text').single_string() + + basis_gates = [ + "id", + "x", + "y", + "z", + "h", + "s", + "t", + "sdg", + "tdg", + "rx", + "ry", + "rz", + "rxx", + "ryy", + "cx", + "cy", + "cz", + "ch", + "crx", + "cry", + "crz", + "swap", + "cswap", + "ccx", + "cu1", + "cu3", + "u1", + "u2", + "u3", + ] + return ( + transpile(self, basis_gates=basis_gates, optimization_level=0) + .draw(output="text") + .single_string() + ) def get_parameters(block: Union[QuantumCircuit, Instruction]) -> List[Parameter]: @@ -927,8 +1008,9 @@ def get_parameters(block: Union[QuantumCircuit, Instruction]) -> List[Parameter] return [p for p in block.params if isinstance(p, ParameterExpression)] -def get_entangler_map(num_block_qubits: int, num_circuit_qubits: int, entanglement: str, - offset: int = 0) -> List[Sequence[int]]: +def get_entangler_map( + num_block_qubits: int, num_circuit_qubits: int, entanglement: str, offset: int = 0 +) -> List[Sequence[int]]: """Get an entangler map for an arbitrary number of qubits. Args: @@ -947,21 +1029,23 @@ def get_entangler_map(num_block_qubits: int, num_circuit_qubits: int, entangleme """ n, m = num_circuit_qubits, num_block_qubits if m > n: - raise ValueError('The number of block qubits must be smaller or equal to the number of ' - 'qubits in the circuit.') + raise ValueError( + "The number of block qubits must be smaller or equal to the number of " + "qubits in the circuit." + ) - if entanglement == 'pairwise' and num_block_qubits != 2: - raise ValueError('Pairwise entanglement is only defined for blocks of 2 qubits.') + if entanglement == "pairwise" and num_block_qubits != 2: + raise ValueError("Pairwise entanglement is only defined for blocks of 2 qubits.") - if entanglement == 'full': + if entanglement == "full": return list(combinations(list(range(n)), m)) - if entanglement in ['linear', 'circular', 'sca', 'pairwise']: + if entanglement in ["linear", "circular", "sca", "pairwise"]: linear = [tuple(range(i, i + m)) for i in range(n - m + 1)] # if the number of block qubits is 1, we don't have to add the 'circular' part - if entanglement == 'linear' or m == 1: + if entanglement == "linear" or m == 1: return linear - if entanglement == 'pairwise': + if entanglement == "pairwise": return linear[::2] + linear[1::2] # circular equals linear plus top-bottom entanglement (if there's space for it) @@ -969,7 +1053,7 @@ def get_entangler_map(num_block_qubits: int, num_circuit_qubits: int, entangleme circular = [tuple(range(n - m + 1, n)) + (0,)] + linear else: circular = linear - if entanglement == 'circular': + if entanglement == "circular": return circular # sca is circular plus shift and reverse @@ -982,4 +1066,4 @@ def get_entangler_map(num_block_qubits: int, num_circuit_qubits: int, entangleme return sca else: - raise ValueError('Unsupported entanglement type: {}'.format(entanglement)) + raise ValueError("Unsupported entanglement type: {}".format(entanglement)) diff --git a/qiskit/circuit/library/n_local/pauli_two_design.py b/qiskit/circuit/library/n_local/pauli_two_design.py index 1e9efb5a22c4..282299b95200 100644 --- a/qiskit/circuit/library/n_local/pauli_two_design.py +++ b/qiskit/circuit/library/n_local/pauli_two_design.py @@ -66,8 +66,13 @@ class PauliTwoDesign(TwoLocal): `arXiv:1803.11173 `_ """ - def __init__(self, num_qubits: Optional[int] = None, reps: int = 3, - seed: Optional[int] = None, insert_barriers: bool = False): + def __init__( + self, + num_qubits: Optional[int] = None, + reps: int = 3, + seed: Optional[int] = None, + insert_barriers: bool = False, + ): from qiskit.circuit.library import RYGate # pylint: disable=cyclic-import # store a random number generator @@ -77,12 +82,17 @@ def __init__(self, num_qubits: Optional[int] = None, reps: int = 3, # store a dict to keep track of the random gates self._gates = dict() - super().__init__(num_qubits, reps=reps, entanglement_blocks='cz', entanglement='pairwise', - insert_barriers=insert_barriers) + super().__init__( + num_qubits, + reps=reps, + entanglement_blocks="cz", + entanglement="pairwise", + insert_barriers=insert_barriers, + ) # set the initial layer self._prepended_blocks = [RYGate(np.pi / 4)] - self._prepended_entanglement = ['linear'] + self._prepended_entanglement = ["linear"] def _invalidate(self): self._rng = np.random.default_rng(self._seed) # reset number generator @@ -95,11 +105,11 @@ def _build_rotation_layer(self, param_iter, i): # if no gates for this layer were generated, generate them if i not in self._gates.keys(): - self._gates[i] = list(self._rng.choice(['rx', 'ry', 'rz'], self.num_qubits)) + self._gates[i] = list(self._rng.choice(["rx", "ry", "rz"], self.num_qubits)) # if not enough gates exist, add more elif len(self._gates[i]) < self.num_qubits: num_missing = self.num_qubits - len(self._gates[i]) - self._gates[i] += list(self._rng.choice(['rx', 'ry', 'rz'], num_missing)) + self._gates[i] += list(self._rng.choice(["rx", "ry", "rz"], num_missing)) for j in qubits: getattr(layer, self._gates[i][j])(next(param_iter), j) diff --git a/qiskit/circuit/library/n_local/qaoa_ansatz.py b/qiskit/circuit/library/n_local/qaoa_ansatz.py index 19663fc8306a..51074d4220e4 100644 --- a/qiskit/circuit/library/n_local/qaoa_ansatz.py +++ b/qiskit/circuit/library/n_local/qaoa_ansatz.py @@ -32,12 +32,14 @@ class QAOAAnsatz(BlueprintCircuit): `arXiv:1411.4028 `_ """ - def __init__(self, - cost_operator=None, - reps: int = 1, - initial_state: Optional[QuantumCircuit] = None, - mixer_operator=None, - name: str = "qaoa"): + def __init__( + self, + cost_operator=None, + reps: int = 1, + initial_state: Optional[QuantumCircuit] = None, + mixer_operator=None, + name: str = "qaoa", + ): r""" Args: cost_operator (OperatorBase, optional): The operator representing the cost of @@ -74,28 +76,36 @@ def _check_configuration(self, raise_on_failure: bool = True) -> bool: if self._cost_operator is None: valid = False if raise_on_failure: - raise AttributeError("The operator representing the cost of " - "the optimization problem is not set") + raise AttributeError( + "The operator representing the cost of " "the optimization problem is not set" + ) if self._reps is None or self._reps < 0: valid = False if raise_on_failure: - raise AttributeError("The integer parameter reps, which determines the depth " - "of the circuit, needs to be >= 0 but has value {}" - .format(self._reps)) + raise AttributeError( + "The integer parameter reps, which determines the depth " + "of the circuit, needs to be >= 0 but has value {}".format(self._reps) + ) if self.initial_state is not None and self.initial_state.num_qubits != self.num_qubits: valid = False if raise_on_failure: - raise AttributeError("The number of qubits of the initial state {} does not match " - "the number of qubits of the cost operator {}" - .format(self.initial_state.num_qubits, self.num_qubits)) + raise AttributeError( + "The number of qubits of the initial state {} does not match " + "the number of qubits of the cost operator {}".format( + self.initial_state.num_qubits, self.num_qubits + ) + ) if self.mixer_operator is not None and self.mixer_operator.num_qubits != self.num_qubits: valid = False if raise_on_failure: - raise AttributeError("The number of qubits of the mixer {} does not match " - "the number of qubits of the cost operator {}" - .format(self.mixer_operator.num_qubits, self.num_qubits)) + raise AttributeError( + "The number of qubits of the mixer {} does not match " + "the number of qubits of the cost operator {}".format( + self.mixer_operator.num_qubits, self.num_qubits + ) + ) return valid @@ -124,7 +134,7 @@ def _reset_registers(self, num_qubits): self._qubit_set = set() if num_qubits > 0: - qr = QuantumRegister(num_qubits, 'q') + qr = QuantumRegister(num_qubits, "q") self.add_register(qr) @property @@ -151,10 +161,12 @@ def parameter_bounds(self) -> List[Tuple[float, float]]: def _calculate_parameters(self): """Calculated internal parameters of the circuit to be built.""" from qiskit.opflow import OperatorBase + if isinstance(self._mixer, QuantumCircuit): self._num_parameters = (1 + self._mixer.num_parameters) * self._reps - self._bounds = [(None, None)] * self._reps + \ - [(None, None)] * self._reps * self._mixer.num_parameters + self._bounds = [(None, None)] * self._reps + [ + (None, None) + ] * self._reps * self._mixer.num_parameters elif isinstance(self._mixer, OperatorBase): self._num_parameters = 2 * self._reps self._bounds = [(None, None)] * self._reps + [(None, None)] * self._reps @@ -165,9 +177,11 @@ def _calculate_parameters(self): def _construct_circuit(self, parameters) -> QuantumCircuit: """Construct a parameterized circuit.""" if not len(parameters) == self._num_parameters: - raise ValueError('Incorrect number of angles: expecting {}, but {} given.'.format( - self._num_parameters, len(parameters) - )) + raise ValueError( + "Incorrect number of angles: expecting {}, but {} given.".format( + self._num_parameters, len(parameters) + ) + ) # local imports to avoid circular imports from qiskit.opflow import CircuitStateFn @@ -192,8 +206,9 @@ def _construct_circuit(self, parameters) -> QuantumCircuit: num_params = mixer.num_parameters # the remaining [self._p:] parameters are used for the mixer, # there may be multiple layers, so parameters are grouped by layers. - param_values = parameters[self._reps + num_params * idx: - self._reps + num_params * (idx + 1)] + param_values = parameters[ + self._reps + num_params * idx : self._reps + num_params * (idx + 1) + ] param_dict = dict(zip(mixer.parameters, param_values)) mixer = mixer.assign_parameters(param_dict) circuit_op = CircuitOp(mixer).compose(circuit_op) @@ -273,10 +288,13 @@ def mixer_operator(self): if self.num_qubits > 0: # local imports to avoid circular imports from qiskit.opflow import I, X + # Mixer is just a sum of single qubit X's on each qubit. Evolving by this operator # will simply produce rx's on each qubit. - mixer_terms = [(I ^ left) ^ X ^ (I ^ (self.num_qubits - left - 1)) - for left in range(self.num_qubits)] + mixer_terms = [ + (I ^ left) ^ X ^ (I ^ (self.num_qubits - left - 1)) + for left in range(self.num_qubits) + ] mixer = sum(mixer_terms) return mixer diff --git a/qiskit/circuit/library/n_local/real_amplitudes.py b/qiskit/circuit/library/n_local/real_amplitudes.py index eb222c1289a1..d448b46982b1 100644 --- a/qiskit/circuit/library/n_local/real_amplitudes.py +++ b/qiskit/circuit/library/n_local/real_amplitudes.py @@ -104,16 +104,17 @@ class RealAmplitudes(TwoLocal): """ - def __init__(self, - num_qubits: Optional[int] = None, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = "θ", + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: """Create a new RealAmplitudes 2-local circuit. Args: @@ -141,16 +142,18 @@ def __init__(self, no barriers are inserted. """ - super().__init__(num_qubits=num_qubits, - reps=reps, - rotation_blocks=RYGate, - entanglement_blocks=CXGate, - entanglement=entanglement, - initial_state=initial_state, - skip_unentangled_qubits=skip_unentangled_qubits, - skip_final_rotation_layer=skip_final_rotation_layer, - parameter_prefix=parameter_prefix, - insert_barriers=insert_barriers) + super().__init__( + num_qubits=num_qubits, + reps=reps, + rotation_blocks=RYGate, + entanglement_blocks=CXGate, + entanglement=entanglement, + initial_state=initial_state, + skip_unentangled_qubits=skip_unentangled_qubits, + skip_final_rotation_layer=skip_final_rotation_layer, + parameter_prefix=parameter_prefix, + insert_barriers=insert_barriers, + ) @property def parameter_bounds(self) -> List[Tuple[float, float]]: diff --git a/qiskit/circuit/library/n_local/two_local.py b/qiskit/circuit/library/n_local/two_local.py index 2d8526d347e6..fb3793dfcc29 100644 --- a/qiskit/circuit/library/n_local/two_local.py +++ b/qiskit/circuit/library/n_local/two_local.py @@ -19,9 +19,30 @@ from .n_local import NLocal from ..standard_gates import ( - IGate, XGate, YGate, ZGate, RXGate, RYGate, RZGate, HGate, SGate, SdgGate, TGate, TdgGate, - RXXGate, RYYGate, RZXGate, RZZGate, SwapGate, CXGate, CYGate, CZGate, CRXGate, CRYGate, CRZGate, - CHGate + IGate, + XGate, + YGate, + ZGate, + RXGate, + RYGate, + RZGate, + HGate, + SGate, + SdgGate, + TGate, + TdgGate, + RXXGate, + RYYGate, + RZXGate, + RZZGate, + SwapGate, + CXGate, + CYGate, + CZGate, + CRXGate, + CRYGate, + CRZGate, + CHGate, ) @@ -127,20 +148,23 @@ class TwoLocal(NLocal): """ - def __init__(self, - num_qubits: Optional[int] = None, - rotation_blocks: Optional[Union[str, List[str], type, List[type], - QuantumCircuit, List[QuantumCircuit]]] = None, - entanglement_blocks: Optional[Union[str, List[str], type, List[type], - QuantumCircuit, List[QuantumCircuit]]] = None, - entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = 'full', - reps: int = 3, - skip_unentangled_qubits: bool = False, - skip_final_rotation_layer: bool = False, - parameter_prefix: str = 'θ', - insert_barriers: bool = False, - initial_state: Optional[Any] = None, - ) -> None: + def __init__( + self, + num_qubits: Optional[int] = None, + rotation_blocks: Optional[ + Union[str, List[str], type, List[type], QuantumCircuit, List[QuantumCircuit]] + ] = None, + entanglement_blocks: Optional[ + Union[str, List[str], type, List[type], QuantumCircuit, List[QuantumCircuit]] + ] = None, + entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", + reps: int = 3, + skip_unentangled_qubits: bool = False, + skip_final_rotation_layer: bool = False, + parameter_prefix: str = "θ", + insert_barriers: bool = False, + initial_state: Optional[Any] = None, + ) -> None: """Construct a new two-local circuit. Args: @@ -174,16 +198,18 @@ def __init__(self, initial_state: A `QuantumCircuit` object to prepend to the circuit. """ - super().__init__(num_qubits=num_qubits, - rotation_blocks=rotation_blocks, - entanglement_blocks=entanglement_blocks, - entanglement=entanglement, - reps=reps, - skip_final_rotation_layer=skip_final_rotation_layer, - skip_unentangled_qubits=skip_unentangled_qubits, - insert_barriers=insert_barriers, - initial_state=initial_state, - parameter_prefix=parameter_prefix) + super().__init__( + num_qubits=num_qubits, + rotation_blocks=rotation_blocks, + entanglement_blocks=entanglement_blocks, + entanglement=entanglement, + reps=reps, + skip_final_rotation_layer=skip_final_rotation_layer, + skip_unentangled_qubits=skip_unentangled_qubits, + insert_barriers=insert_barriers, + initial_state=initial_state, + parameter_prefix=parameter_prefix, + ) def _convert_to_block(self, layer: Union[str, type, Gate, QuantumCircuit]) -> QuantumCircuit: """For a layer provided as str (e.g. 'ry') or type (e.g. RYGate) this function returns the @@ -210,34 +236,34 @@ def _convert_to_block(self, layer: Union[str, type, Gate, QuantumCircuit]) -> Qu # check the list of valid layers # this could be a lot easier if the standard layers would have `name` and `num_params` # as static types, which might be something they should have anyways - theta = Parameter('θ') + theta = Parameter("θ") valid_layers = { - 'ch': CHGate(), - 'cx': CXGate(), - 'cy': CYGate(), - 'cz': CZGate(), - 'crx': CRXGate(theta), - 'cry': CRYGate(theta), - 'crz': CRZGate(theta), - 'h': HGate(), - 'i': IGate(), - 'id': IGate(), - 'iden': IGate(), - 'rx': RXGate(theta), - 'rxx': RXXGate(theta), - 'ry': RYGate(theta), - 'ryy': RYYGate(theta), - 'rz': RZGate(theta), - 'rzx': RZXGate(theta), - 'rzz': RZZGate(theta), - 's': SGate(), - 'sdg': SdgGate(), - 'swap': SwapGate(), - 'x': XGate(), - 'y': YGate(), - 'z': ZGate(), - 't': TGate(), - 'tdg': TdgGate(), + "ch": CHGate(), + "cx": CXGate(), + "cy": CYGate(), + "cz": CZGate(), + "crx": CRXGate(theta), + "cry": CRYGate(theta), + "crz": CRZGate(theta), + "h": HGate(), + "i": IGate(), + "id": IGate(), + "iden": IGate(), + "rx": RXGate(theta), + "rxx": RXXGate(theta), + "ry": RYGate(theta), + "ryy": RYYGate(theta), + "rz": RZGate(theta), + "rzx": RZXGate(theta), + "rzz": RZZGate(theta), + "s": SGate(), + "sdg": SdgGate(), + "swap": SwapGate(), + "x": XGate(), + "y": YGate(), + "z": ZGate(), + "t": TGate(), + "tdg": TdgGate(), } # try to exchange `layer` from a string to a gate instance @@ -245,7 +271,7 @@ def _convert_to_block(self, layer: Union[str, type, Gate, QuantumCircuit]) -> Qu try: layer = valid_layers[layer] except KeyError as ex: - raise ValueError(f'Unknown layer name `{layer}`.') from ex + raise ValueError(f"Unknown layer name `{layer}`.") from ex # try to exchange `layer` from a type to a gate instance if isinstance(layer, type): @@ -255,7 +281,7 @@ def _convert_to_block(self, layer: Union[str, type, Gate, QuantumCircuit]) -> Qu if isinstance(gate, layer): instance = gate if instance is None: - raise ValueError('Unknown layer type`{}`.'.format(layer)) + raise ValueError("Unknown layer type`{}`.".format(layer)) layer = instance if isinstance(layer, Instruction): @@ -263,11 +289,14 @@ def _convert_to_block(self, layer: Union[str, type, Gate, QuantumCircuit]) -> Qu circuit.append(layer, list(range(layer.num_qubits))) return circuit - raise TypeError('Invalid input type {}. '.format(type(layer)) - + '`layer` must be a type, str or QuantumCircuit.') + raise TypeError( + "Invalid input type {}. ".format(type(layer)) + + "`layer` must be a type, str or QuantumCircuit." + ) - def get_entangler_map(self, rep_num: int, block_num: int, num_block_qubits: int - ) -> List[List[int]]: + def get_entangler_map( + self, rep_num: int, block_num: int, num_block_qubits: int + ) -> List[List[int]]: """Overloading to handle the special case of 1 qubit where the entanglement are ignored.""" if self.num_qubits <= 1: return [] diff --git a/qiskit/circuit/library/phase_estimation.py b/qiskit/circuit/library/phase_estimation.py index f829dd36a502..1381feb5eb4c 100644 --- a/qiskit/circuit/library/phase_estimation.py +++ b/qiskit/circuit/library/phase_estimation.py @@ -48,11 +48,13 @@ class PhaseEstimation(QuantumCircuit): """ - def __init__(self, - num_evaluation_qubits: int, - unitary: QuantumCircuit, - iqft: Optional[QuantumCircuit] = None, - name: str = 'QPE') -> None: + def __init__( + self, + num_evaluation_qubits: int, + unitary: QuantumCircuit, + iqft: Optional[QuantumCircuit] = None, + name: str = "QPE", + ) -> None: """ Args: num_evaluation_qubits: The number of evaluation qubits. @@ -79,8 +81,8 @@ def __init__(self, circuit = PhaseEstimation(3, unitary) %circuit_library_info circuit """ - qr_eval = QuantumRegister(num_evaluation_qubits, 'eval') - qr_state = QuantumRegister(unitary.num_qubits, 'q') + qr_eval = QuantumRegister(num_evaluation_qubits, "eval") + qr_state = QuantumRegister(unitary.num_qubits, "q") super().__init__(qr_eval, qr_state, name=name) if iqft is None: @@ -89,6 +91,6 @@ def __init__(self, self.h(qr_eval) # hadamards on evaluation qubits for j in range(num_evaluation_qubits): # controlled powers - self.append(unitary.power(2**j).control(), [j] + qr_state[:]) + self.append(unitary.power(2 ** j).control(), [j] + qr_state[:]) self.append(iqft, qr_eval[:]) # final QFT diff --git a/qiskit/circuit/library/phase_oracle.py b/qiskit/circuit/library/phase_oracle.py index 4659175fde2b..be3301c117e1 100644 --- a/qiskit/circuit/library/phase_oracle.py +++ b/qiskit/circuit/library/phase_oracle.py @@ -41,9 +41,11 @@ class PhaseOracle(QuantumCircuit): default synthesizer. """ - def __init__(self, expression: Union[str, ClassicalElement], - synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None) \ - -> None: + def __init__( + self, + expression: Union[str, ClassicalElement], + synthesizer: Optional[Callable[[BooleanExpression], QuantumCircuit]] = None, + ) -> None: """Creates a PhaseOracle object Args: @@ -57,17 +59,20 @@ def __init__(self, expression: Union[str, ClassicalElement], self.boolean_expression = expression if synthesizer is None: + def synthesizer(boolean_expression): from tweedledum.synthesis import pkrm_synth # pylint: disable=no-name-in-module from qiskit.circuit.classicalfunction.utils import tweedledum2qiskit + truth_table = boolean_expression._tweedledum_bool_expression.truth_table( - output_bit=0) + output_bit=0 + ) tweedledum_circuit = pkrm_synth(truth_table, {"pkrm_synth": {"phase_esop": True}}) return tweedledum2qiskit(tweedledum_circuit) oracle = expression.synth(synthesizer=synthesizer) - super().__init__(oracle.num_qubits, name='Phase Oracle') + super().__init__(oracle.num_qubits, name="Phase Oracle") self.compose(oracle, inplace=True) diff --git a/qiskit/circuit/library/probability_distributions/__init__.py b/qiskit/circuit/library/probability_distributions/__init__.py index 19245506f174..742682325a5e 100644 --- a/qiskit/circuit/library/probability_distributions/__init__.py +++ b/qiskit/circuit/library/probability_distributions/__init__.py @@ -16,8 +16,4 @@ from .normal import NormalDistribution from .uniform import UniformDistribution -__all__ = [ - 'LogNormalDistribution', - 'NormalDistribution', - 'UniformDistribution' -] +__all__ = ["LogNormalDistribution", "NormalDistribution", "UniformDistribution"] diff --git a/qiskit/circuit/library/probability_distributions/lognormal.py b/qiskit/circuit/library/probability_distributions/lognormal.py index cd69c34c21f5..c4f16c0d4457 100644 --- a/qiskit/circuit/library/probability_distributions/lognormal.py +++ b/qiskit/circuit/library/probability_distributions/lognormal.py @@ -79,13 +79,15 @@ class LogNormalDistribution(QuantumCircuit): """ - def __init__(self, - num_qubits: Union[int, List[int]], - mu: Optional[Union[float, List[float]]] = None, - sigma: Optional[Union[float, List[float]]] = None, - bounds: Optional[Union[Tuple[float, float], List[Tuple[float, float]]]] = None, - upto_diag: bool = False, - name: str = 'P(X)') -> None: + def __init__( + self, + num_qubits: Union[int, List[int]], + mu: Optional[Union[float, List[float]]] = None, + sigma: Optional[Union[float, List[float]]] = None, + bounds: Optional[Union[Tuple[float, float], List[Tuple[float, float]]]] = None, + upto_diag: bool = False, + name: str = "P(X)", + ) -> None: r""" Args: num_qubits: The number of qubits used to discretize the random variable. For a 1d @@ -103,10 +105,13 @@ def __init__(self, with a diagonal for a more efficient circuit. name: The name of the circuit. """ - warnings.warn('`LogNormalDistribution` is deprecated as of version 0.17.0 and will be ' - 'removed no earlier than 3 months after the release date. ' - 'It moved to qiskit_finance.circuit.library.LogNormalDistribution.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`LogNormalDistribution` is deprecated as of version 0.17.0 and will be " + "removed no earlier than 3 months after the release date. " + "It moved to qiskit_finance.circuit.library.LogNormalDistribution.", + DeprecationWarning, + stacklevel=2, + ) _check_dimensions_match(num_qubits, mu, sigma, bounds) _check_bounds_valid(bounds) @@ -125,14 +130,19 @@ def __init__(self, if not isinstance(num_qubits, list): # univariate case super().__init__(num_qubits, name=name) - x = np.linspace(bounds[0], bounds[1], num=2**num_qubits) # evaluation points + x = np.linspace(bounds[0], bounds[1], num=2 ** num_qubits) # evaluation points else: # multivariate case super().__init__(sum(num_qubits), name=name) # compute the evaluation points using numpy's meshgrid # indexing 'ij' yields the "column-based" indexing - meshgrid = np.meshgrid(*[np.linspace(bound[0], bound[1], num=2**num_qubits[i]) - for i, bound in enumerate(bounds)], indexing='ij') + meshgrid = np.meshgrid( + *[ + np.linspace(bound[0], bound[1], num=2 ** num_qubits[i]) + for i, bound in enumerate(bounds) + ], + indexing="ij", + ) # flatten into a list of points x = list(zip(*[grid.flatten() for grid in meshgrid])) @@ -162,6 +172,7 @@ def __init__(self, self.isometry(np.sqrt(normalized_probabilities), self.qubits, None) else: from qiskit.extensions import Initialize # pylint: disable=cyclic-import + initialize = Initialize(np.sqrt(normalized_probabilities)) circuit = initialize.gates_to_uncompute().inverse() self.compose(circuit, inplace=True) diff --git a/qiskit/circuit/library/probability_distributions/normal.py b/qiskit/circuit/library/probability_distributions/normal.py index 50f09022c241..8594985c0833 100644 --- a/qiskit/circuit/library/probability_distributions/normal.py +++ b/qiskit/circuit/library/probability_distributions/normal.py @@ -126,13 +126,15 @@ class NormalDistribution(QuantumCircuit): """ - def __init__(self, - num_qubits: Union[int, List[int]], - mu: Optional[Union[float, List[float]]] = None, - sigma: Optional[Union[float, List[float]]] = None, - bounds: Optional[Union[Tuple[float, float], List[Tuple[float, float]]]] = None, - upto_diag: bool = False, - name: str = 'P(X)') -> None: + def __init__( + self, + num_qubits: Union[int, List[int]], + mu: Optional[Union[float, List[float]]] = None, + sigma: Optional[Union[float, List[float]]] = None, + bounds: Optional[Union[Tuple[float, float], List[Tuple[float, float]]]] = None, + upto_diag: bool = False, + name: str = "P(X)", + ) -> None: r""" Args: num_qubits: The number of qubits used to discretize the random variable. For a 1d @@ -150,10 +152,13 @@ def __init__(self, with a diagonal for a more efficient circuit. name: The name of the circuit. """ - warnings.warn('`NormalDistribution` is deprecated as of version 0.17.0 and will be ' - 'removed no earlier than 3 months after the release date. ' - 'It moved to qiskit_finance.circuit.library.NormalDistribution.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`NormalDistribution` is deprecated as of version 0.17.0 and will be " + "removed no earlier than 3 months after the release date. " + "It moved to qiskit_finance.circuit.library.NormalDistribution.", + DeprecationWarning, + stacklevel=2, + ) _check_dimensions_match(num_qubits, mu, sigma, bounds) _check_bounds_valid(bounds) @@ -172,14 +177,19 @@ def __init__(self, if not isinstance(num_qubits, list): # univariate case super().__init__(num_qubits, name=name) - x = np.linspace(bounds[0], bounds[1], num=2**num_qubits) + x = np.linspace(bounds[0], bounds[1], num=2 ** num_qubits) else: # multivariate case super().__init__(sum(num_qubits), name=name) # compute the evaluation points using numpy's meshgrid # indexing 'ij' yields the "column-based" indexing - meshgrid = np.meshgrid(*[np.linspace(bound[0], bound[1], num=2**num_qubits[i]) - for i, bound in enumerate(bounds)], indexing='ij') + meshgrid = np.meshgrid( + *[ + np.linspace(bound[0], bound[1], num=2 ** num_qubits[i]) + for i, bound in enumerate(bounds) + ], + indexing="ij", + ) # flatten into a list of points x = list(zip(*[grid.flatten() for grid in meshgrid])) @@ -200,6 +210,7 @@ def __init__(self, self.isometry(np.sqrt(normalized_probabilities), self.qubits, None) else: from qiskit.extensions import Initialize # pylint: disable=cyclic-import + initialize = Initialize(np.sqrt(normalized_probabilities)) circuit = initialize.gates_to_uncompute().inverse() self.compose(circuit, inplace=True) @@ -227,25 +238,31 @@ def _check_dimensions_match(num_qubits, mu, sigma, bounds): if mu is not None: mu = [mu] if not isinstance(mu, (list, np.ndarray)) else mu if len(mu) != dim: - raise ValueError('Dimension of mu ({}) does not match the dimension of the ' - 'random variable specified by the number of qubits ({})' - ''.format(len(mu), dim)) + raise ValueError( + "Dimension of mu ({}) does not match the dimension of the " + "random variable specified by the number of qubits ({})" + "".format(len(mu), dim) + ) if sigma is not None: sigma = [[sigma]] if not isinstance(sigma, (list, np.ndarray)) else sigma if len(sigma) != dim or len(sigma[0]) != dim: - raise ValueError('Dimension of sigma ({} x {}) does not match the dimension of ' - 'the random variable specified by the number of qubits ({})' - ''.format(len(sigma), len(sigma[0]), dim)) + raise ValueError( + "Dimension of sigma ({} x {}) does not match the dimension of " + "the random variable specified by the number of qubits ({})" + "".format(len(sigma), len(sigma[0]), dim) + ) if bounds is not None: # bit differently to cover the case the users might pass `bounds` as a single list, # e.g. [0, 1], instead of a tuple bounds = [bounds] if not isinstance(bounds[0], tuple) else bounds if len(bounds) != dim: - raise ValueError('Dimension of bounds ({}) does not match the dimension of the ' - 'random variable specified by the number of qubits ({})' - ''.format(len(bounds), dim)) + raise ValueError( + "Dimension of bounds ({}) does not match the dimension of the " + "random variable specified by the number of qubits ({})" + "".format(len(bounds), dim) + ) def _check_bounds_valid(bounds): @@ -256,6 +273,8 @@ def _check_bounds_valid(bounds): for i, bound in enumerate(bounds): if not bound[1] - bound[0] > 0: - raise ValueError('Dimension {} of the bounds are invalid, must be a non-empty ' - 'interval where the lower bounds is smaller than the upper bound.' - ''.format(i)) + raise ValueError( + "Dimension {} of the bounds are invalid, must be a non-empty " + "interval where the lower bounds is smaller than the upper bound." + "".format(i) + ) diff --git a/qiskit/circuit/library/probability_distributions/uniform.py b/qiskit/circuit/library/probability_distributions/uniform.py index 12540161d9ba..450d1bf1ff6d 100644 --- a/qiskit/circuit/library/probability_distributions/uniform.py +++ b/qiskit/circuit/library/probability_distributions/uniform.py @@ -47,17 +47,20 @@ class UniformDistribution(QuantumCircuit): """ - def __init__(self, num_qubits: int, name: str = 'P(X)') -> None: + def __init__(self, num_qubits: int, name: str = "P(X)") -> None: """ Args: num_qubits: The number of qubits in the circuit, the distribution is uniform over ``2 ** num_qubits`` values. name: The name of the circuit. """ - warnings.warn('`UniformDistribution` is deprecated as of version 0.17.0 and will be ' - 'removed no earlier than 3 months after the release date. ' - 'It moved to qiskit_finance.circuit.library.UniformDistribution.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`UniformDistribution` is deprecated as of version 0.17.0 and will be " + "removed no earlier than 3 months after the release date. " + "It moved to qiskit_finance.circuit.library.UniformDistribution.", + DeprecationWarning, + stacklevel=2, + ) super().__init__(num_qubits, name=name) self.h(self.qubits) diff --git a/qiskit/circuit/library/quantum_volume.py b/qiskit/circuit/library/quantum_volume.py index bbb21bc33735..8befaf3d85dc 100644 --- a/qiskit/circuit/library/quantum_volume.py +++ b/qiskit/circuit/library/quantum_volume.py @@ -57,11 +57,13 @@ class QuantumVolume(QuantumCircuit): [`arXiv:1811.12926 `_] """ - def __init__(self, - num_qubits: int, - depth: Optional[int] = None, - seed: Optional[Union[int, np.random.Generator]] = None, - classical_permutation: bool = True) -> None: + def __init__( + self, + num_qubits: int, + depth: Optional[int] = None, + seed: Optional[Union[int, np.random.Generator]] = None, + classical_permutation: bool = True, + ) -> None: """Create quantum volume model circuit of size num_qubits x depth. Args: @@ -82,8 +84,8 @@ def __init__(self, # Parameters depth = depth or num_qubits # how many layers of SU(4) - width = int(np.floor(num_qubits/2)) # how many SU(4)s fit in each layer - name = "quantum_volume_" + str([num_qubits, depth, seed]).replace(' ', '') + width = int(np.floor(num_qubits / 2)) # how many SU(4)s fit in each layer + name = "quantum_volume_" + str([num_qubits, depth, seed]).replace(" ", "") super().__init__(num_qubits, name=name) # Generator random unitary seeds in advance. @@ -105,11 +107,11 @@ def __init__(self, for w in range(width): seed_u = unitary_seeds[d][w] su4 = random_unitary(4, seed=seed_u).to_instruction() - su4.label = 'su4_' + str(seed_u) + su4.label = "su4_" + str(seed_u) if classical_permutation: - physical_qubits = int(perm[2*w]), int(perm[2*w+1]) + physical_qubits = int(perm[2 * w]), int(perm[2 * w + 1]) inner.compose(su4, [physical_qubits[0], physical_qubits[1]], inplace=True) else: - inner.compose(su4, [2*w, 2*w+1], inplace=True) + inner.compose(su4, [2 * w, 2 * w + 1], inplace=True) inner.label = name self.append(inner, self.qubits) diff --git a/qiskit/circuit/library/standard_gates/dcx.py b/qiskit/circuit/library/standard_gates/dcx.py index a3b37b8a3a68..a2804cb1ca27 100644 --- a/qiskit/circuit/library/standard_gates/dcx.py +++ b/qiskit/circuit/library/standard_gates/dcx.py @@ -46,7 +46,7 @@ class DCXGate(Gate): def __init__(self): """Create new DCX gate.""" - super().__init__('dcx', 2, []) + super().__init__("dcx", 2, []) def _define(self): """ @@ -55,12 +55,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (CXGate(), [q[0], q[1]], []), - (CXGate(), [q[1], q[0]], []) - ] + rules = [(CXGate(), [q[0], q[1]], []), (CXGate(), [q[1], q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -68,7 +66,4 @@ def _define(self): def __array__(self, dtype=None): """Return a numpy.array for the DCX gate.""" - return np.array([[1, 0, 0, 0], - [0, 0, 0, 1], - [0, 1, 0, 0], - [0, 0, 1, 0]], dtype=dtype) + return np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/ecr.py b/qiskit/circuit/library/standard_gates/ecr.py index a2b6676c854d..bfe7594660d4 100644 --- a/qiskit/circuit/library/standard_gates/ecr.py +++ b/qiskit/circuit/library/standard_gates/ecr.py @@ -78,7 +78,7 @@ class ECRGate(Gate): def __init__(self): """Create new ECR gate.""" - super().__init__('ecr', 2, []) + super().__init__("ecr", 2, []) def _define(self): """ @@ -86,12 +86,13 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ - (RZXGate(np.pi/4), [q[0], q[1]], []), + (RZXGate(np.pi / 4), [q[0], q[1]], []), (XGate(), [q[0]], []), - (RZXGate(-np.pi/4), [q[0], q[1]], []) + (RZXGate(-np.pi / 4), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -100,8 +101,11 @@ def _define(self): def to_matrix(self): """Return a numpy.array for the ECR gate.""" - return 1/np.sqrt(2) * \ - np.array([[0, 1, 0, 1.j], - [1, 0, -1.j, 0], - [0, 1.j, 0, 1], - [-1.j, 0, 1, 0]], dtype=complex) + return ( + 1 + / np.sqrt(2) + * np.array( + [[0, 1, 0, 1.0j], [1, 0, -1.0j, 0], [0, 1.0j, 0, 1], [-1.0j, 0, 1, 0]], + dtype=complex, + ) + ) diff --git a/qiskit/circuit/library/standard_gates/equivalence_library.py b/qiskit/circuit/library/standard_gates/equivalence_library.py index 1726e1ed941e..215b69514171 100644 --- a/qiskit/circuit/library/standard_gates/equivalence_library.py +++ b/qiskit/circuit/library/standard_gates/equivalence_library.py @@ -74,23 +74,23 @@ # HGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_h = QuantumCircuit(q) def_h.append(U2Gate(0, pi), [q[0]], []) _sel.add_equivalence(HGate(), def_h) # CHGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_ch = QuantumCircuit(q) for inst, qargs, cargs in [ - (SGate(), [q[1]], []), - (HGate(), [q[1]], []), - (TGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (TdgGate(), [q[1]], []), - (HGate(), [q[1]], []), - (SdgGate(), [q[1]], []) + (SGate(), [q[1]], []), + (HGate(), [q[1]], []), + (TGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (TdgGate(), [q[1]], []), + (HGate(), [q[1]], []), + (SdgGate(), [q[1]], []), ]: def_ch.append(inst, qargs, cargs) _sel.add_equivalence(CHGate(), def_ch) @@ -98,8 +98,8 @@ # MSGate for num_qubits in range(2, 20): - q = QuantumRegister(num_qubits, 'q') - theta = Parameter('theta') + q = QuantumRegister(num_qubits, "q") + theta = Parameter("theta") def_ms = QuantumCircuit(q) for i in range(num_qubits): for j in range(i + 1, num_qubits): @@ -110,22 +110,22 @@ # PhaseGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") phase_to_u1 = QuantumCircuit(q) phase_to_u1.append(U1Gate(theta), [0]) _sel.add_equivalence(PhaseGate(theta), phase_to_u1) -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") phase_to_u = QuantumCircuit(q) phase_to_u.u(0, 0, theta, 0) _sel.add_equivalence(PhaseGate(theta), phase_to_u) # CPhaseGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_cphase = QuantumCircuit(q) def_cphase.p(theta / 2, 0) def_cphase.cx(0, 1) @@ -134,104 +134,104 @@ def_cphase.p(theta / 2, 1) _sel.add_equivalence(CPhaseGate(theta), def_cphase) -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") cphase_to_cu1 = QuantumCircuit(q) cphase_to_cu1.append(CU1Gate(theta), [0, 1]) _sel.add_equivalence(CPhaseGate(theta), cphase_to_cu1) # RGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') -phi = Parameter('phi') +q = QuantumRegister(1, "q") +theta = Parameter("theta") +phi = Parameter("phi") def_r = QuantumCircuit(q) def_r.append(U3Gate(theta, phi - pi / 2, -phi + pi / 2), [q[0]]) _sel.add_equivalence(RGate(theta, phi), def_r) # RCCXGate -q = QuantumRegister(3, 'q') +q = QuantumRegister(3, "q") def_rccx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[2]], []), - (TGate(), [q[2]], []), - (CXGate(), [q[1], q[2]], []), - (TdgGate(), [q[2]], []), - (CXGate(), [q[0], q[2]], []), - (TGate(), [q[2]], []), - (CXGate(), [q[1], q[2]], []), - (TdgGate(), [q[2]], []), - (HGate(), [q[2]], []), + (HGate(), [q[2]], []), + (TGate(), [q[2]], []), + (CXGate(), [q[1], q[2]], []), + (TdgGate(), [q[2]], []), + (CXGate(), [q[0], q[2]], []), + (TGate(), [q[2]], []), + (CXGate(), [q[1], q[2]], []), + (TdgGate(), [q[2]], []), + (HGate(), [q[2]], []), ]: def_rccx.append(inst, qargs, cargs) _sel.add_equivalence(RCCXGate(), def_rccx) # RXGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") def_rx = QuantumCircuit(q) def_rx.append(RGate(theta, 0), [q[0]], []) _sel.add_equivalence(RXGate(theta), def_rx) # CRXGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_crx = QuantumCircuit(q) for inst, qargs, cargs in [ - (U1Gate(pi / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (U3Gate(-theta / 2, 0, 0), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (U3Gate(theta / 2, -pi / 2, 0), [q[1]], []) + (U1Gate(pi / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (U3Gate(-theta / 2, 0, 0), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (U3Gate(theta / 2, -pi / 2, 0), [q[1]], []), ]: def_crx.append(inst, qargs, cargs) _sel.add_equivalence(CRXGate(theta), def_crx) -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") crx_to_srycx = QuantumCircuit(q) for inst, qargs, cargs in [ - (SGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RYGate(-theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RYGate(theta / 2), [q[1]], []), - (SdgGate(), [q[1]], []), + (SGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RYGate(-theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RYGate(theta / 2), [q[1]], []), + (SdgGate(), [q[1]], []), ]: crx_to_srycx.append(inst, qargs, cargs) _sel.add_equivalence(CRXGate(theta), crx_to_srycx) # RXXGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_rxx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[0]], []), - (HGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []), - (HGate(), [q[0]], []), + (HGate(), [q[0]], []), + (HGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RZGate(theta), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), + (HGate(), [q[0]], []), ]: def_rxx.append(inst, qargs, cargs) _sel.add_equivalence(RXXGate(theta), def_rxx) # RZXGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_rzx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []), + (HGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RZGate(theta), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: def_rzx.append(inst, qargs, cargs) _sel.add_equivalence(RZXGate(theta), def_rzx) @@ -239,52 +239,52 @@ # RYGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") def_ry = QuantumCircuit(q) def_ry.append(RGate(theta, pi / 2), [q[0]], []) _sel.add_equivalence(RYGate(theta), def_ry) # CRYGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_cry = QuantumCircuit(q) for inst, qargs, cargs in [ - (RYGate(theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RYGate(-theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (RYGate(theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RYGate(-theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), ]: def_cry.append(inst, qargs, cargs) _sel.add_equivalence(CRYGate(theta), def_cry) # RYYGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_ryy = QuantumCircuit(q) for inst, qargs, cargs in [ - (RXGate(pi / 2), [q[0]], []), - (RXGate(pi / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RXGate(-pi / 2), [q[0]], []), - (RXGate(-pi / 2), [q[1]], []), + (RXGate(pi / 2), [q[0]], []), + (RXGate(pi / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RZGate(theta), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RXGate(-pi / 2), [q[0]], []), + (RXGate(-pi / 2), [q[1]], []), ]: def_ryy.append(inst, qargs, cargs) _sel.add_equivalence(RYYGate(theta), def_ryy) # RZGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") def_rz = QuantumCircuit(q, global_phase=-theta / 2) def_rz.append(U1Gate(theta), [q[0]], []) _sel.add_equivalence(RZGate(theta), def_rz) -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") rz_to_sxry = QuantumCircuit(q) rz_to_sxry.sx(0) rz_to_sxry.ry(-theta, 0) @@ -293,211 +293,200 @@ # CRZGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_crz = QuantumCircuit(q) for inst, qargs, cargs in [ - (RZGate(theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RZGate(-theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (RZGate(theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RZGate(-theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), ]: def_crz.append(inst, qargs, cargs) _sel.add_equivalence(CRZGate(theta), def_crz) # RZZGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_rzz = QuantumCircuit(q) for inst, qargs, cargs in [ - (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), + (RZGate(theta), [q[1]], []), + (CXGate(), [q[0], q[1]], []), ]: def_rzz.append(inst, qargs, cargs) _sel.add_equivalence(RZZGate(theta), def_rzz) # RZXGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_rzx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (RZGate(theta), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: def_rzx.append(inst, qargs, cargs) _sel.add_equivalence(RZXGate(theta), def_rzx) # ECRGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_ecr = QuantumCircuit(q) for inst, qargs, cargs in [ - (RZXGate(pi/4), [q[0], q[1]], []), - (XGate(), [q[0]], []), - (RZXGate(-pi/4), [q[0], q[1]], []) + (RZXGate(pi / 4), [q[0], q[1]], []), + (XGate(), [q[0]], []), + (RZXGate(-pi / 4), [q[0], q[1]], []), ]: def_ecr.append(inst, qargs, cargs) _sel.add_equivalence(ECRGate(), def_ecr) # SGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_s = QuantumCircuit(q) def_s.append(U1Gate(pi / 2), [q[0]], []) _sel.add_equivalence(SGate(), def_s) # SdgGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_sdg = QuantumCircuit(q) def_sdg.append(U1Gate(-pi / 2), [q[0]], []) _sel.add_equivalence(SdgGate(), def_sdg) # SwapGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_swap = QuantumCircuit(q) for inst, qargs, cargs in [ - (CXGate(), [q[0], q[1]], []), - (CXGate(), [q[1], q[0]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), + (CXGate(), [q[1], q[0]], []), + (CXGate(), [q[0], q[1]], []), ]: def_swap.append(inst, qargs, cargs) _sel.add_equivalence(SwapGate(), def_swap) # iSwapGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_iswap = QuantumCircuit(q) for inst, qargs, cargs in [ - (SGate(), [q[0]], []), - (SGate(), [q[1]], []), - (HGate(), [q[0]], []), - (CXGate(), [q[0], q[1]], []), - (CXGate(), [q[1], q[0]], []), - (HGate(), [q[1]], []) + (SGate(), [q[0]], []), + (SGate(), [q[1]], []), + (HGate(), [q[0]], []), + (CXGate(), [q[0], q[1]], []), + (CXGate(), [q[1], q[0]], []), + (HGate(), [q[1]], []), ]: def_iswap.append(inst, qargs, cargs) _sel.add_equivalence(iSwapGate(), def_iswap) # SXGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_sx = QuantumCircuit(q, global_phase=pi / 4) -for inst, qargs, cargs in [ - (SdgGate(), [q[0]], []), - (HGate(), [q[0]], []), - (SdgGate(), [q[0]], []) -]: +for inst, qargs, cargs in [(SdgGate(), [q[0]], []), (HGate(), [q[0]], []), (SdgGate(), [q[0]], [])]: def_sx.append(inst, qargs, cargs) _sel.add_equivalence(SXGate(), def_sx) -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") sx_to_rx = QuantumCircuit(q, global_phase=pi / 4) sx_to_rx.rx(pi / 2, 0) _sel.add_equivalence(SXGate(), sx_to_rx) # SXdgGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_sxdg = QuantumCircuit(q, global_phase=-pi / 4) -for inst, qargs, cargs in [ - (SGate(), [q[0]], []), - (HGate(), [q[0]], []), - (SGate(), [q[0]], []) -]: +for inst, qargs, cargs in [(SGate(), [q[0]], []), (HGate(), [q[0]], []), (SGate(), [q[0]], [])]: def_sxdg.append(inst, qargs, cargs) _sel.add_equivalence(SXdgGate(), def_sxdg) -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") sxdg_to_rx = QuantumCircuit(q, global_phase=-pi / 4) sxdg_to_rx.rx(-pi / 2, 0) _sel.add_equivalence(SXdgGate(), sxdg_to_rx) # CSXGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_csx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[1]], []), - (CU1Gate(pi / 2), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), + (CU1Gate(pi / 2), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: def_csx.append(inst, qargs, cargs) _sel.add_equivalence(CSXGate(), def_csx) # DCXGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_dcx = QuantumCircuit(q) -for inst, qargs, cargs in [ - (CXGate(), [q[0], q[1]], []), - (CXGate(), [q[1], q[0]], []) -]: +for inst, qargs, cargs in [(CXGate(), [q[0], q[1]], []), (CXGate(), [q[1], q[0]], [])]: def_dcx.append(inst, qargs, cargs) _sel.add_equivalence(DCXGate(), def_dcx) -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") dcx_to_iswap = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[0]], []), - (SdgGate(), [q[0]], []), - (SdgGate(), [q[1]], []), - (iSwapGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[0]], []), + (SdgGate(), [q[0]], []), + (SdgGate(), [q[1]], []), + (iSwapGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: dcx_to_iswap.append(inst, qargs, cargs) _sel.add_equivalence(DCXGate(), dcx_to_iswap) # CSwapGate -q = QuantumRegister(3, 'q') +q = QuantumRegister(3, "q") def_cswap = QuantumCircuit(q) for inst, qargs, cargs in [ - (CXGate(), [q[2], q[1]], []), - (CCXGate(), [q[0], q[1], q[2]], []), - (CXGate(), [q[2], q[1]], []) + (CXGate(), [q[2], q[1]], []), + (CCXGate(), [q[0], q[1], q[2]], []), + (CXGate(), [q[2], q[1]], []), ]: def_cswap.append(inst, qargs, cargs) _sel.add_equivalence(CSwapGate(), def_cswap) # TGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_t = QuantumCircuit(q) def_t.append(U1Gate(pi / 4), [q[0]], []) _sel.add_equivalence(TGate(), def_t) # TdgGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_tdg = QuantumCircuit(q) def_tdg.append(U1Gate(-pi / 4), [q[0]], []) _sel.add_equivalence(TdgGate(), def_tdg) # UGate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(1, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") u_to_u3 = QuantumCircuit(q) u_to_u3.append(U3Gate(theta, phi, lam), [0]) _sel.add_equivalence(UGate(theta, phi, lam), u_to_u3) # CUGate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') -gamma = Parameter('gamma') +q = QuantumRegister(2, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") +gamma = Parameter("gamma") def_cu = QuantumCircuit(q) def_cu.p(gamma, 0) def_cu.p((lam + phi) / 2, 0) @@ -508,11 +497,11 @@ def_cu.u(theta / 2, phi, 0, 1) _sel.add_equivalence(CUGate(theta, phi, lam, gamma), def_cu) -q = QuantumRegister(2, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') -gamma = Parameter('gamma') +q = QuantumRegister(2, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") +gamma = Parameter("gamma") cu_to_cu3 = QuantumCircuit(q) cu_to_cu3.p(gamma, 0) cu_to_cu3.append(CU3Gate(theta, phi, lam), [0, 1]) @@ -520,111 +509,111 @@ # U1Gate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") def_u1 = QuantumCircuit(q) def_u1.append(U3Gate(0, 0, theta), [q[0]], []) _sel.add_equivalence(U1Gate(theta), def_u1) -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") u1_to_phase = QuantumCircuit(q) u1_to_phase.p(theta, 0) _sel.add_equivalence(U1Gate(theta), u1_to_phase) # U1Gate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') +q = QuantumRegister(1, "q") +theta = Parameter("theta") u1_to_rz = QuantumCircuit(q, global_phase=theta / 2) u1_to_rz.append(RZGate(theta), [q[0]], []) _sel.add_equivalence(U1Gate(theta), u1_to_rz) # CU1Gate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') +q = QuantumRegister(2, "q") +theta = Parameter("theta") def_cu1 = QuantumCircuit(q) for inst, qargs, cargs in [ - (U1Gate(theta / 2), [q[0]], []), - (CXGate(), [q[0], q[1]], []), - (U1Gate(-theta / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (U1Gate(theta / 2), [q[1]], []) + (U1Gate(theta / 2), [q[0]], []), + (CXGate(), [q[0], q[1]], []), + (U1Gate(-theta / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (U1Gate(theta / 2), [q[1]], []), ]: def_cu1.append(inst, qargs, cargs) _sel.add_equivalence(CU1Gate(theta), def_cu1) # U1Gate -q = QuantumRegister(1, 'q') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(1, "q") +phi = Parameter("phi") +lam = Parameter("lam") def_u2 = QuantumCircuit(q) def_u2.append(U3Gate(pi / 2, phi, lam), [q[0]], []) _sel.add_equivalence(U2Gate(phi, lam), def_u2) # U2Gate -q = QuantumRegister(1, 'q') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(1, "q") +phi = Parameter("phi") +lam = Parameter("lam") u2_to_u1sx = QuantumCircuit(q, global_phase=-pi / 4) -u2_to_u1sx.append(U1Gate(lam - pi/2), [0]) +u2_to_u1sx.append(U1Gate(lam - pi / 2), [0]) u2_to_u1sx.sx(0) -u2_to_u1sx.append(U1Gate(phi + pi/2), [0]) +u2_to_u1sx.append(U1Gate(phi + pi / 2), [0]) _sel.add_equivalence(U2Gate(phi, lam), u2_to_u1sx) # U3Gate -q = QuantumRegister(1, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(1, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") u3_qasm_def = QuantumCircuit(q, global_phase=(lam + phi - pi) / 2) u3_qasm_def.rz(lam, 0) u3_qasm_def.sx(0) -u3_qasm_def.rz(theta+pi, 0) +u3_qasm_def.rz(theta + pi, 0) u3_qasm_def.sx(0) -u3_qasm_def.rz(phi+3*pi, 0) +u3_qasm_def.rz(phi + 3 * pi, 0) _sel.add_equivalence(U3Gate(theta, phi, lam), u3_qasm_def) -q = QuantumRegister(1, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(1, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") u3_to_u = QuantumCircuit(q) u3_to_u.u(theta, phi, lam, 0) _sel.add_equivalence(U3Gate(theta, phi, lam), u3_to_u) # CU3Gate -q = QuantumRegister(2, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(2, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") def_cu3 = QuantumCircuit(q) for inst, qargs, cargs in [ - (U1Gate((lam + phi) / 2), [q[0]], []), - (U1Gate((lam - phi) / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (U3Gate(-theta / 2, 0, -(phi + lam) / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (U3Gate(theta / 2, phi, 0), [q[1]], []) + (U1Gate((lam + phi) / 2), [q[0]], []), + (U1Gate((lam - phi) / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (U3Gate(-theta / 2, 0, -(phi + lam) / 2), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (U3Gate(theta / 2, phi, 0), [q[1]], []), ]: def_cu3.append(inst, qargs, cargs) _sel.add_equivalence(CU3Gate(theta, phi, lam), def_cu3) -q = QuantumRegister(2, 'q') -theta = Parameter('theta') -phi = Parameter('phi') -lam = Parameter('lam') +q = QuantumRegister(2, "q") +theta = Parameter("theta") +phi = Parameter("phi") +lam = Parameter("lam") cu3_to_cu = QuantumCircuit(q) cu3_to_cu.cu(theta, phi, lam, 0, 0, 1) # XGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_x = QuantumCircuit(q) def_x.append(U3Gate(pi, 0, pi), [q[0]], []) _sel.add_equivalence(XGate(), def_x) @@ -636,140 +625,140 @@ cx_to_rxx = cnot_rxx_decompose(plus_ry, plus_rxx) _sel.add_equivalence(CXGate(), cx_to_rxx) -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") cx_to_cz = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[1]], []), - (CZGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), + (CZGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: cx_to_cz.append(inst, qargs, cargs) _sel.add_equivalence(CXGate(), cx_to_cz) -q = QuantumRegister(2, 'q') -cx_to_iswap = QuantumCircuit(q, global_phase=3*pi/4) +q = QuantumRegister(2, "q") +cx_to_iswap = QuantumCircuit(q, global_phase=3 * pi / 4) for inst, qargs, cargs in [ - (HGate(), [q[0]], []), - (XGate(), [q[1]], []), - (HGate(), [q[1]], []), - (iSwapGate(), [q[0], q[1]], []), - (XGate(), [q[0]], []), - (XGate(), [q[1]], []), - (HGate(), [q[1]], []), - (iSwapGate(), [q[0], q[1]], []), - (HGate(), [q[0]], []), - (SGate(), [q[0]], []), - (SGate(), [q[1]], []), - (XGate(), [q[1]], []), - (HGate(), [q[1]], []), + (HGate(), [q[0]], []), + (XGate(), [q[1]], []), + (HGate(), [q[1]], []), + (iSwapGate(), [q[0], q[1]], []), + (XGate(), [q[0]], []), + (XGate(), [q[1]], []), + (HGate(), [q[1]], []), + (iSwapGate(), [q[0], q[1]], []), + (HGate(), [q[0]], []), + (SGate(), [q[0]], []), + (SGate(), [q[1]], []), + (XGate(), [q[1]], []), + (HGate(), [q[1]], []), ]: cx_to_iswap.append(inst, qargs, cargs) _sel.add_equivalence(CXGate(), cx_to_iswap) -q = QuantumRegister(2, 'q') -cx_to_ecr = QuantumCircuit(q, global_phase=-pi/4) +q = QuantumRegister(2, "q") +cx_to_ecr = QuantumCircuit(q, global_phase=-pi / 4) for inst, qargs, cargs in [ - (RZGate(-pi/2), [q[0]], []), - (RYGate(pi), [q[0]], []), - (RXGate(pi/2), [q[1]], []), - (ECRGate(), [q[0], q[1]], []) + (RZGate(-pi / 2), [q[0]], []), + (RYGate(pi), [q[0]], []), + (RXGate(pi / 2), [q[1]], []), + (ECRGate(), [q[0], q[1]], []), ]: cx_to_ecr.append(inst, qargs, cargs) _sel.add_equivalence(CXGate(), cx_to_ecr) # CCXGate -q = QuantumRegister(3, 'q') +q = QuantumRegister(3, "q") def_ccx = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[2]], []), - (CXGate(), [q[1], q[2]], []), - (TdgGate(), [q[2]], []), - (CXGate(), [q[0], q[2]], []), - (TGate(), [q[2]], []), - (CXGate(), [q[1], q[2]], []), - (TdgGate(), [q[2]], []), - (CXGate(), [q[0], q[2]], []), - (TGate(), [q[1]], []), - (TGate(), [q[2]], []), - (HGate(), [q[2]], []), - (CXGate(), [q[0], q[1]], []), - (TGate(), [q[0]], []), - (TdgGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (HGate(), [q[2]], []), + (CXGate(), [q[1], q[2]], []), + (TdgGate(), [q[2]], []), + (CXGate(), [q[0], q[2]], []), + (TGate(), [q[2]], []), + (CXGate(), [q[1], q[2]], []), + (TdgGate(), [q[2]], []), + (CXGate(), [q[0], q[2]], []), + (TGate(), [q[1]], []), + (TGate(), [q[2]], []), + (HGate(), [q[2]], []), + (CXGate(), [q[0], q[1]], []), + (TGate(), [q[0]], []), + (TdgGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), ]: def_ccx.append(inst, qargs, cargs) _sel.add_equivalence(CCXGate(), def_ccx) # YGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_y = QuantumCircuit(q) def_y.append(U3Gate(pi, pi / 2, pi / 2), [q[0]], []) _sel.add_equivalence(YGate(), def_y) # CYGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_cy = QuantumCircuit(q) for inst, qargs, cargs in [ - (SdgGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (SGate(), [q[1]], []) + (SdgGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (SGate(), [q[1]], []), ]: def_cy.append(inst, qargs, cargs) _sel.add_equivalence(CYGate(), def_cy) # ZGate -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") def_z = QuantumCircuit(q) def_z.append(U1Gate(pi), [q[0]], []) _sel.add_equivalence(ZGate(), def_z) # CZGate -q = QuantumRegister(2, 'q') +q = QuantumRegister(2, "q") def_cz = QuantumCircuit(q) for inst, qargs, cargs in [ - (HGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), + (CXGate(), [q[0], q[1]], []), + (HGate(), [q[1]], []), ]: def_cz.append(inst, qargs, cargs) _sel.add_equivalence(CZGate(), def_cz) # RXGate, XGate equivalence -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") x_to_rx = QuantumCircuit(q) x_to_rx.append(RXGate(theta=pi), [q[0]]) -x_to_rx.global_phase = pi/2 +x_to_rx.global_phase = pi / 2 _sel.add_equivalence(XGate(), x_to_rx) # RYGate, YGate equivalence -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") y_to_ry = QuantumCircuit(q) y_to_ry.append(RYGate(theta=pi), [q[0]]) -y_to_ry.global_phase = pi/2 +y_to_ry.global_phase = pi / 2 _sel.add_equivalence(YGate(), y_to_ry) # HGate, RXGate(pi).RYGate(pi/2) equivalence -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") h_to_rxry = QuantumCircuit(q) -h_to_rxry.append(RYGate(theta=pi/2), [q[0]]) +h_to_rxry.append(RYGate(theta=pi / 2), [q[0]]) h_to_rxry.append(RXGate(theta=pi), [q[0]]) -h_to_rxry.global_phase = pi/2 +h_to_rxry.global_phase = pi / 2 _sel.add_equivalence(HGate(), h_to_rxry) # HGate, RGate(pi, 0).RGate(pi/2, pi/2) equivalence -q = QuantumRegister(1, 'q') +q = QuantumRegister(1, "q") h_to_rr = QuantumCircuit(q) -h_to_rr.append(RGate(theta=pi/2, phi=pi/2), [q[0]]) +h_to_rr.append(RGate(theta=pi / 2, phi=pi / 2), [q[0]]) h_to_rr.append(RGate(theta=pi, phi=0), [q[0]]) -h_to_rr.global_phase = pi/2 +h_to_rr.global_phase = pi / 2 _sel.add_equivalence(HGate(), h_to_rr) diff --git a/qiskit/circuit/library/standard_gates/h.py b/qiskit/circuit/library/standard_gates/h.py index 24d6fe904702..a4071aeed61a 100644 --- a/qiskit/circuit/library/standard_gates/h.py +++ b/qiskit/circuit/library/standard_gates/h.py @@ -49,7 +49,7 @@ class HGate(Gate): def __init__(self, label=None): """Create new H gate.""" - super().__init__('h', 1, [], label=label) + super().__init__("h", 1, [], label=label) def _define(self): """ @@ -58,11 +58,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u2 import U2Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U2Gate(0, pi), [q[0]], []) - ] + rules = [(U2Gate(0, pi), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -86,8 +85,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): gate = CHGate(label=label, ctrl_state=ctrl_state) gate.base_gate.label = self.label return gate - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, - ctrl_state=ctrl_state) + return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) def inverse(self): r"""Return inverted H gate (itself).""" @@ -95,8 +93,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a Numpy.array for the H gate.""" - return numpy.array([[1, 1], - [1, -1]], dtype=dtype) / numpy.sqrt(2) + return numpy.array([[1, 1], [1, -1]], dtype=dtype) / numpy.sqrt(2) class CHGate(ControlledGate): @@ -152,21 +149,20 @@ class CHGate(ControlledGate): """ # Define class constants. This saves future allocation time. _sqrt2o2 = 1 / numpy.sqrt(2) - _matrix1 = numpy.array([[1, 0, 0, 0], - [0, _sqrt2o2, 0, _sqrt2o2], - [0, 0, 1, 0], - [0, _sqrt2o2, 0, -_sqrt2o2]], - dtype=complex) - _matrix0 = numpy.array([[_sqrt2o2, 0, _sqrt2o2, 0], - [0, 1, 0, 0], - [_sqrt2o2, 0, -_sqrt2o2, 0], - [0, 0, 0, 1]], - dtype=complex) + _matrix1 = numpy.array( + [[1, 0, 0, 0], [0, _sqrt2o2, 0, _sqrt2o2], [0, 0, 1, 0], [0, _sqrt2o2, 0, -_sqrt2o2]], + dtype=complex, + ) + _matrix0 = numpy.array( + [[_sqrt2o2, 0, _sqrt2o2, 0], [0, 1, 0, 0], [_sqrt2o2, 0, -_sqrt2o2, 0], [0, 0, 0, 1]], + dtype=complex, + ) def __init__(self, label=None, ctrl_state=None): """Create new CH gate.""" - super().__init__('ch', 2, [], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=HGate()) + super().__init__( + "ch", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=HGate() + ) def _define(self): """ @@ -183,7 +179,8 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate # pylint: disable=cyclic-import - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (SGate(), [q[1]], []), @@ -192,7 +189,7 @@ def _define(self): (CXGate(), [q[0], q[1]], []), (TdgGate(), [q[1]], []), (HGate(), [q[1]], []), - (SdgGate(), [q[1]], []) + (SdgGate(), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) diff --git a/qiskit/circuit/library/standard_gates/i.py b/qiskit/circuit/library/standard_gates/i.py index b73730f87696..67ece09f9322 100644 --- a/qiskit/circuit/library/standard_gates/i.py +++ b/qiskit/circuit/library/standard_gates/i.py @@ -41,7 +41,7 @@ class IGate(Gate): def __init__(self, label=None): """Create new Identity gate.""" - super().__init__('id', 1, [], label=label) + super().__init__("id", 1, [], label=label) def inverse(self): """Invert this gate.""" @@ -49,5 +49,4 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the identity gate.""" - return numpy.array([[1, 0], - [0, 1]], dtype=dtype) + return numpy.array([[1, 0], [0, 1]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/iswap.py b/qiskit/circuit/library/standard_gates/iswap.py index ce470355a235..6e21951b3e80 100644 --- a/qiskit/circuit/library/standard_gates/iswap.py +++ b/qiskit/circuit/library/standard_gates/iswap.py @@ -77,7 +77,7 @@ class iSwapGate(Gate): def __init__(self): """Create new iSwap gate.""" - super().__init__('iswap', 2, []) + super().__init__("iswap", 2, []) def _define(self): """ @@ -95,7 +95,8 @@ def _define(self): from .h import HGate from .s import SGate from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (SGate(), [q[0]], []), @@ -103,7 +104,7 @@ def _define(self): (HGate(), [q[0]], []), (CXGate(), [q[0], q[1]], []), (CXGate(), [q[1], q[0]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -112,7 +113,4 @@ def _define(self): def __array__(self, dtype=None): """Return a numpy.array for the iSWAP gate.""" - return np.array([[1, 0, 0, 0], - [0, 0, 1j, 0], - [0, 1j, 0, 0], - [0, 0, 0, 1]], dtype=dtype) + return np.array([[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/ms.py b/qiskit/circuit/library/standard_gates/ms.py index 50f6bb679390..6681f02ea5ee 100644 --- a/qiskit/circuit/library/standard_gates/ms.py +++ b/qiskit/circuit/library/standard_gates/ms.py @@ -32,18 +32,23 @@ class MSGate(Gate): def __init__(self, num_qubits, theta, label=None): """Create new MS gate.""" - warnings.warn('The qiskit.circuit.library.standard_gates.ms import ' - 'is deprecated as of 0.16.0. You should import MSGate ' - 'using qiskit.circuit.library.generalized_gates ' - 'instead.', DeprecationWarning, stacklevel=2) - super().__init__('ms', num_qubits, [theta], label=label) + warnings.warn( + "The qiskit.circuit.library.standard_gates.ms import " + "is deprecated as of 0.16.0. You should import MSGate " + "using qiskit.circuit.library.generalized_gates " + "instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__("ms", num_qubits, [theta], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .rxx import RXXGate + theta = self.params[0] - q = QuantumRegister(self.num_qubits, 'q') + q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) for i in range(self.num_qubits): for j in range(i + 1, self.num_qubits): diff --git a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py index 22c224ae3623..4a2d25325a14 100644 --- a/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py +++ b/qiskit/circuit/library/standard_gates/multi_control_rotation_gates.py @@ -43,12 +43,12 @@ def _apply_mcu_graycode(circuit, theta, phi, lam, ctls, tgt, use_basis_gates): last_pattern = None for pattern in gray_code: - if '1' not in pattern: + if "1" not in pattern: continue if last_pattern is None: last_pattern = pattern # find left most set bit - lm_pos = list(pattern).index('1') + lm_pos = list(pattern).index("1") # find changed bit comp = [i != j for i, j in zip(pattern, last_pattern)] @@ -60,17 +60,17 @@ def _apply_mcu_graycode(circuit, theta, phi, lam, ctls, tgt, use_basis_gates): if pos != lm_pos: circuit.cx(ctls[pos], ctls[lm_pos]) else: - indices = [i for i, x in enumerate(pattern) if x == '1'] + indices = [i for i, x in enumerate(pattern) if x == "1"] for idx in indices[1:]: circuit.cx(ctls[idx], ctls[lm_pos]) # check parity and undo rotation - if pattern.count('1') % 2 == 0: + if pattern.count("1") % 2 == 0: # inverse CU: u(theta, phi, lamb)^dagger = u(-theta, -lam, -phi) - _apply_cu(circuit, -theta, -lam, -phi, ctls[lm_pos], tgt, - use_basis_gates=use_basis_gates) + _apply_cu( + circuit, -theta, -lam, -phi, ctls[lm_pos], tgt, use_basis_gates=use_basis_gates + ) else: - _apply_cu(circuit, theta, phi, lam, ctls[lm_pos], tgt, - use_basis_gates=use_basis_gates) + _apply_cu(circuit, theta, phi, lam, ctls[lm_pos], tgt, use_basis_gates=use_basis_gates) last_pattern = pattern @@ -96,13 +96,14 @@ def mcrx(self, theta, q_controls, q_target, use_basis_gates=False): control_qubits = q_controls else: raise QiskitError( - 'The mcrx gate needs a list of qubits or a quantum register for controls.') + "The mcrx gate needs a list of qubits or a quantum register for controls." + ) # check target if isinstance(q_target, Qubit): target_qubit = q_target else: - raise QiskitError('The mcrx gate needs a single qubit as target.') + raise QiskitError("The mcrx gate needs a single qubit as target.") all_qubits = control_qubits + [target_qubit] @@ -111,16 +112,29 @@ def mcrx(self, theta, q_controls, q_target, use_basis_gates=False): n_c = len(control_qubits) if n_c == 1: # cu - _apply_cu(self, theta, -pi/2, pi/2, control_qubits[0], - target_qubit, use_basis_gates=use_basis_gates) + _apply_cu( + self, + theta, + -pi / 2, + pi / 2, + control_qubits[0], + target_qubit, + use_basis_gates=use_basis_gates, + ) else: theta_step = theta * (1 / (2 ** (n_c - 1))) - _apply_mcu_graycode(self, theta_step, -pi/2, pi/2, control_qubits, - target_qubit, use_basis_gates=use_basis_gates) - - -def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, - use_basis_gates=False): + _apply_mcu_graycode( + self, + theta_step, + -pi / 2, + pi / 2, + control_qubits, + target_qubit, + use_basis_gates=use_basis_gates, + ) + + +def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, use_basis_gates=False): """ Apply Multiple-Controlled Y rotation gate @@ -143,14 +157,15 @@ def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, elif isinstance(q_controls, list): control_qubits = q_controls else: - raise QiskitError('The mcry gate needs a list of qubits or a quantum ' - 'register for controls.') + raise QiskitError( + "The mcry gate needs a list of qubits or a quantum " "register for controls." + ) # check target if isinstance(q_target, Qubit): target_qubit = q_target else: - raise QiskitError('The mcry gate needs a single qubit as target.') + raise QiskitError("The mcry gate needs a single qubit as target.") # check ancilla if q_ancillae is None: @@ -160,8 +175,9 @@ def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, elif isinstance(q_ancillae, list): ancillary_qubits = q_ancillae else: - raise QiskitError('The mcry gate needs None or a list of qubits or a ' - 'quantum register for ancilla.') + raise QiskitError( + "The mcry gate needs None or a list of qubits or a " "quantum register for ancilla." + ) all_qubits = control_qubits + [target_qubit] + ancillary_qubits @@ -171,28 +187,36 @@ def mcry(self, theta, q_controls, q_target, q_ancillae=None, mode=None, # auto-select the best mode if mode is None: # if enough ancillary qubits are provided, use the 'v-chain' method - additional_vchain = MCXGate.get_num_ancilla_qubits(len(control_qubits), 'v-chain') + additional_vchain = MCXGate.get_num_ancilla_qubits(len(control_qubits), "v-chain") if len(ancillary_qubits) >= additional_vchain: - mode = 'basic' + mode = "basic" else: - mode = 'noancilla' + mode = "noancilla" - if mode == 'basic': + if mode == "basic": self.ry(theta / 2, q_target) - self.mcx(q_controls, q_target, q_ancillae, mode='v-chain') + self.mcx(q_controls, q_target, q_ancillae, mode="v-chain") self.ry(-theta / 2, q_target) - self.mcx(q_controls, q_target, q_ancillae, mode='v-chain') - elif mode == 'noancilla': + self.mcx(q_controls, q_target, q_ancillae, mode="v-chain") + elif mode == "noancilla": n_c = len(control_qubits) if n_c == 1: # cu - _apply_cu(self, theta, 0, 0, control_qubits[0], - target_qubit, use_basis_gates=use_basis_gates) + _apply_cu( + self, theta, 0, 0, control_qubits[0], target_qubit, use_basis_gates=use_basis_gates + ) else: theta_step = theta * (1 / (2 ** (n_c - 1))) - _apply_mcu_graycode(self, theta_step, 0, 0, control_qubits, - target_qubit, use_basis_gates=use_basis_gates) + _apply_mcu_graycode( + self, + theta_step, + 0, + 0, + control_qubits, + target_qubit, + use_basis_gates=use_basis_gates, + ) else: - raise QiskitError('Unrecognized mode for building MCRY circuit: {}.'.format(mode)) + raise QiskitError("Unrecognized mode for building MCRY circuit: {}.".format(mode)) def mcrz(self, lam, q_controls, q_target, use_basis_gates=False): @@ -217,13 +241,14 @@ def mcrz(self, lam, q_controls, q_target, use_basis_gates=False): control_qubits = q_controls else: raise QiskitError( - 'The mcrz gate needs a list of qubits or a quantum register for controls.') + "The mcrz gate needs a list of qubits or a quantum register for controls." + ) # check target if isinstance(q_target, Qubit): target_qubit = q_target else: - raise QiskitError('The mcrz gate needs a single qubit as target.') + raise QiskitError("The mcrz gate needs a single qubit as target.") all_qubits = control_qubits + [target_qubit] @@ -232,12 +257,12 @@ def mcrz(self, lam, q_controls, q_target, use_basis_gates=False): n_c = len(control_qubits) if n_c == 1: # cu - _apply_cu(self, 0, 0, lam, control_qubits[0], - target_qubit, use_basis_gates=use_basis_gates) + _apply_cu(self, 0, 0, lam, control_qubits[0], target_qubit, use_basis_gates=use_basis_gates) else: lam_step = lam * (1 / (2 ** (n_c - 1))) - _apply_mcu_graycode(self, 0, 0, lam_step, control_qubits, - target_qubit, use_basis_gates=use_basis_gates) + _apply_mcu_graycode( + self, 0, 0, lam_step, control_qubits, target_qubit, use_basis_gates=use_basis_gates + ) QuantumCircuit.mcrx = mcrx diff --git a/qiskit/circuit/library/standard_gates/p.py b/qiskit/circuit/library/standard_gates/p.py index db98d4c1a4dc..03388cba5555 100644 --- a/qiskit/circuit/library/standard_gates/p.py +++ b/qiskit/circuit/library/standard_gates/p.py @@ -71,13 +71,14 @@ class PhaseGate(Gate): def __init__(self, theta, label=None): """Create new Phase gate.""" - super().__init__('p', 1, [theta], label=label) + super().__init__("p", 1, [theta], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u import UGate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) qc.append(UGate(0, 0, self.params[0]), [0]) self.definition = qc @@ -99,8 +100,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): elif ctrl_state is None and num_ctrl_qubits > 1: gate = MCPhaseGate(self.params[0], num_ctrl_qubits, label=label) else: - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, - ctrl_state=ctrl_state) + return super().control( + num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state + ) gate.base_gate.label = self.label return gate @@ -153,8 +155,15 @@ class CPhaseGate(ControlledGate): def __init__(self, theta, label=None, ctrl_state=None): """Create new CPhase gate.""" - super().__init__('cp', 2, [theta], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=PhaseGate(theta)) + super().__init__( + "cp", + 2, + [theta], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=PhaseGate(theta), + ) def _define(self): """ @@ -166,7 +175,8 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) qc.p(self.params[0] / 2, 0) qc.cx(0, 1) @@ -201,16 +211,10 @@ def __array__(self, dtype=None): """Return a numpy.array for the CPhase gate.""" eith = numpy.exp(1j * float(self.params[0])) if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, eith]], - dtype=dtype) - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, eith, 0], - [0, 0, 0, 1]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, eith]], dtype=dtype + ) + return numpy.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, eith, 0], [0, 0, 0, 1]], dtype=dtype) class MCPhaseGate(ControlledGate): @@ -240,13 +244,20 @@ class MCPhaseGate(ControlledGate): def __init__(self, lam, num_ctrl_qubits, label=None): """Create new MCPhase gate.""" - super().__init__('mcphase', num_ctrl_qubits + 1, [lam], num_ctrl_qubits=num_ctrl_qubits, - label=label, base_gate=PhaseGate(lam)) + super().__init__( + "mcphase", + num_ctrl_qubits + 1, + [lam], + num_ctrl_qubits=num_ctrl_qubits, + label=label, + base_gate=PhaseGate(lam), + ) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(self.num_qubits, 'q') + + q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) if self.num_ctrl_qubits == 0: @@ -255,6 +266,7 @@ def _define(self): qc.cp(self.params[0], 0, 1) else: from .u3 import _gray_code_chain + scaled_lam = self.params[0] / (2 ** (self.num_ctrl_qubits - 1)) bottom_gate = CPhaseGate(scaled_lam) definition = _gray_code_chain(q, self.num_ctrl_qubits, bottom_gate) @@ -274,9 +286,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): ControlledGate: controlled version of this gate. """ if ctrl_state is None: - gate = MCPhaseGate(self.params[0], - num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits, - label=label) + gate = MCPhaseGate( + self.params[0], num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits, label=label + ) gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) diff --git a/qiskit/circuit/library/standard_gates/r.py b/qiskit/circuit/library/standard_gates/r.py index 02483d8c521b..86adff2dfd99 100644 --- a/qiskit/circuit/library/standard_gates/r.py +++ b/qiskit/circuit/library/standard_gates/r.py @@ -45,7 +45,7 @@ class RGate(Gate): def __init__(self, theta, phi): """Create new r single-qubit gate.""" - super().__init__('r', 1, [theta, phi]) + super().__init__("r", 1, [theta, phi]) def _define(self): """ @@ -54,13 +54,12 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u3 import U3Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) theta = self.params[0] phi = self.params[1] - rules = [ - (U3Gate(theta, phi - pi / 2, -phi + pi / 2), [q[0]], []) - ] + rules = [(U3Gate(theta, phi - pi / 2, -phi + pi / 2), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -80,5 +79,4 @@ def __array__(self, dtype=None): sin = math.sin(theta / 2) exp_m = numpy.exp(-1j * phi) exp_p = numpy.exp(1j * phi) - return numpy.array([[cos, -1j * exp_m * sin], - [-1j * exp_p * sin, cos]], dtype=dtype) + return numpy.array([[cos, -1j * exp_m * sin], [-1j * exp_p * sin, cos]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/rx.py b/qiskit/circuit/library/standard_gates/rx.py index e006559a8bee..163484646527 100644 --- a/qiskit/circuit/library/standard_gates/rx.py +++ b/qiskit/circuit/library/standard_gates/rx.py @@ -46,7 +46,7 @@ class RXGate(Gate): def __init__(self, theta, label=None): """Create new RX gate.""" - super().__init__('rx', 1, [theta], label=label) + super().__init__("rx", 1, [theta], label=label) def _define(self): """ @@ -55,11 +55,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .r import RGate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (RGate(self.params[0], 0), [q[0]], []) - ] + rules = [(RGate(self.params[0], 0), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -94,8 +93,7 @@ def __array__(self, dtype=None): """Return a numpy.array for the RX gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) - return numpy.array([[cos, -1j * sin], - [-1j * sin, cos]], dtype=dtype) + return numpy.array([[cos, -1j * sin], [-1j * sin, cos]], dtype=dtype) class CRXGate(ControlledGate): @@ -155,9 +153,15 @@ class CRXGate(ControlledGate): def __init__(self, theta, label=None, ctrl_state=None): """Create new CRX gate.""" - super().__init__('crx', 2, [theta], num_ctrl_qubits=1, - label=label, ctrl_state=ctrl_state, - base_gate=RXGate(theta)) + super().__init__( + "crx", + 2, + [theta], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=RXGate(theta), + ) def _define(self): """ @@ -174,14 +178,15 @@ def _define(self): from .u1 import U1Gate from .u3 import U3Gate from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (U1Gate(pi / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, 0), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (U3Gate(self.params[0] / 2, -pi / 2, 0), [q[1]], []) + (U3Gate(self.params[0] / 2, -pi / 2, 0), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -198,14 +203,10 @@ def __array__(self, dtype=None): cos = numpy.cos(half_theta) isin = 1j * numpy.sin(half_theta) if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, cos, 0, -isin], - [0, 0, 1, 0], - [0, -isin, 0, cos]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, cos, 0, -isin], [0, 0, 1, 0], [0, -isin, 0, cos]], dtype=dtype + ) else: - return numpy.array([[cos, 0, -isin, 0], - [0, 1, 0, 0], - [-isin, 0, cos, 0], - [0, 0, 0, 1]], - dtype=dtype) + return numpy.array( + [[cos, 0, -isin, 0], [0, 1, 0, 0], [-isin, 0, cos, 0], [0, 0, 0, 1]], dtype=dtype + ) diff --git a/qiskit/circuit/library/standard_gates/rxx.py b/qiskit/circuit/library/standard_gates/rxx.py index 0ecd0035502e..7208a06cfe78 100644 --- a/qiskit/circuit/library/standard_gates/rxx.py +++ b/qiskit/circuit/library/standard_gates/rxx.py @@ -68,7 +68,7 @@ class RXXGate(Gate): def __init__(self, theta): """Create new RXX gate.""" - super().__init__('rxx', 2, [theta]) + super().__init__("rxx", 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" @@ -77,8 +77,9 @@ def _define(self): from .x import CXGate from .h import HGate from .rz import RZGate + theta = self.params[0] - q = QuantumRegister(2, 'q') + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (HGate(), [q[0]], []), @@ -101,11 +102,11 @@ def inverse(self): def __array__(self, dtype=None): """Return a Numpy.array for the RXX gate.""" import numpy + theta2 = float(self.params[0]) / 2 cos = numpy.cos(theta2) isin = 1j * numpy.sin(theta2) - return numpy.array([ - [cos, 0, 0, -isin], - [0, cos, -isin, 0], - [0, -isin, cos, 0], - [-isin, 0, 0, cos]], dtype=dtype) + return numpy.array( + [[cos, 0, 0, -isin], [0, cos, -isin, 0], [0, -isin, cos, 0], [-isin, 0, 0, cos]], + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/ry.py b/qiskit/circuit/library/standard_gates/ry.py index 0a0ee8bda7d1..09055f347eb1 100644 --- a/qiskit/circuit/library/standard_gates/ry.py +++ b/qiskit/circuit/library/standard_gates/ry.py @@ -46,7 +46,7 @@ class RYGate(Gate): def __init__(self, theta, label=None): """Create new RY gate.""" - super().__init__('ry', 1, [theta], label=label) + super().__init__("ry", 1, [theta], label=label) def _define(self): """ @@ -55,11 +55,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .r import RGate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (RGate(self.params[0], pi / 2), [q[0]], []) - ] + rules = [(RGate(self.params[0], pi / 2), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -94,8 +93,7 @@ def __array__(self, dtype=None): """Return a numpy.array for the RY gate.""" cos = math.cos(self.params[0] / 2) sin = math.sin(self.params[0] / 2) - return numpy.array([[cos, -sin], - [sin, cos]], dtype=dtype) + return numpy.array([[cos, -sin], [sin, cos]], dtype=dtype) class CRYGate(ControlledGate): @@ -155,8 +153,15 @@ class CRYGate(ControlledGate): def __init__(self, theta, label=None, ctrl_state=None): """Create new CRY gate.""" - super().__init__('cry', 2, [theta], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=RYGate(theta)) + super().__init__( + "cry", + 2, + [theta], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=RYGate(theta), + ) def _define(self): """ @@ -168,13 +173,14 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (RYGate(self.params[0] / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), (RYGate(-self.params[0] / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -191,14 +197,10 @@ def __array__(self, dtype=None): cos = numpy.cos(half_theta) sin = numpy.sin(half_theta) if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, cos, 0, -sin], - [0, 0, 1, 0], - [0, sin, 0, cos]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, cos, 0, -sin], [0, 0, 1, 0], [0, sin, 0, cos]], dtype=dtype + ) else: - return numpy.array([[cos, 0, -sin, 0], - [0, 1, 0, 0], - [sin, 0, cos, 0], - [0, 0, 0, 1]], - dtype=dtype) + return numpy.array( + [[cos, 0, -sin, 0], [0, 1, 0, 0], [sin, 0, cos, 0], [0, 0, 0, 1]], dtype=dtype + ) diff --git a/qiskit/circuit/library/standard_gates/ryy.py b/qiskit/circuit/library/standard_gates/ryy.py index bc1b90bbf9b3..cdb284cee95d 100644 --- a/qiskit/circuit/library/standard_gates/ryy.py +++ b/qiskit/circuit/library/standard_gates/ryy.py @@ -69,7 +69,7 @@ class RYYGate(Gate): def __init__(self, theta): """Create new RYY gate.""" - super().__init__('ryy', 2, [theta]) + super().__init__("ryy", 2, [theta]) def _define(self): """Calculate a subcircuit that implements this unitary.""" @@ -79,7 +79,7 @@ def _define(self): from .rx import RXGate from .rz import RZGate - q = QuantumRegister(2, 'q') + q = QuantumRegister(2, "q") theta = self.params[0] qc = QuantumCircuit(q, name=self.name) rules = [ @@ -105,9 +105,7 @@ def __array__(self, dtype=None): theta = float(self.params[0]) cos = np.cos(theta / 2) isin = 1j * np.sin(theta / 2) - return np.array([ - [cos, 0, 0, isin], - [0, cos, -isin, 0], - [0, -isin, cos, 0], - [isin, 0, 0, cos] - ], dtype=dtype) + return np.array( + [[cos, 0, 0, isin], [0, cos, -isin, 0], [0, -isin, cos, 0], [isin, 0, 0, cos]], + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/rz.py b/qiskit/circuit/library/standard_gates/rz.py index 3dda6ab77ff3..141b27f62172 100644 --- a/qiskit/circuit/library/standard_gates/rz.py +++ b/qiskit/circuit/library/standard_gates/rz.py @@ -56,7 +56,7 @@ class RZGate(Gate): def __init__(self, phi, label=None): """Create new RZ gate.""" - super().__init__('rz', 1, [phi], label=label) + super().__init__("rz", 1, [phi], label=label) def _define(self): """ @@ -65,12 +65,11 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") theta = self.params[0] qc = QuantumCircuit(q, name=self.name, global_phase=-theta / 2) - rules = [ - (U1Gate(theta), [q[0]], []) - ] + rules = [(U1Gate(theta), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -104,9 +103,9 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the RZ gate.""" import numpy as np + ilam2 = 0.5j * float(self.params[0]) - return np.array([[np.exp(-ilam2), 0], - [0, np.exp(ilam2)]], dtype=dtype) + return np.array([[np.exp(-ilam2), 0], [0, np.exp(ilam2)]], dtype=dtype) class CRZGate(ControlledGate): @@ -172,8 +171,15 @@ class CRZGate(ControlledGate): def __init__(self, theta, label=None, ctrl_state=None): """Create new CRZ gate.""" - super().__init__('crz', 2, [theta], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=RZGate(theta)) + super().__init__( + "crz", + 2, + [theta], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=RZGate(theta), + ) def _define(self): """ @@ -185,13 +191,14 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (RZGate(self.params[0] / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), (RZGate(-self.params[0] / 2), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -205,16 +212,15 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the CRZ gate.""" import numpy + arg = 1j * float(self.params[0]) / 2 if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, numpy.exp(-arg), 0, 0], - [0, 0, 1, 0], - [0, 0, 0, numpy.exp(arg)]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, numpy.exp(-arg), 0, 0], [0, 0, 1, 0], [0, 0, 0, numpy.exp(arg)]], + dtype=dtype, + ) else: - return numpy.array([[numpy.exp(-arg), 0, 0, 0], - [0, 1, 0, 0], - [0, 0, numpy.exp(arg), 0], - [0, 0, 0, 1]], - dtype=dtype) + return numpy.array( + [[numpy.exp(-arg), 0, 0, 0], [0, 1, 0, 0], [0, 0, numpy.exp(arg), 0], [0, 0, 0, 1]], + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/rzx.py b/qiskit/circuit/library/standard_gates/rzx.py index 594c9e6c6a7f..bb03ab04cf28 100644 --- a/qiskit/circuit/library/standard_gates/rzx.py +++ b/qiskit/circuit/library/standard_gates/rzx.py @@ -114,7 +114,7 @@ class RZXGate(Gate): def __init__(self, theta): """Create new RZX gate.""" - super().__init__('rzx', 2, [theta]) + super().__init__("rzx", 2, [theta]) def _define(self): """ @@ -125,15 +125,16 @@ def _define(self): from .h import HGate from .x import CXGate from .rz import RZGate + theta = self.params[0] - q = QuantumRegister(2, 'q') + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), (RZGate(theta), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) + (HGate(), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -147,11 +148,11 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the RZX gate.""" import numpy + half_theta = float(self.params[0]) / 2 cos = numpy.cos(half_theta) isin = 1j * numpy.sin(half_theta) - return numpy.array([[cos, 0, -isin, 0], - [0, cos, 0, isin], - [-isin, 0, cos, 0], - [0, isin, 0, cos]], - dtype=dtype) + return numpy.array( + [[cos, 0, -isin, 0], [0, cos, 0, isin], [-isin, 0, cos, 0], [0, isin, 0, cos]], + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/rzz.py b/qiskit/circuit/library/standard_gates/rzz.py index 1abad7f87205..062c8f5e7d2f 100644 --- a/qiskit/circuit/library/standard_gates/rzz.py +++ b/qiskit/circuit/library/standard_gates/rzz.py @@ -81,7 +81,7 @@ class RZZGate(Gate): def __init__(self, theta): """Create new RZZ gate.""" - super().__init__('rzz', 2, [theta]) + super().__init__("rzz", 2, [theta]) def _define(self): """ @@ -91,13 +91,14 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate from .rz import RZGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") theta = self.params[0] qc = QuantumCircuit(q, name=self.name) rules = [ (CXGate(), [q[0], q[1]], []), (RZGate(theta), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -111,8 +112,14 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the RZZ gate.""" import numpy + itheta2 = 1j * float(self.params[0]) / 2 - return numpy.array([[numpy.exp(-itheta2), 0, 0, 0], - [0, numpy.exp(itheta2), 0, 0], - [0, 0, numpy.exp(itheta2), 0], - [0, 0, 0, numpy.exp(-itheta2)]], dtype=dtype) + return numpy.array( + [ + [numpy.exp(-itheta2), 0, 0, 0], + [0, numpy.exp(itheta2), 0, 0], + [0, 0, numpy.exp(itheta2), 0], + [0, 0, 0, numpy.exp(-itheta2)], + ], + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/s.py b/qiskit/circuit/library/standard_gates/s.py index c14dfcf51b7c..d7890f27513f 100644 --- a/qiskit/circuit/library/standard_gates/s.py +++ b/qiskit/circuit/library/standard_gates/s.py @@ -47,7 +47,7 @@ class SGate(Gate): def __init__(self, label=None): """Create new S gate.""" - super().__init__('s', 1, [], label=label) + super().__init__("s", 1, [], label=label) def _define(self): """ @@ -56,11 +56,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U1Gate(pi / 2), [q[0]], []) - ] + rules = [(U1Gate(pi / 2), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -72,8 +71,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the S gate.""" - return numpy.array([[1, 0], - [0, 1j]], dtype=dtype) + return numpy.array([[1, 0], [0, 1j]], dtype=dtype) class SdgGate(Gate): @@ -105,7 +103,7 @@ class SdgGate(Gate): def __init__(self, label=None): """Create new Sdg gate.""" - super().__init__('sdg', 1, [], label=label) + super().__init__("sdg", 1, [], label=label) def _define(self): """ @@ -114,11 +112,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U1Gate(-pi / 2), [q[0]], []) - ] + rules = [(U1Gate(-pi / 2), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -130,5 +127,4 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the Sdg gate.""" - return numpy.array([[1, 0], - [0, -1j]], dtype=dtype) + return numpy.array([[1, 0], [0, -1j]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/swap.py b/qiskit/circuit/library/standard_gates/swap.py index 624d64ad51b9..bdef0799d20a 100644 --- a/qiskit/circuit/library/standard_gates/swap.py +++ b/qiskit/circuit/library/standard_gates/swap.py @@ -52,7 +52,7 @@ class SwapGate(Gate): def __init__(self, label=None): """Create new SWAP gate.""" - super().__init__('swap', 2, [], label=label) + super().__init__("swap", 2, [], label=label) def _define(self): """ @@ -61,12 +61,13 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (CXGate(), [q[0], q[1]], []), (CXGate(), [q[1], q[0]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -99,10 +100,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the SWAP gate.""" - return numpy.array([[1, 0, 0, 0], - [0, 0, 1, 0], - [0, 1, 0, 0], - [0, 0, 0, 1]], dtype=dtype) + return numpy.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=dtype) class CSwapGate(ControlledGate): @@ -178,27 +176,42 @@ class CSwapGate(ControlledGate): |1, b, c\rangle \rightarrow |1, c, b\rangle """ # Define class constants. This saves future allocation time. - _matrix1 = numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 1]]) - _matrix0 = numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 1, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, 1]]) + _matrix1 = numpy.array( + [ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1], + ] + ) + _matrix0 = numpy.array( + [ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 1, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, 1], + ] + ) def __init__(self, label=None, ctrl_state=None): """Create new CSWAP gate.""" - super().__init__('cswap', 3, [], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=SwapGate()) + super().__init__( + "cswap", + 3, + [], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=SwapGate(), + ) def _define(self): """ @@ -211,12 +224,13 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate, CCXGate - q = QuantumRegister(3, 'q') + + q = QuantumRegister(3, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (CXGate(), [q[2], q[1]], []), (CCXGate(), [q[0], q[1], q[2]], []), - (CXGate(), [q[2], q[1]], []) + (CXGate(), [q[2], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) diff --git a/qiskit/circuit/library/standard_gates/sx.py b/qiskit/circuit/library/standard_gates/sx.py index d821de758166..f5242cb732a8 100644 --- a/qiskit/circuit/library/standard_gates/sx.py +++ b/qiskit/circuit/library/standard_gates/sx.py @@ -56,7 +56,7 @@ class SXGate(Gate): def __init__(self, label=None): """Create new SX gate.""" - super().__init__('sx', 1, [], label=label) + super().__init__("sx", 1, [], label=label) def _define(self): """ @@ -66,13 +66,10 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .s import SdgGate from .h import HGate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name, global_phase=pi / 4) - rules = [ - (SdgGate(), [q[0]], []), - (HGate(), [q[0]], []), - (SdgGate(), [q[0]], []) - ] + rules = [(SdgGate(), [q[0]], []), (HGate(), [q[0]], []), (SdgGate(), [q[0]], [])] qc.data = rules self.definition = qc @@ -102,8 +99,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): def __array__(self, dtype=None): """Return a numpy.array for the SX gate.""" - return numpy.array([[1 + 1j, 1 - 1j], - [1 - 1j, 1 + 1j]], dtype=dtype) / 2 + return numpy.array([[1 + 1j, 1 - 1j], [1 - 1j, 1 + 1j]], dtype=dtype) / 2 class SXdgGate(Gate): @@ -134,7 +130,7 @@ class SXdgGate(Gate): def __init__(self, label=None): """Create new SXdg gate.""" - super().__init__('sxdg', 1, [], label=label) + super().__init__("sxdg", 1, [], label=label) def _define(self): """ @@ -144,13 +140,10 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .s import SGate from .h import HGate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name, global_phase=-pi / 4) - rules = [ - (SGate(), [q[0]], []), - (HGate(), [q[0]], []), - (SGate(), [q[0]], []) - ] + rules = [(SGate(), [q[0]], []), (HGate(), [q[0]], []), (SGate(), [q[0]], [])] qc.data = rules self.definition = qc @@ -160,8 +153,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the SXdg gate.""" - return numpy.array([[1 - 1j, 1 + 1j], - [1 + 1j, 1 - 1j]], dtype=dtype) / 2 + return numpy.array([[1 - 1j, 1 + 1j], [1 + 1j, 1 - 1j]], dtype=dtype) / 2 class CSXGate(ControlledGate): @@ -217,19 +209,28 @@ class CSXGate(ControlledGate): """ # Define class constants. This saves future allocation time. - _matrix1 = numpy.array([[1, 0, 0, 0], - [0, (1 + 1j) / 2, 0, (1 - 1j) / 2], - [0, 0, 1, 0], - [0, (1 - 1j) / 2, 0, (1 + 1j) / 2]]) - _matrix0 = numpy.array([[(1 + 1j) / 2, 0, (1 - 1j) / 2, 0], - [0, 1, 0, 0], - [(1 - 1j) / 2, 0, (1 + 1j) / 2, 0], - [0, 0, 0, 1]]) + _matrix1 = numpy.array( + [ + [1, 0, 0, 0], + [0, (1 + 1j) / 2, 0, (1 - 1j) / 2], + [0, 0, 1, 0], + [0, (1 - 1j) / 2, 0, (1 + 1j) / 2], + ] + ) + _matrix0 = numpy.array( + [ + [(1 + 1j) / 2, 0, (1 - 1j) / 2, 0], + [0, 1, 0, 0], + [(1 - 1j) / 2, 0, (1 + 1j) / 2, 0], + [0, 0, 0, 1], + ] + ) def __init__(self, label=None, ctrl_state=None): """Create new CSX gate.""" - super().__init__('csx', 2, [], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=SXGate()) + super().__init__( + "csx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=SXGate() + ) def _define(self): """ @@ -239,13 +240,10 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .h import HGate from .u1 import CU1Gate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (HGate(), [q[1]], []), - (CU1Gate(pi/2), [q[0], q[1]], []), - (HGate(), [q[1]], []) - ] + rules = [(HGate(), [q[1]], []), (CU1Gate(pi / 2), [q[0], q[1]], []), (HGate(), [q[1]], [])] qc.data = rules self.definition = qc diff --git a/qiskit/circuit/library/standard_gates/t.py b/qiskit/circuit/library/standard_gates/t.py index 036d60992539..feee34cea7a5 100644 --- a/qiskit/circuit/library/standard_gates/t.py +++ b/qiskit/circuit/library/standard_gates/t.py @@ -48,7 +48,7 @@ class TGate(Gate): def __init__(self, label=None): """Create new T gate.""" - super().__init__('t', 1, [], label=label) + super().__init__("t", 1, [], label=label) def _define(self): """ @@ -57,11 +57,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U1Gate(pi / 4), [q[0]], []) - ] + rules = [(U1Gate(pi / 4), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -73,8 +72,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the T gate.""" - return numpy.array([[1, 0], - [0, (1 + 1j) / numpy.sqrt(2)]], dtype=dtype) + return numpy.array([[1, 0], [0, (1 + 1j) / numpy.sqrt(2)]], dtype=dtype) class TdgGate(Gate): @@ -106,7 +104,7 @@ class TdgGate(Gate): def __init__(self, label=None): """Create new Tdg gate.""" - super().__init__('tdg', 1, [], label=label) + super().__init__("tdg", 1, [], label=label) def _define(self): """ @@ -115,11 +113,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U1Gate(-pi / 4), [q[0]], []) - ] + rules = [(U1Gate(-pi / 4), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -131,5 +128,4 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the inverse T gate.""" - return numpy.array([[1, 0], - [0, (1 - 1j) / numpy.sqrt(2)]], dtype=dtype) + return numpy.array([[1, 0], [0, (1 - 1j) / numpy.sqrt(2)]], dtype=dtype) diff --git a/qiskit/circuit/library/standard_gates/u.py b/qiskit/circuit/library/standard_gates/u.py index 26595ddc6f66..ba7ee4393772 100644 --- a/qiskit/circuit/library/standard_gates/u.py +++ b/qiskit/circuit/library/standard_gates/u.py @@ -61,7 +61,7 @@ class UGate(Gate): def __init__(self, theta, phi, lam, label=None): """Create new U gate.""" - super().__init__('u', 1, [theta, phi, lam], label=label) + super().__init__("u", 1, [theta, phi, lam], label=label) def inverse(self): r"""Return inverted U gate. @@ -83,8 +83,14 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): ControlledGate: controlled version of this gate. """ if num_ctrl_qubits == 1: - gate = CUGate(self.params[0], self.params[1], self.params[2], 0, - label=label, ctrl_state=ctrl_state) + gate = CUGate( + self.params[0], + self.params[1], + self.params[2], + 0, + label=label, + ctrl_state=ctrl_state, + ) gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -92,16 +98,16 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): def __array__(self, dtype=None): """Return a numpy.array for the U gate.""" theta, phi, lam = [float(param) for param in self.params] - return numpy.array([ + return numpy.array( [ - numpy.cos(theta / 2), - -numpy.exp(1j * lam) * numpy.sin(theta / 2) + [numpy.cos(theta / 2), -numpy.exp(1j * lam) * numpy.sin(theta / 2)], + [ + numpy.exp(1j * phi) * numpy.sin(theta / 2), + numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2), + ], ], - [ - numpy.exp(1j * phi) * numpy.sin(theta / 2), - numpy.exp(1j * (phi + lam)) * numpy.cos(theta / 2) - ] - ], dtype=dtype) + dtype=dtype, + ) class CUGate(ControlledGate): @@ -164,9 +170,15 @@ class CUGate(ControlledGate): def __init__(self, theta, phi, lam, gamma, label=None, ctrl_state=None): """Create new CU gate.""" - super().__init__('cu', 2, [theta, phi, lam, gamma], num_ctrl_qubits=1, - label=label, ctrl_state=ctrl_state, - base_gate=UGate(theta, phi, lam)) + super().__init__( + "cu", + 2, + [theta, phi, lam, gamma], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=UGate(theta, phi, lam), + ) def _define(self): """ @@ -182,7 +194,8 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) qc.p(self.params[3], 0) qc.p((self.params[2] + self.params[1]) / 2, 0) @@ -203,7 +216,7 @@ def inverse(self): -self.params[2], -self.params[1], -self.params[3], - ctrl_state=self.ctrl_state + ctrl_state=self.ctrl_state, ) def __array__(self, dtype=None): @@ -216,15 +229,13 @@ def __array__(self, dtype=None): c = numpy.exp(1j * (gamma + phi)) * sin d = numpy.exp(1j * (gamma + phi + lam)) * cos if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, a, 0, b], - [0, 0, 1, 0], - [0, c, 0, d]], dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, a, 0, b], [0, 0, 1, 0], [0, c, 0, d]], dtype=dtype + ) else: - return numpy.array([[a, 0, b, 0], - [0, 1, 0, 0], - [c, 0, d, 0], - [0, 0, 0, 1]], dtype=dtype) + return numpy.array( + [[a, 0, b, 0], [0, 1, 0, 0], [c, 0, d, 0], [0, 0, 0, 1]], dtype=dtype + ) @property def params(self): @@ -240,8 +251,7 @@ def params(self): # CU has one additional parameter to the U base gate return self.base_gate.params + self._params else: - raise CircuitError('Controlled gate does not define base gate ' - 'for extracting params') + raise CircuitError("Controlled gate does not define base gate " "for extracting params") @params.setter def params(self, parameters): @@ -258,5 +268,4 @@ def params(self, parameters): if self.base_gate: self.base_gate.params = parameters[:-1] else: - raise CircuitError('Controlled gate does not define base gate ' - 'for extracting params') + raise CircuitError("Controlled gate does not define base gate " "for extracting params") diff --git a/qiskit/circuit/library/standard_gates/u1.py b/qiskit/circuit/library/standard_gates/u1.py index 981aace32fc7..7a303882dc6e 100644 --- a/qiskit/circuit/library/standard_gates/u1.py +++ b/qiskit/circuit/library/standard_gates/u1.py @@ -76,17 +76,16 @@ class U1Gate(Gate): def __init__(self, theta, label=None): """Create new U1 gate.""" - super().__init__('u1', 1, [theta], label=label) + super().__init__("u1", 1, [theta], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u3 import U3Gate # pylint: disable=cyclic-import - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U3Gate(0, 0, self.params[0]), [q[0]], []) - ] + rules = [(U3Gate(0, 0, self.params[0]), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -109,8 +108,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): elif ctrl_state is None and num_ctrl_qubits > 1: gate = MCU1Gate(self.params[0], num_ctrl_qubits, label=label) else: - return super().control(num_ctrl_qubits=num_ctrl_qubits, label=label, - ctrl_state=ctrl_state) + return super().control( + num_ctrl_qubits=num_ctrl_qubits, label=label, ctrl_state=ctrl_state + ) gate.base_gate.label = self.label return gate @@ -163,8 +163,15 @@ class CU1Gate(ControlledGate): def __init__(self, theta, label=None, ctrl_state=None): """Create new CU1 gate.""" - super().__init__('cu1', 2, [theta], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=U1Gate(theta)) + super().__init__( + "cu1", + 2, + [theta], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=U1Gate(theta), + ) def _define(self): """ @@ -177,14 +184,15 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .x import CXGate # pylint: disable=cyclic-import - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (U1Gate(self.params[0] / 2), [q[0]], []), (CXGate(), [q[0], q[1]], []), (U1Gate(-self.params[0] / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (U1Gate(self.params[0] / 2), [q[1]], []) + (U1Gate(self.params[0] / 2), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -217,17 +225,13 @@ def __array__(self, dtype=None): """Return a numpy.array for the CU1 gate.""" eith = numpy.exp(1j * float(self.params[0])) if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, eith]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, eith]], dtype=dtype + ) else: - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, eith, 0], - [0, 0, 0, 1]], - dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, eith, 0], [0, 0, 0, 1]], dtype=dtype + ) class MCU1Gate(ControlledGate): @@ -257,13 +261,21 @@ class MCU1Gate(ControlledGate): def __init__(self, lam, num_ctrl_qubits, label=None, ctrl_state=None): """Create new MCU1 gate.""" - super().__init__('mcu1', num_ctrl_qubits + 1, [lam], num_ctrl_qubits=num_ctrl_qubits, - label=label, ctrl_state=ctrl_state, base_gate=U1Gate(lam)) + super().__init__( + "mcu1", + num_ctrl_qubits + 1, + [lam], + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + base_gate=U1Gate(lam), + ) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(self.num_qubits, 'q') + + q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) if self.num_ctrl_qubits == 0: @@ -272,6 +284,7 @@ def _define(self): definition = CU1Gate(self.params[0]).definition else: from .u3 import _gray_code_chain + scaled_lam = self.params[0] / (2 ** (self.num_ctrl_qubits - 1)) bottom_gate = CU1Gate(scaled_lam) definition = _gray_code_chain(q, self.num_ctrl_qubits, bottom_gate) @@ -293,8 +306,12 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits) new_ctrl_state = (self.ctrl_state << num_ctrl_qubits) | ctrl_state - gate = MCU1Gate(self.params[0], num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits, - label=label, ctrl_state=new_ctrl_state) + gate = MCU1Gate( + self.params[0], + num_ctrl_qubits=num_ctrl_qubits + self.num_ctrl_qubits, + label=label, + ctrl_state=new_ctrl_state, + ) gate.base_gate.label = self.label return gate diff --git a/qiskit/circuit/library/standard_gates/u2.py b/qiskit/circuit/library/standard_gates/u2.py index 0cb5e8187352..4be1e3b86621 100644 --- a/qiskit/circuit/library/standard_gates/u2.py +++ b/qiskit/circuit/library/standard_gates/u2.py @@ -60,13 +60,14 @@ class U2Gate(Gate): def __init__(self, phi, lam, label=None): """Create new U2 gate.""" - super().__init__('u2', 1, [phi, lam], label=label) + super().__init__("u2", 1, [phi, lam], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u3 import U3Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) rules = [(U3Gate(pi / 2, self.params[0], self.params[1]), [q[0]], [])] for instr, qargs, cargs in rules: @@ -86,13 +87,10 @@ def __array__(self, dtype=None): isqrt2 = 1 / numpy.sqrt(2) phi, lam = self.params phi, lam = float(phi), float(lam) - return numpy.array([ + return numpy.array( [ - isqrt2, - -numpy.exp(1j * lam) * isqrt2 + [isqrt2, -numpy.exp(1j * lam) * isqrt2], + [numpy.exp(1j * phi) * isqrt2, numpy.exp(1j * (phi + lam)) * isqrt2], ], - [ - numpy.exp(1j * phi) * isqrt2, - numpy.exp(1j * (phi + lam)) * isqrt2 - ] - ], dtype=dtype) + dtype=dtype, + ) diff --git a/qiskit/circuit/library/standard_gates/u3.py b/qiskit/circuit/library/standard_gates/u3.py index a15ccc516638..9145978fc308 100644 --- a/qiskit/circuit/library/standard_gates/u3.py +++ b/qiskit/circuit/library/standard_gates/u3.py @@ -60,7 +60,7 @@ class U3Gate(Gate): def __init__(self, theta, phi, lam, label=None): """Create new U3 gate.""" - super().__init__('u3', 1, [theta, phi, lam], label=label) + super().__init__("u3", 1, [theta, phi, lam], label=label) def inverse(self): r"""Return inverted U3 gate. @@ -89,7 +89,8 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) qc.u(self.params[0], self.params[1], self.params[2], 0) self.definition = qc @@ -100,10 +101,13 @@ def __array__(self, dtype=None): theta, phi, lam = float(theta), float(phi), float(lam) cos = numpy.cos(theta / 2) sin = numpy.sin(theta / 2) - return numpy.array([ - [cos, -numpy.exp(1j * lam) * sin], - [numpy.exp(1j * phi) * sin, numpy.exp(1j * (phi + lam)) * cos] - ], dtype=dtype) + return numpy.array( + [ + [cos, -numpy.exp(1j * lam) * sin], + [numpy.exp(1j * phi) * sin, numpy.exp(1j * (phi + lam)) * cos], + ], + dtype=dtype, + ) class CU3Gate(ControlledGate): @@ -167,9 +171,15 @@ class CU3Gate(ControlledGate): def __init__(self, theta, phi, lam, label=None, ctrl_state=None): """Create new CU3 gate.""" - super().__init__('cu3', 2, [theta, phi, lam], num_ctrl_qubits=1, - label=label, ctrl_state=ctrl_state, - base_gate=U3Gate(theta, phi, lam)) + super().__init__( + "cu3", + 2, + [theta, phi, lam], + num_ctrl_qubits=1, + label=label, + ctrl_state=ctrl_state, + base_gate=U3Gate(theta, phi, lam), + ) def _define(self): """ @@ -186,7 +196,8 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate from .x import CXGate # pylint: disable=cyclic-import - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (U1Gate((self.params[2] + self.params[1]) / 2), [q[0]], []), @@ -194,7 +205,7 @@ def _define(self): (CXGate(), [q[0], q[1]], []), (U3Gate(-self.params[0] / 2, 0, -(self.params[1] + self.params[2]) / 2), [q[1]], []), (CXGate(), [q[0], q[1]], []), - (U3Gate(self.params[0] / 2, self.params[1], 0), [q[1]], []) + (U3Gate(self.params[0] / 2, self.params[1], 0), [q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -207,10 +218,7 @@ def inverse(self): :math:`CU3(\theta,\phi,\lambda)^{\dagger} =CU3(-\theta,-\phi,-\lambda)`) """ return CU3Gate( - -self.params[0], - -self.params[2], - -self.params[1], - ctrl_state=self.ctrl_state + -self.params[0], -self.params[2], -self.params[1], ctrl_state=self.ctrl_state ) def __array__(self, dtype=None): @@ -221,28 +229,34 @@ def __array__(self, dtype=None): sin = numpy.sin(theta / 2) if self.ctrl_state: return numpy.array( - [[1, 0, 0, 0], - [0, cos, 0, -numpy.exp(1j * lam) * sin], - [0, 0, 1, 0], - [0, numpy.exp(1j * phi) * sin, 0, numpy.exp(1j * (phi+lam)) * cos]], - dtype=dtype) + [ + [1, 0, 0, 0], + [0, cos, 0, -numpy.exp(1j * lam) * sin], + [0, 0, 1, 0], + [0, numpy.exp(1j * phi) * sin, 0, numpy.exp(1j * (phi + lam)) * cos], + ], + dtype=dtype, + ) else: return numpy.array( - [[cos, 0, -numpy.exp(1j * lam) * sin, 0], - [0, 1, 0, 0], - [numpy.exp(1j * phi) * sin, 0, numpy.exp(1j * (phi+lam)) * cos, 0], - [0, 0, 0, 1]], - dtype=dtype) + [ + [cos, 0, -numpy.exp(1j * lam) * sin, 0], + [0, 1, 0, 0], + [numpy.exp(1j * phi) * sin, 0, numpy.exp(1j * (phi + lam)) * cos, 0], + [0, 0, 0, 1], + ], + dtype=dtype, + ) def _generate_gray_code(num_bits): """Generate the gray code for ``num_bits`` bits.""" if num_bits <= 0: - raise ValueError('Cannot generate the gray code for less than 1 bit.') + raise ValueError("Cannot generate the gray code for less than 1 bit.") result = [0] for i in range(num_bits): - result += [x + 2**i for x in reversed(result)] - return [format(x, '0%sb' % num_bits) for x in result] + result += [x + 2 ** i for x in reversed(result)] + return [format(x, "0%sb" % num_bits) for x in result] def _gray_code_chain(q, num_ctrl_qubits, gate): @@ -260,12 +274,12 @@ def _gray_code_chain(q, num_ctrl_qubits, gate): last_pattern = None for pattern in gray_code: - if '1' not in pattern: + if "1" not in pattern: continue if last_pattern is None: last_pattern = pattern # find left most set bit - lm_pos = list(pattern).index('1') + lm_pos = list(pattern).index("1") # find changed bit comp = [i != j for i, j in zip(pattern, last_pattern)] @@ -275,25 +289,17 @@ def _gray_code_chain(q, num_ctrl_qubits, gate): pos = None if pos is not None: if pos != lm_pos: - rule.append( - (CXGate(), [q_controls[pos], q_controls[lm_pos]], []) - ) + rule.append((CXGate(), [q_controls[pos], q_controls[lm_pos]], [])) else: - indices = [i for i, x in enumerate(pattern) if x == '1'] + indices = [i for i, x in enumerate(pattern) if x == "1"] for idx in indices[1:]: - rule.append( - (CXGate(), [q_controls[idx], q_controls[lm_pos]], []) - ) + rule.append((CXGate(), [q_controls[idx], q_controls[lm_pos]], [])) # check parity - if pattern.count('1') % 2 == 0: + if pattern.count("1") % 2 == 0: # inverse - rule.append( - (gate.inverse(), [q_controls[lm_pos], q_target], []) - ) + rule.append((gate.inverse(), [q_controls[lm_pos], q_target], [])) else: - rule.append( - (gate, [q_controls[lm_pos], q_target], []) - ) + rule.append((gate, [q_controls[lm_pos], q_target], [])) last_pattern = pattern return rule diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index 0ddc6da62e6b..6fc48538ff12 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -72,7 +72,7 @@ class XGate(Gate): def __init__(self, label=None): """Create new X gate.""" - super().__init__('x', 1, [], label=label) + super().__init__("x", 1, [], label=label) def _define(self): """ @@ -81,11 +81,10 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u3 import U3Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U3Gate(pi, 0, pi), [q[0]], []) - ] + rules = [(U3Gate(pi, 0, pi), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -115,8 +114,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the X gate.""" - return numpy.array([[0, 1], - [1, 0]], dtype=dtype) + return numpy.array([[0, 1], [1, 0]], dtype=dtype) class CXGate(ControlledGate): @@ -180,8 +178,9 @@ class CXGate(ControlledGate): def __init__(self, label=None, ctrl_state=None): """Create new CX gate.""" - super().__init__('cx', 2, [], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__( + "cx", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=XGate() + ) def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """Return a controlled-X gate with more control lines. @@ -208,15 +207,13 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the CX gate.""" if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - [0, 1, 0, 0]], dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=dtype + ) else: - return numpy.array([[0, 0, 1, 0], - [0, 1, 0, 0], - [1, 0, 0, 0], - [0, 0, 0, 1]], dtype=dtype) + return numpy.array( + [[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0], [0, 0, 0, 1]], dtype=dtype + ) class CCXGate(ControlledGate): @@ -285,8 +282,9 @@ class CCXGate(ControlledGate): def __init__(self, label=None, ctrl_state=None): """Create new CCX gate.""" - super().__init__('ccx', 3, [], num_ctrl_qubits=2, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__( + "ccx", 3, [], num_ctrl_qubits=2, label=label, ctrl_state=ctrl_state, base_gate=XGate() + ) def _define(self): """ @@ -299,7 +297,8 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(3, 'q') + + q = QuantumRegister(3, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (HGate(), [q[2]], []), @@ -316,7 +315,7 @@ def _define(self): (CXGate(), [q[0], q[1]], []), (TGate(), [q[0]], []), (TdgGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []) + (CXGate(), [q[0], q[1]], []), ] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -347,9 +346,9 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the CCX gate.""" - mat = _compute_control_matrix(self.base_gate.to_matrix(), - self.num_ctrl_qubits, - ctrl_state=self.ctrl_state) + mat = _compute_control_matrix( + self.base_gate.to_matrix(), self.num_ctrl_qubits, ctrl_state=self.ctrl_state + ) if dtype: return numpy.asarray(mat, dtype=dtype) return mat @@ -370,7 +369,7 @@ class RCCXGate(Gate): def __init__(self, label=None): """Create a new simplified CCX gate.""" - super().__init__('rccx', 3, [], label=label) + super().__init__("rccx", 3, [], label=label) def _define(self): """ @@ -388,7 +387,8 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(3, 'q') + + q = QuantumRegister(3, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (U2Gate(0, pi), [q[2]], []), # H gate @@ -408,14 +408,19 @@ def _define(self): def __array__(self, dtype=None): """Return a numpy.array for the simplified CCX gate.""" - return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, -1j], - [0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, -1, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 1j, 0, 0, 0, 0]], dtype=dtype) + return numpy.array( + [ + [1, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, -1j], + [0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, -1, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 1j, 0, 0, 0, 0], + ], + dtype=dtype, + ) class C3SXGate(ControlledGate): @@ -437,13 +442,17 @@ def __init__(self, label=None, ctrl_state=None, *, angle=None): angle (float): DEPRECATED. The angle used in the controlled-U1 gates. An angle of π/8 yields the sqrt(X) gates, an angle of π/4 the 3-qubit controlled X gate. """ - super().__init__('c3sx', 4, [], num_ctrl_qubits=3, label=label, - ctrl_state=ctrl_state, base_gate=SXGate()) + super().__init__( + "c3sx", 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state, base_gate=SXGate() + ) if angle is not None: - warnings.warn('The angle argument is deprecated as of Qiskit Terra 0.17.0 and will ' - 'be removed no earlier than 3 months after the release date.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "The angle argument is deprecated as of Qiskit Terra 0.17.0 and will " + "be removed no earlier than 3 months after the release date.", + DeprecationWarning, + stacklevel=2, + ) if angle is None: angle = numpy.pi / 8 @@ -472,7 +481,8 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import CU1Gate - q = QuantumRegister(4, name='q') + + q = QuantumRegister(4, name="q") # pylint: disable=invalid-unary-operand-type rules = [ (HGate(), [q[3]], []), @@ -501,7 +511,7 @@ def _define(self): (CXGate(), [q[0], q[2]], []), (HGate(), [q[3]], []), (CU1Gate(-self._angle), [q[2], q[3]], []), - (HGate(), [q[3]], []) + (HGate(), [q[3]], []), ] qc = QuantumCircuit(q) for instr, qargs, cargs in rules: @@ -537,8 +547,9 @@ def __new__(cls, angle=None, label=None, ctrl_state=None): # pylint: disable=unused-argument def __init__(self, angle=None, label=None, ctrl_state=None): """Create a new 3-qubit controlled X gate.""" - super().__init__('mcx', 4, [], num_ctrl_qubits=3, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__( + "mcx", 4, [], num_ctrl_qubits=3, label=label, ctrl_state=ctrl_state, base_gate=XGate() + ) # seems like open controls not hapening? def _define(self): @@ -579,7 +590,8 @@ def _define(self): } """ from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(4, name='q') + + q = QuantumRegister(4, name="q") qc = QuantumCircuit(q, name=self.name) qc.h(3) qc.p(pi / 8, [0, 1, 2, 3]) @@ -636,9 +648,9 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the C4X gate.""" - mat = _compute_control_matrix(self.base_gate.to_matrix(), - self.num_ctrl_qubits, - ctrl_state=self.ctrl_state) + mat = _compute_control_matrix( + self.base_gate.to_matrix(), self.num_ctrl_qubits, ctrl_state=self.ctrl_state + ) if dtype: return numpy.asarray(mat, dtype=dtype) return mat @@ -657,7 +669,7 @@ class RC3XGate(Gate): def __init__(self, label=None): """Create a new RC3X gate.""" - super().__init__('rcccx', 4, [], label=label) + super().__init__("rcccx", 4, [], label=label) def _define(self): """ @@ -684,7 +696,8 @@ def _define(self): """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(4, 'q') + + q = QuantumRegister(4, "q") qc = QuantumCircuit(q, name=self.name) rules = [ (U2Gate(0, pi), [q[3]], []), # H gate @@ -713,22 +726,27 @@ def _define(self): def __array__(self, dtype=None): """Return a numpy.array for the RC3X gate.""" - return numpy.array([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 1j, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], - [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1j, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], - [0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=dtype) + return numpy.array( + [ + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 1j, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1j, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], + [0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0], + ], + dtype=dtype, + ) class C4XGate(ControlledGate): @@ -744,8 +762,9 @@ class C4XGate(ControlledGate): def __init__(self, label=None, ctrl_state=None): """Create a new 4-qubit controlled X gate.""" - super().__init__('mcx', 5, [], num_ctrl_qubits=4, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__( + "mcx", 5, [], num_ctrl_qubits=4, label=label, ctrl_state=ctrl_state, base_gate=XGate() + ) # seems like open controls not hapening? def _define(self): @@ -778,7 +797,8 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import CU1Gate - q = QuantumRegister(5, name='q') + + q = QuantumRegister(5, name="q") qc = QuantumCircuit(q, name=self.name) rules = [ (HGate(), [q[4]], []), @@ -820,9 +840,9 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the C4X gate.""" - mat = _compute_control_matrix(self.base_gate.to_matrix(), - self.num_ctrl_qubits, - ctrl_state=self.ctrl_state) + mat = _compute_control_matrix( + self.base_gate.to_matrix(), self.num_ctrl_qubits, ctrl_state=self.ctrl_state + ) if dtype: return numpy.asarray(mat, dtype=dtype) return mat @@ -839,10 +859,7 @@ def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): """ # The CXGate and CCXGate will be implemented for all modes of the MCX, and # the C3XGate and C4XGate will be implemented in the MCXGrayCode class. - explicit = { - 1: CXGate, - 2: CCXGate - } + explicit = {1: CXGate, 2: CCXGate} if num_ctrl_qubits in explicit.keys(): gate_class = explicit[num_ctrl_qubits] gate = gate_class.__new__(gate_class, label=label, ctrl_state=ctrl_state) @@ -851,37 +868,44 @@ def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): return gate return super().__new__(cls) - def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None, _name='mcx'): + def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None, _name="mcx"): """Create new MCX gate.""" num_ancilla_qubits = self.__class__.get_num_ancilla_qubits(num_ctrl_qubits) - super().__init__(_name, num_ctrl_qubits + 1 + num_ancilla_qubits, [], - num_ctrl_qubits=num_ctrl_qubits, label=label, - ctrl_state=ctrl_state, base_gate=XGate()) + super().__init__( + _name, + num_ctrl_qubits + 1 + num_ancilla_qubits, + [], + num_ctrl_qubits=num_ctrl_qubits, + label=label, + ctrl_state=ctrl_state, + base_gate=XGate(), + ) def inverse(self): """Invert this gate. The MCX is its own inverse.""" return MCXGate(num_ctrl_qubits=self.num_ctrl_qubits, ctrl_state=self.ctrl_state) @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode='noancilla'): + def get_num_ancilla_qubits(num_ctrl_qubits, mode="noancilla"): """Get the number of required ancilla qubits without instantiating the class. This staticmethod might be necessary to check the number of ancillas before creating the gate, or to use the number of ancillas in the initialization. """ - if mode == 'noancilla': + if mode == "noancilla": return 0 - if mode in ['recursion', 'advanced']: + if mode in ["recursion", "advanced"]: return int(num_ctrl_qubits > 4) - if mode[:7] == 'v-chain' or mode[:5] == 'basic': + if mode[:7] == "v-chain" or mode[:5] == "basic": return max(0, num_ctrl_qubits - 2) - raise AttributeError('Unsupported mode ({}) specified!'.format(mode)) + raise AttributeError("Unsupported mode ({}) specified!".format(mode)) def _define(self): """The standard definition used the Gray code implementation.""" # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(self.num_qubits, name='q') + + q = QuantumRegister(self.num_qubits, name="q") qc = QuantumCircuit(q) qc._append(MCXGrayCode(self.num_ctrl_qubits), q[:], []) self.definition = qc @@ -905,8 +929,9 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): """ if ctrl_state is None: # use __class__ so this works for derived classes - gate = self.__class__(self.num_ctrl_qubits + num_ctrl_qubits, label=label, - ctrl_state=ctrl_state) + gate = self.__class__( + self.num_ctrl_qubits + num_ctrl_qubits, label=label, ctrl_state=ctrl_state + ) gate.base_gate.label = self.label return gate return super().control(num_ctrl_qubits, label=label, ctrl_state=ctrl_state) @@ -917,16 +942,11 @@ class MCXGrayCode(MCXGate): This delegates the implementation to the MCU1 gate, since :math:`X = H \cdot U1(\pi) \cdot H`. """ + def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): - """Create a new MCXGrayCode instance - """ + """Create a new MCXGrayCode instance""" # if 1 to 4 control qubits, create explicit gates - explicit = { - 1: CXGate, - 2: CCXGate, - 3: C3XGate, - 4: C4XGate - } + explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate} if num_ctrl_qubits in explicit.keys(): gate_class = explicit[num_ctrl_qubits] gate = gate_class.__new__(gate_class, label=label, ctrl_state=ctrl_state) @@ -936,7 +956,7 @@ def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None): return super().__new__(cls) def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None): - super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_gray') + super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_gray") def inverse(self): """Invert this gate. The MCX is its own inverse.""" @@ -947,7 +967,8 @@ def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import MCU1Gate - q = QuantumRegister(self.num_qubits, name='q') + + q = QuantumRegister(self.num_qubits, name="q") qc = QuantumCircuit(q, name=self.name) qc._append(HGate(), [q[-1]], []) qc._append(MCU1Gate(numpy.pi, num_ctrl_qubits=self.num_ctrl_qubits), q[:], []) @@ -964,10 +985,10 @@ class MCXRecursive(MCXGate): """ def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None): - super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_recursive') + super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_recursive") @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode='recursion'): + def get_num_ancilla_qubits(num_ctrl_qubits, mode="recursion"): """Get the number of required ancilla qubits.""" return MCXGate.get_num_ancilla_qubits(num_ctrl_qubits, mode) @@ -979,7 +1000,8 @@ def _define(self): """Define the MCX gate using recursion.""" # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(self.num_qubits, name='q') + + q = QuantumRegister(self.num_qubits, name="q") qc = QuantumCircuit(q, name=self.name) if self.num_qubits == 4: qc._append(C3XGate(), q[:], []) @@ -999,7 +1021,7 @@ def _recurse(self, q, q_ancilla=None): if len(q) == 5: return [(C4XGate(), q[:], [])] if len(q) < 4: - raise AttributeError('Something went wrong in the recursion, have less than 4 qubits.') + raise AttributeError("Something went wrong in the recursion, have less than 4 qubits.") # recurse num_ctrl_qubits = len(q) - 1 @@ -1019,8 +1041,13 @@ def _recurse(self, q, q_ancilla=None): class MCXVChain(MCXGate): """Implement the multi-controlled X gate using a V-chain of CX gates.""" - def __new__(cls, num_ctrl_qubits=None, dirty_ancillas=False, # pylint: disable=unused-argument - label=None, ctrl_state=None): + def __new__( + cls, + num_ctrl_qubits=None, + dirty_ancillas=False, # pylint: disable=unused-argument + label=None, + ctrl_state=None, + ): """Create a new MCX instance. This must be defined anew to include the additional argument ``dirty_ancillas``. @@ -1028,17 +1055,19 @@ def __new__(cls, num_ctrl_qubits=None, dirty_ancillas=False, # pylint: disable= return super().__new__(cls, num_ctrl_qubits, label=label, ctrl_state=ctrl_state) def __init__(self, num_ctrl_qubits, dirty_ancillas=False, label=None, ctrl_state=None): - super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_vchain') + super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name="mcx_vchain") self._dirty_ancillas = dirty_ancillas def inverse(self): """Invert this gate. The MCX is its own inverse.""" - return MCXVChain(num_ctrl_qubits=self.num_ctrl_qubits, - dirty_ancillas=self._dirty_ancillas, - ctrl_state=self.ctrl_state) + return MCXVChain( + num_ctrl_qubits=self.num_ctrl_qubits, + dirty_ancillas=self._dirty_ancillas, + ctrl_state=self.ctrl_state, + ) @staticmethod - def get_num_ancilla_qubits(num_ctrl_qubits, mode='v-chain'): + def get_num_ancilla_qubits(num_ctrl_qubits, mode="v-chain"): """Get the number of required ancilla qubits.""" return MCXGate.get_num_ancilla_qubits(num_ctrl_qubits, mode) @@ -1046,11 +1075,12 @@ def _define(self): """Define the MCX gate using a V-chain of CX gates.""" # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit - q = QuantumRegister(self.num_qubits, name='q') + + q = QuantumRegister(self.num_qubits, name="q") qc = QuantumCircuit(q, name=self.name) - q_controls = q[:self.num_ctrl_qubits] + q_controls = q[: self.num_ctrl_qubits] q_target = q[self.num_ctrl_qubits] - q_ancillas = q[self.num_ctrl_qubits + 1:] + q_ancillas = q[self.num_ctrl_qubits + 1 :] definition = [] @@ -1072,7 +1102,8 @@ def _define(self): for j in reversed(range(2, self.num_ctrl_qubits - 1)): definition.append( - (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], [])) + (RCCXGate(), [q_controls[j], q_ancillas[i - 1], q_ancillas[i]], []) + ) i -= 1 definition.append((RCCXGate(), [q_controls[0], q_controls[1], q_ancillas[0]], [])) @@ -1106,7 +1137,8 @@ def _define(self): if self._dirty_ancillas: for i, j in enumerate(list(range(2, self.num_ctrl_qubits - 1))): definition.append( - (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], [])) + (RCCXGate(), [q_controls[j], q_ancillas[i], q_ancillas[i + 1]], []) + ) for instr, qargs, cargs in definition: qc._append(instr, qargs, cargs) diff --git a/qiskit/circuit/library/standard_gates/y.py b/qiskit/circuit/library/standard_gates/y.py index 102228d24e0c..a08d3393a04e 100644 --- a/qiskit/circuit/library/standard_gates/y.py +++ b/qiskit/circuit/library/standard_gates/y.py @@ -14,6 +14,7 @@ import numpy from qiskit.qasm import pi + # pylint: disable=cyclic-import from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.gate import Gate @@ -65,17 +66,16 @@ class YGate(Gate): def __init__(self, label=None): """Create new Y gate.""" - super().__init__('y', 1, [], label=label) + super().__init__("y", 1, [], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u3 import U3Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U3Gate(pi, pi / 2, pi / 2), [q[0]], []) - ] + rules = [(U3Gate(pi, pi / 2, pi / 2), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -107,8 +107,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the Y gate.""" - return numpy.array([[0, -1j], - [1j, 0]], dtype=dtype) + return numpy.array([[0, -1j], [1j, 0]], dtype=dtype) class CYGate(ControlledGate): @@ -164,19 +163,14 @@ class CYGate(ControlledGate): """ # Define class constants. This saves future allocation time. - _matrix1 = numpy.array([[1, 0, 0, 0], - [0, 0, 0, -1j], - [0, 0, 1, 0], - [0, 1j, 0, 0]]) - _matrix0 = numpy.array([[0, 0, -1j, 0], - [0, 1, 0, 0], - [1j, 0, 0, 0], - [0, 0, 0, 1]]) + _matrix1 = numpy.array([[1, 0, 0, 0], [0, 0, 0, -1j], [0, 0, 1, 0], [0, 1j, 0, 0]]) + _matrix0 = numpy.array([[0, 0, -1j, 0], [0, 1, 0, 0], [1j, 0, 0, 0], [0, 0, 0, 1]]) def __init__(self, label=None, ctrl_state=None): """Create new CY gate.""" - super().__init__('cy', 2, [], num_ctrl_qubits=1, label=label, - ctrl_state=ctrl_state, base_gate=YGate()) + super().__init__( + "cy", 2, [], num_ctrl_qubits=1, label=label, ctrl_state=ctrl_state, base_gate=YGate() + ) def _define(self): """ @@ -186,13 +180,10 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .s import SGate, SdgGate from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (SdgGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (SGate(), [q[1]], []) - ] + rules = [(SdgGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), (SGate(), [q[1]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) diff --git a/qiskit/circuit/library/standard_gates/z.py b/qiskit/circuit/library/standard_gates/z.py index 26db55f0702a..9c6d4475fdd9 100644 --- a/qiskit/circuit/library/standard_gates/z.py +++ b/qiskit/circuit/library/standard_gates/z.py @@ -64,17 +64,16 @@ class ZGate(Gate): def __init__(self, label=None): """Create new Z gate.""" - super().__init__('z', 1, [], label=label) + super().__init__("z", 1, [], label=label) def _define(self): # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit from .u1 import U1Gate - q = QuantumRegister(1, 'q') + + q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (U1Gate(pi), [q[0]], []) - ] + rules = [(U1Gate(pi), [q[0]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -106,8 +105,7 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the Z gate.""" - return numpy.array([[1, 0], - [0, -1]], dtype=dtype) + return numpy.array([[1, 0], [0, -1]], dtype=dtype) class CZGate(ControlledGate): @@ -142,8 +140,9 @@ class CZGate(ControlledGate): def __init__(self, label=None, ctrl_state=None): """Create new CZ gate.""" - super().__init__('cz', 2, [], label=label, num_ctrl_qubits=1, - ctrl_state=ctrl_state, base_gate=ZGate()) + super().__init__( + "cz", 2, [], label=label, num_ctrl_qubits=1, ctrl_state=ctrl_state, base_gate=ZGate() + ) def _define(self): """ @@ -153,13 +152,10 @@ def _define(self): from qiskit.circuit.quantumcircuit import QuantumCircuit from .h import HGate from .x import CXGate - q = QuantumRegister(2, 'q') + + q = QuantumRegister(2, "q") qc = QuantumCircuit(q, name=self.name) - rules = [ - (HGate(), [q[1]], []), - (CXGate(), [q[0], q[1]], []), - (HGate(), [q[1]], []) - ] + rules = [(HGate(), [q[1]], []), (CXGate(), [q[0], q[1]], []), (HGate(), [q[1]], [])] for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) @@ -172,12 +168,10 @@ def inverse(self): def __array__(self, dtype=None): """Return a numpy.array for the CZ gate.""" if self.ctrl_state: - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, 1, 0], - [0, 0, 0, -1]], dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=dtype + ) else: - return numpy.array([[1, 0, 0, 0], - [0, 1, 0, 0], - [0, 0, -1, 0], - [0, 0, 0, 1]], dtype=dtype) + return numpy.array( + [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], dtype=dtype + ) diff --git a/qiskit/circuit/library/templates/rzx/rzx_cy.py b/qiskit/circuit/library/templates/rzx/rzx_cy.py index 786255398f2f..2ca641ab8ede 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_cy.py +++ b/qiskit/circuit/library/templates/rzx/rzx_cy.py @@ -27,16 +27,16 @@ def rzx_cy(theta: float = None): """Template for CX - RYGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") circ = QuantumCircuit(2) circ.cx(0, 1) circ.ry(theta, 1) circ.cx(0, 1) - circ.ry(-1*theta, 1) + circ.ry(-1 * theta, 1) circ.rz(-np.pi / 2, 1) circ.rx(theta, 1) - circ.rzx(-1*theta, 0, 1) + circ.rzx(-1 * theta, 0, 1) circ.rz(np.pi / 2, 1) return circ diff --git a/qiskit/circuit/library/templates/rzx/rzx_xz.py b/qiskit/circuit/library/templates/rzx/rzx_xz.py index 8ad6c0eb79fc..4def8657e9aa 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_xz.py +++ b/qiskit/circuit/library/templates/rzx/rzx_xz.py @@ -32,7 +32,7 @@ def rzx_xz(theta: float = None): """Template for CX - RXGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") qc = QuantumCircuit(2) qc.cx(1, 0) @@ -42,7 +42,7 @@ def rzx_xz(theta: float = None): qc.rz(np.pi / 2, 0) qc.rx(np.pi / 2, 0) qc.rz(np.pi / 2, 0) - qc.rzx(-1*theta, 0, 1) + qc.rzx(-1 * theta, 0, 1) qc.rz(np.pi / 2, 0) qc.rx(np.pi / 2, 0) qc.rz(np.pi / 2, 0) diff --git a/qiskit/circuit/library/templates/rzx/rzx_yz.py b/qiskit/circuit/library/templates/rzx/rzx_yz.py index 081202dee4b0..96c4655f6d5e 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_yz.py +++ b/qiskit/circuit/library/templates/rzx/rzx_yz.py @@ -27,11 +27,11 @@ def rzx_yz(theta: float = None): """Template for CX - RYGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") circ = QuantumCircuit(2) circ.cx(0, 1) - circ.ry(-1*theta, 0) + circ.ry(-1 * theta, 0) circ.cx(0, 1) circ.rx(np.pi / 2, 0) circ.rzx(theta, 0, 1) diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz1.py b/qiskit/circuit/library/templates/rzx/rzx_zz1.py index 85c490ad5b2a..8eb715c3bcbc 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz1.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz1.py @@ -37,7 +37,7 @@ def rzx_zz1(theta: float = None): """Template for CX - RZGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") qc = QuantumCircuit(2) qc.cx(0, 1) @@ -47,7 +47,7 @@ def rzx_zz1(theta: float = None): qc.sx(1) qc.rz(3 * np.pi, 1) qc.cx(0, 1) - qc.rz(-1*theta, 1) + qc.rz(-1 * theta, 1) # Hadamard qc.rz(np.pi / 2, 1) @@ -55,7 +55,7 @@ def rzx_zz1(theta: float = None): qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) + qc.rzx(-1 * theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz2.py b/qiskit/circuit/library/templates/rzx/rzx_zz2.py index 677f7fb636e4..cd6774b337c2 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz2.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz2.py @@ -32,20 +32,20 @@ def rzx_zz2(theta: float = None): """Template for CX - RZGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") qc = QuantumCircuit(2) qc.cx(0, 1) qc.p(theta, 1) qc.cx(0, 1) - qc.p(-1*theta, 1) + qc.p(-1 * theta, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) + qc.rzx(-1 * theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) diff --git a/qiskit/circuit/library/templates/rzx/rzx_zz3.py b/qiskit/circuit/library/templates/rzx/rzx_zz3.py index 92632a4766dc..b1e117367f73 100644 --- a/qiskit/circuit/library/templates/rzx/rzx_zz3.py +++ b/qiskit/circuit/library/templates/rzx/rzx_zz3.py @@ -32,20 +32,20 @@ def rzx_zz3(theta: float = None): """Template for CX - RZGate - CX.""" if theta is None: - theta = Parameter('ϴ') + theta = Parameter("ϴ") qc = QuantumCircuit(2) qc.cx(0, 1) qc.rz(theta, 1) qc.cx(0, 1) - qc.rz(-1*theta, 1) + qc.rz(-1 * theta, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) qc.rz(np.pi / 2, 1) qc.rx(theta, 1) - qc.rzx(-1*theta, 0, 1) + qc.rzx(-1 * theta, 0, 1) # Hadamard qc.rz(np.pi / 2, 1) qc.rx(np.pi / 2, 1) diff --git a/qiskit/circuit/measure.py b/qiskit/circuit/measure.py index c14c9ef6a9d9..ef6acaf83c0c 100644 --- a/qiskit/circuit/measure.py +++ b/qiskit/circuit/measure.py @@ -36,7 +36,7 @@ def broadcast_arguments(self, qargs, cargs): for each_carg in carg: yield qarg, [each_carg] else: - raise CircuitError('register size error') + raise CircuitError("register size error") def measure(self, qubit, cbit): diff --git a/qiskit/circuit/parameter.py b/qiskit/circuit/parameter.py index 25d1df2c62aa..139fd72a4d47 100644 --- a/qiskit/circuit/parameter.py +++ b/qiskit/circuit/parameter.py @@ -51,6 +51,7 @@ def __init__(self, name: str): self._name = name from sympy import Symbol + symbol = Symbol(name) super().__init__(symbol_map={self: symbol}, expr=symbol) @@ -73,7 +74,7 @@ def __deepcopy__(self, memo=None): return self def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self.name) + return "{}({})".format(self.__class__.__name__, self.name) def __eq__(self, other): if isinstance(other, Parameter): diff --git a/qiskit/circuit/parameterexpression.py b/qiskit/circuit/parameterexpression.py index 89ad58342fed..c44c93dc8e21 100644 --- a/qiskit/circuit/parameterexpression.py +++ b/qiskit/circuit/parameterexpression.py @@ -21,13 +21,13 @@ from qiskit.circuit.exceptions import CircuitError -ParameterValueType = Union['ParameterExpression', float, int] +ParameterValueType = Union["ParameterExpression", float, int] class ParameterExpression: """ParameterExpression class to enable creating expressions of Parameters.""" - __slots__ = ['_parameter_symbols', '_parameters', '_symbol_expr', '_names'] + __slots__ = ["_parameter_symbols", "_parameters", "_symbol_expr", "_names"] def __init__(self, symbol_map: Dict, expr): """Create a new :class:`ParameterExpression`. @@ -51,12 +51,12 @@ def parameters(self) -> Set: """Returns a set of the unbound Parameters in the expression.""" return self._parameters - def conjugate(self) -> 'ParameterExpression': + def conjugate(self) -> "ParameterExpression": """Return the conjugate.""" conjugated = ParameterExpression(self._parameter_symbols, self._symbol_expr.conjugate()) return conjugated - def assign(self, parameter, value: ParameterValueType) -> 'ParameterExpression': + def assign(self, parameter, value: ParameterValueType) -> "ParameterExpression": """ Assign one parameter to a value, which can either be numeric or another parameter expression. @@ -72,7 +72,7 @@ def assign(self, parameter, value: ParameterValueType) -> 'ParameterExpression': return self.subs({parameter: value}) return self.bind({parameter: value}) - def bind(self, parameter_values: Dict) -> 'ParameterExpression': + def bind(self, parameter_values: Dict) -> "ParameterExpression": """Binds the provided set of parameters to their corresponding values. Args: @@ -94,8 +94,10 @@ def bind(self, parameter_values: Dict) -> 'ParameterExpression': self._raise_if_passed_unknown_parameters(parameter_values.keys()) self._raise_if_passed_nan(parameter_values) - symbol_values = {self._parameter_symbols[parameter]: value - for parameter, value in parameter_values.items()} + symbol_values = { + self._parameter_symbols[parameter]: value + for parameter, value in parameter_values.items() + } bound_symbol_expr = self._symbol_expr.subs(symbol_values) # Don't use sympy.free_symbols to count remaining parameters here. @@ -104,19 +106,20 @@ def bind(self, parameter_values: Dict) -> 'ParameterExpression': # e.g. (sympy.Symbol('s') * 0).free_symbols == set() free_parameters = self.parameters - parameter_values.keys() - free_parameter_symbols = {p: s for p, s in self._parameter_symbols.items() - if p in free_parameters} + free_parameter_symbols = { + p: s for p, s in self._parameter_symbols.items() if p in free_parameters + } if bound_symbol_expr.is_infinite: - raise ZeroDivisionError('Binding provided for expression ' - 'results in division by zero ' - '(Expression: {}, Bindings: {}).'.format( - self, parameter_values)) + raise ZeroDivisionError( + "Binding provided for expression " + "results in division by zero " + "(Expression: {}, Bindings: {}).".format(self, parameter_values) + ) return ParameterExpression(free_parameter_symbols, bound_symbol_expr) - def subs(self, - parameter_map: Dict) -> 'ParameterExpression': + def subs(self, parameter_map: Dict) -> "ParameterExpression": """Returns a new Expression with replacement Parameters. Args: @@ -133,21 +136,21 @@ def subs(self, A new expression with the specified parameters replaced. """ - inbound_parameters = {p - for replacement_expr in parameter_map.values() - for p in replacement_expr.parameters} + inbound_parameters = { + p for replacement_expr in parameter_map.values() for p in replacement_expr.parameters + } self._raise_if_passed_unknown_parameters(parameter_map.keys()) self._raise_if_parameter_names_conflict(inbound_parameters, parameter_map.keys()) from sympy import Symbol - new_parameter_symbols = {p: Symbol(p.name) - for p in inbound_parameters} + + new_parameter_symbols = {p: Symbol(p.name) for p in inbound_parameters} # Include existing parameters in self not set to be replaced. - new_parameter_symbols.update({p: s - for p, s in self._parameter_symbols.items() - if p not in parameter_map}) + new_parameter_symbols.update( + {p: s for p, s in self._parameter_symbols.items() if p not in parameter_map} + ) # If new_param is an expr, we'll need to construct a matching sympy expr # but with our sympy symbols instead of theirs. @@ -164,15 +167,19 @@ def subs(self, def _raise_if_passed_unknown_parameters(self, parameters): unknown_parameters = parameters - self.parameters if unknown_parameters: - raise CircuitError('Cannot bind Parameters ({}) not present in ' - 'expression.'.format([str(p) for p in unknown_parameters])) + raise CircuitError( + "Cannot bind Parameters ({}) not present in " + "expression.".format([str(p) for p in unknown_parameters]) + ) def _raise_if_passed_nan(self, parameter_values): - nan_parameter_values = {p: v for p, v in parameter_values.items() - if not isinstance(v, numbers.Number)} + nan_parameter_values = { + p: v for p, v in parameter_values.items() if not isinstance(v, numbers.Number) + } if nan_parameter_values: - raise CircuitError('Expression cannot bind non-numeric values ({})'.format( - nan_parameter_values)) + raise CircuitError( + "Expression cannot bind non-numeric values ({})".format(nan_parameter_values) + ) def _raise_if_parameter_names_conflict(self, inbound_parameters, outbound_parameters=None): if outbound_parameters is None: @@ -185,15 +192,17 @@ def _raise_if_parameter_names_conflict(self, inbound_parameters, outbound_parame outbound_names = {p.name: p for p in outbound_parameters} shared_names = (self._names.keys() - outbound_names.keys()) & inbound_names.keys() - conflicting_names = {name for name in shared_names - if self._names[name] != inbound_names[name]} + conflicting_names = { + name for name in shared_names if self._names[name] != inbound_names[name] + } if conflicting_names: - raise CircuitError('Name conflict applying operation for parameters: ' - '{}'.format(conflicting_names)) + raise CircuitError( + "Name conflict applying operation for parameters: " "{}".format(conflicting_names) + ) - def _apply_operation(self, operation: Callable, - other: ParameterValueType, - reflected: bool = False) -> 'ParameterExpression': + def _apply_operation( + self, operation: Callable, other: ParameterValueType, reflected: bool = False + ) -> "ParameterExpression": """Base method implementing math operations between Parameters and either a constant or a second ParameterExpression. @@ -233,7 +242,7 @@ def _apply_operation(self, operation: Callable, return ParameterExpression(parameter_symbols, expr) - def gradient(self, param) -> Union['ParameterExpression', float]: + def gradient(self, param) -> Union["ParameterExpression", float]: """Get the derivative of a parameter expression w.r.t. a specified parameter expression. Args: @@ -249,6 +258,7 @@ def gradient(self, param) -> Union['ParameterExpression', float]: # Compute the gradient of the parameter expression w.r.t. param import sympy as sy + key = self._parameter_symbols[param] # TODO enable nth derivative expr_grad = sy.Derivative(self._symbol_expr, key).doit() @@ -289,80 +299,91 @@ def __rmul__(self, other): def __truediv__(self, other): if other == 0: - raise ZeroDivisionError('Division of a ParameterExpression by zero.') + raise ZeroDivisionError("Division of a ParameterExpression by zero.") return self._apply_operation(operator.truediv, other) def __rtruediv__(self, other): return self._apply_operation(operator.truediv, other, reflected=True) def _call(self, ufunc): - return ParameterExpression( - self._parameter_symbols, - ufunc(self._symbol_expr) - ) + return ParameterExpression(self._parameter_symbols, ufunc(self._symbol_expr)) def sin(self): """Sine of a ParameterExpression""" from sympy import sin as _sin + return self._call(_sin) def cos(self): """Cosine of a ParameterExpression""" from sympy import cos as _cos + return self._call(_cos) def tan(self): """Tangent of a ParameterExpression""" from sympy import tan as _tan + return self._call(_tan) def arcsin(self): """Arcsin of a ParameterExpression""" from sympy import asin as _asin + return self._call(_asin) def arccos(self): """Arccos of a ParameterExpression""" from sympy import acos as _acos + return self._call(_acos) def arctan(self): """Arctan of a ParameterExpression""" from sympy import atan as _atan + return self._call(_atan) def exp(self): """Exponential of a ParameterExpression""" from sympy import exp as _exp + return self._call(_exp) def log(self): """Logarithm of a ParameterExpression""" from sympy import log as _log + return self._call(_log) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, str(self)) + return "{}({})".format(self.__class__.__name__, str(self)) def __str__(self): return str(self._symbol_expr) def __float__(self): if self.parameters: - raise TypeError('ParameterExpression with unbound parameters ({}) ' - 'cannot be cast to a float.'.format(self.parameters)) + raise TypeError( + "ParameterExpression with unbound parameters ({}) " + "cannot be cast to a float.".format(self.parameters) + ) return float(self._symbol_expr) def __complex__(self): if self.parameters: - raise TypeError('ParameterExpression with unbound parameters ({}) ' - 'cannot be cast to a complex.'.format(self.parameters)) + raise TypeError( + "ParameterExpression with unbound parameters ({}) " + "cannot be cast to a complex.".format(self.parameters) + ) return complex(self._symbol_expr) def __int__(self): if self.parameters: - raise TypeError('ParameterExpression with unbound parameters ({}) ' - 'cannot be cast to an int.'.format(self.parameters)) + raise TypeError( + "ParameterExpression with unbound parameters ({}) " + "cannot be cast to an int.".format(self.parameters) + ) return int(self._symbol_expr) def __hash__(self): @@ -384,9 +405,9 @@ def __eq__(self, other): bool: result of the comparison """ if isinstance(other, ParameterExpression): - return (self.parameters == other.parameters - and self._symbol_expr.equals(other._symbol_expr)) + return self.parameters == other.parameters and self._symbol_expr.equals( + other._symbol_expr + ) elif isinstance(other, numbers.Number): - return (len(self.parameters) == 0 - and complex(self._symbol_expr) == other) + return len(self.parameters) == 0 and complex(self._symbol_expr) == other return False diff --git a/qiskit/circuit/parametertable.py b/qiskit/circuit/parametertable.py index 3b8ea3e59cd9..24f2dea97dd8 100644 --- a/qiskit/circuit/parametertable.py +++ b/qiskit/circuit/parametertable.py @@ -22,7 +22,7 @@ class ParameterTable(MutableMapping): """Class for managing and setting circuit parameters""" - __slots__ = ['_table', '_keys', '_names'] + __slots__ = ["_table", "_keys", "_names"] def __init__(self, *args, **kwargs): """ @@ -80,7 +80,7 @@ def __len__(self): return len(self._table) def __repr__(self): - return 'ParameterTable({})'.format(repr(self._table)) + return "ParameterTable({})".format(repr(self._table)) def _deprecated_set_method(): @@ -89,15 +89,21 @@ def deprecate(func): def wrapper(*args, **kwargs): # warn only once if not wrapper._warned: - warnings.warn(f'The ParameterView.{func.__name__} method is deprecated as of ' - 'Qiskit Terra 0.17.0 and will be removed no sooner than 3 months ' - 'after the release date. Circuit parameters are returned as View ' - 'object, not set. To use set methods you can explicitly cast to a ' - 'set.', DeprecationWarning, stacklevel=2) + warnings.warn( + f"The ParameterView.{func.__name__} method is deprecated as of " + "Qiskit Terra 0.17.0 and will be removed no sooner than 3 months " + "after the release date. Circuit parameters are returned as View " + "object, not set. To use set methods you can explicitly cast to a " + "set.", + DeprecationWarning, + stacklevel=2, + ) wrapper._warned = True return func(*args, **kwargs) + wrapper._warned = False return wrapper + return deprecate @@ -197,7 +203,7 @@ def remove(self, x): def __repr__(self): """Format the class as string.""" - return f'ParameterView({self.data})' + return f"ParameterView({self.data})" def __getitem__(self, index): """Get items.""" diff --git a/qiskit/circuit/parametervector.py b/qiskit/circuit/parametervector.py index bc8f4a0d0f4e..c3878063120d 100644 --- a/qiskit/circuit/parametervector.py +++ b/qiskit/circuit/parametervector.py @@ -35,7 +35,7 @@ def __getnewargs__(self): return (self.vector, self.index, self._uuid) def __init__(self, vector, index): - name = f'{vector.name}[{index}]' + name = f"{vector.name}[{index}]" super().__init__(name) self._vector = vector self._index = index @@ -81,20 +81,20 @@ def __getitem__(self, key): return self.params[start:stop:step] if key > self._size: - raise IndexError('Index out of range: {} > {}'.format(key, self._size)) + raise IndexError("Index out of range: {} > {}".format(key, self._size)) return self.params[key] def __iter__(self): - return iter(self.params[:self._size]) + return iter(self.params[: self._size]) def __len__(self): return self._size def __str__(self): - return '{}, {}'.format(self.name, [str(item) for item in self.params[:self._size]]) + return "{}, {}".format(self.name, [str(item) for item in self.params[: self._size]]) def __repr__(self): - return '{}(name={}, length={})'.format(self.__class__.__name__, self.name, len(self)) + return "{}(name={}, length={})".format(self.__class__.__name__, self.name, len(self)) def resize(self, length): """Resize the parameter vector. diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index e9797af5acfe..1fa2c5d5c784 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -48,6 +48,7 @@ from pygments.formatters import Terminal256Formatter # pylint: disable=no-name-in-module from qiskit.qasm.pygments import OpenQASMLexer # pylint: disable=ungrouped-imports from qiskit.qasm.pygments import QasmTerminalStyle # pylint: disable=ungrouped-imports + HAS_PYGMENTS = True except Exception: # pylint: disable=broad-except HAS_PYGMENTS = False @@ -141,12 +142,13 @@ class QuantumCircuit: qc.draw() """ + instances = 0 - prefix = 'circuit' + prefix = "circuit" # Class variable OPENQASM header header = "OPENQASM 2.0;" - extension_lib = "include \"qelib1.inc\";" + extension_lib = 'include "qelib1.inc";' def __init__(self, *regs, name=None, global_phase=0, metadata=None): if any(not isinstance(reg, (list, QuantumRegister, ClassicalRegister)) for reg in regs): @@ -158,8 +160,10 @@ def __init__(self, *regs, name=None, global_phase=0, metadata=None): valid_reg_size = False if not valid_reg_size: - raise CircuitError("Circuit args must be Registers or integers. (%s '%s' was " - "provided)" % ([type(reg).__name__ for reg in regs], regs)) + raise CircuitError( + "Circuit args must be Registers or integers. (%s '%s' was " + "provided)" % ([type(reg).__name__ for reg in regs], regs) + ) regs = tuple(int(reg) for reg in regs) # cast to int self._base_name = None @@ -167,8 +171,9 @@ def __init__(self, *regs, name=None, global_phase=0, metadata=None): self._base_name = self.cls_prefix() self._name_update() elif not isinstance(name, str): - raise CircuitError("The circuit name should be a string " - "(or None to auto-generate a name).") + raise CircuitError( + "The circuit name should be a string " "(or None to auto-generate a name)." + ) else: self._base_name = name self.name = name @@ -201,7 +206,7 @@ def __init__(self, *regs, name=None, global_phase=0, metadata=None): self.global_phase = global_phase self.duration = None - self.unit = 'dt' + self.unit = "dt" if not isinstance(metadata, dict) and metadata is not None: raise TypeError("Only a dictionary or None is accepted for circuit metadata") self._metadata = metadata @@ -279,7 +284,7 @@ def metadata(self, metadata): self._metadata = metadata def __str__(self): - return str(self.draw(output='text')) + return str(self.draw(output="text")) def __eq__(self, other): if not isinstance(other, QuantumCircuit): @@ -287,6 +292,7 @@ def __eq__(self, other): # TODO: remove the DAG from this function from qiskit.converters import circuit_to_dag + return circuit_to_dag(self) == circuit_to_dag(other) @classmethod @@ -307,11 +313,11 @@ def cls_prefix(cls): def _name_update(self): """update name of instance using instance number""" if not is_main_process(): - pid_name = f'-{mp.current_process().pid}' + pid_name = f"-{mp.current_process().pid}" else: - pid_name = '' + pid_name = "" - self.name = f'{self._base_name}-{self.cls_instances()}{pid_name}' + self.name = f"{self._base_name}-{self.cls_instances()}{pid_name}" def has_register(self, register): """ @@ -324,11 +330,9 @@ def has_register(self, register): bool: True if the register is contained in this circuit. """ has_reg = False - if (isinstance(register, QuantumRegister) and - register in self.qregs): + if isinstance(register, QuantumRegister) and register in self.qregs: has_reg = True - elif (isinstance(register, ClassicalRegister) and - register in self.cregs): + elif isinstance(register, ClassicalRegister) and register in self.cregs: has_reg = True return has_reg @@ -357,9 +361,9 @@ def reverse_ops(self): q_1: ┤ RX(1.57) ├───── └──────────┘ """ - reverse_circ = QuantumCircuit(self.qubits, self.clbits, - *self.qregs, *self.cregs, - name=self.name + '_reverse') + reverse_circ = QuantumCircuit( + self.qubits, self.clbits, *self.qregs, *self.cregs, name=self.name + "_reverse" + ) for inst, qargs, cargs in reversed(self.data): reverse_circ._append(inst.reverse_ops(), qargs, cargs) @@ -397,8 +401,12 @@ def reverse_bits(self): q_1: ┤ H ├─────■────── └───┘ """ - circ = QuantumCircuit(*reversed(self.qregs), *reversed(self.cregs), - name=self.name, global_phase=self.global_phase) + circ = QuantumCircuit( + *reversed(self.qregs), + *reversed(self.cregs), + name=self.name, + global_phase=self.global_phase, + ) num_qubits = self.num_qubits num_clbits = self.num_clbits old_qubits = self.qubits @@ -439,9 +447,14 @@ def inverse(self): q_1: ┤ RX(-1.57) ├───── └───────────┘ """ - inverse_circ = QuantumCircuit(self.qubits, self.clbits, - *self.qregs, *self.cregs, - name=self.name + '_dg', global_phase=-self.global_phase) + inverse_circ = QuantumCircuit( + self.qubits, + self.clbits, + *self.qregs, + *self.cregs, + name=self.name + "_dg", + global_phase=-self.global_phase, + ) for inst, qargs, cargs in reversed(self._data): inverse_circ._append(inst.inverse(), qargs, cargs) @@ -456,9 +469,9 @@ def repeat(self, reps): Returns: QuantumCircuit: A circuit containing ``reps`` repetitions of this circuit. """ - repeated_circ = QuantumCircuit(self.qubits, self.clbits, - *self.qregs, *self.cregs, - name=self.name + '**{}'.format(reps)) + repeated_circ = QuantumCircuit( + self.qubits, self.clbits, *self.qregs, *self.cregs, name=self.name + "**{}".format(reps) + ) # benefit of appending instructions: decomposing shows the subparts, i.e. the power # is actually `reps` times this circuit, and it is currently much faster than `compose`. @@ -496,16 +509,20 @@ def power(self, power, matrix_power=False): # attempt conversion to gate if self.num_parameters > 0: - raise CircuitError('Cannot raise a parameterized circuit to a non-positive power ' - 'or matrix-power, please bind the free parameters: ' - '{}'.format(self.parameters)) + raise CircuitError( + "Cannot raise a parameterized circuit to a non-positive power " + "or matrix-power, please bind the free parameters: " + "{}".format(self.parameters) + ) try: gate = self.to_gate() except QiskitError as ex: - raise CircuitError('The circuit contains non-unitary operations and cannot be ' - 'controlled. Note that no qiskit.circuit.Instruction objects may ' - 'be in the circuit for this operation.') from ex + raise CircuitError( + "The circuit contains non-unitary operations and cannot be " + "controlled. Note that no qiskit.circuit.Instruction objects may " + "be in the circuit for this operation." + ) from ex power_circuit = QuantumCircuit(self.qubits, self.clbits, *self.qregs, *self.cregs) power_circuit.append(gate.power(power), list(range(gate.num_qubits))) @@ -529,21 +546,26 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): try: gate = self.to_gate() except QiskitError as ex: - raise CircuitError('The circuit contains non-unitary operations and cannot be ' - 'controlled. Note that no qiskit.circuit.Instruction objects may ' - 'be in the circuit for this operation.') from ex + raise CircuitError( + "The circuit contains non-unitary operations and cannot be " + "controlled. Note that no qiskit.circuit.Instruction objects may " + "be in the circuit for this operation." + ) from ex controlled_gate = gate.control(num_ctrl_qubits, label, ctrl_state) control_qreg = QuantumRegister(num_ctrl_qubits) - controlled_circ = QuantumCircuit(control_qreg, self.qubits, *self.qregs, - name='c_{}'.format(self.name)) + controlled_circ = QuantumCircuit( + control_qreg, self.qubits, *self.qregs, name="c_{}".format(self.name) + ) controlled_circ.append(controlled_gate, controlled_circ.qubits) return controlled_circ - @deprecate_function('The QuantumCircuit.combine() method is being deprecated. ' - 'Use the compose() method which is more flexible w.r.t ' - 'circuit register compatibility.') + @deprecate_function( + "The QuantumCircuit.combine() method is being deprecated. " + "Use the compose() method which is more flexible w.r.t " + "circuit register compatibility." + ) def combine(self, rhs): """DEPRECATED - Returns rhs appended to self if self contains compatible registers. @@ -591,9 +613,11 @@ def combine(self, rhs): return circuit - @deprecate_function('The QuantumCircuit.extend() method is being deprecated. Use the ' - 'compose() (potentially with the inplace=True argument) and tensor() ' - 'methods which are more flexible w.r.t circuit register compatibility.') + @deprecate_function( + "The QuantumCircuit.extend() method is being deprecated. Use the " + "compose() (potentially with the inplace=True argument) and tensor() " + "methods which are more flexible w.r.t circuit register compatibility." + ) def extend(self, rhs): """DEPRECATED - Append QuantumCircuit to the RHS if it contains compatible registers. @@ -703,10 +727,10 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=False): instrs = other.data - if other.num_qubits > self.num_qubits or \ - other.num_clbits > self.num_clbits: - raise CircuitError("Trying to compose with another QuantumCircuit " - "which has more 'in' edges.") + if other.num_qubits > self.num_qubits or other.num_clbits > self.num_clbits: + raise CircuitError( + "Trying to compose with another QuantumCircuit " "which has more 'in' edges." + ) # number of qubits and clbits must match number in circuit or None identity_qubit_map = dict(zip(other.qubits, self.qubits)) @@ -715,19 +739,27 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=False): if qubits is None: qubit_map = identity_qubit_map elif len(qubits) != len(other.qubits): - raise CircuitError("Number of items in qubits parameter does not" - " match number of qubits in the circuit.") + raise CircuitError( + "Number of items in qubits parameter does not" + " match number of qubits in the circuit." + ) else: - qubit_map = {other.qubits[i]: (self.qubits[q] if isinstance(q, int) else q) - for i, q in enumerate(qubits)} + qubit_map = { + other.qubits[i]: (self.qubits[q] if isinstance(q, int) else q) + for i, q in enumerate(qubits) + } if clbits is None: clbit_map = identity_clbit_map elif len(clbits) != len(other.clbits): - raise CircuitError("Number of items in clbits parameter does not" - " match number of clbits in the circuit.") + raise CircuitError( + "Number of items in clbits parameter does not" + " match number of clbits in the circuit." + ) else: - clbit_map = {other.clbits[i]: (self.clbits[c] if isinstance(c, int) else c) - for i, c in enumerate(clbits)} + clbit_map = { + other.clbits[i]: (self.clbits[c] if isinstance(c, int) else c) + for i, c in enumerate(clbits) + } edge_map = {**qubit_map, **clbit_map} or {**identity_qubit_map, **identity_clbit_map} @@ -739,6 +771,7 @@ def compose(self, other, qubits=None, clbits=None, front=False, inplace=False): if instr.condition is not None: from qiskit.dagcircuit import DAGCircuit # pylint: disable=cyclic-import + n_instr.condition = DAGCircuit._map_condition(edge_map, instr.condition, self.cregs) mapped_instrs.append((n_instr, n_qargs, n_cargs)) @@ -809,8 +842,10 @@ def tensor(self, other, inplace=False): # (e.g. QuantumCircuit(2, 2)) then we have a naming collision, as the registers are by # default called "q" resp. "c". To still allow tensoring we define new registers of the # correct sizes. - if len(self.qregs) == len(other.qregs) == 1 and \ - self.qregs[0].name == other.qregs[0].name == 'q': + if ( + len(self.qregs) == len(other.qregs) == 1 + and self.qregs[0].name == other.qregs[0].name == "q" + ): # check if classical registers are in the circuit if num_clbits > 0: dest = QuantumCircuit(num_qubits, num_clbits) @@ -819,20 +854,34 @@ def tensor(self, other, inplace=False): # handle case if ``measure_all`` was called on both circuits, in which case the # registers are both named "meas" - elif len(self.cregs) == len(other.cregs) == 1 and \ - self.cregs[0].name == other.cregs[0].name == 'meas': - cr = ClassicalRegister(self.num_clbits + other.num_clbits, 'meas') + elif ( + len(self.cregs) == len(other.cregs) == 1 + and self.cregs[0].name == other.cregs[0].name == "meas" + ): + cr = ClassicalRegister(self.num_clbits + other.num_clbits, "meas") dest = QuantumCircuit(*other.qregs, *self.qregs, cr) # Now we don't have to handle any more cases arising from special implicit naming else: - dest = QuantumCircuit(other.qubits, self.qubits, other.clbits, self.clbits, - *other.qregs, *self.qregs, *other.cregs, *self.cregs) + dest = QuantumCircuit( + other.qubits, + self.qubits, + other.clbits, + self.clbits, + *other.qregs, + *self.qregs, + *other.cregs, + *self.cregs, + ) # compose self onto the output, and then other dest.compose(other, range(other.num_qubits), range(other.num_clbits), inplace=True) - dest.compose(self, range(other.num_qubits, num_qubits), - range(other.num_clbits, num_clbits), inplace=True) + dest.compose( + self, + range(other.num_qubits, num_qubits), + range(other.num_clbits, num_clbits), + inplace=True, + ) # Replace information from tensored circuit into self when inplace = True if inplace: @@ -861,16 +910,20 @@ def ancillas(self): """ return self._ancillas - @deprecate_function('The QuantumCircuit.__add__() method is being deprecated.' - 'Use the compose() method which is more flexible w.r.t ' - 'circuit register compatibility.') + @deprecate_function( + "The QuantumCircuit.__add__() method is being deprecated." + "Use the compose() method which is more flexible w.r.t " + "circuit register compatibility." + ) def __add__(self, rhs): """Overload + to implement self.combine.""" return self.combine(rhs) - @deprecate_function('The QuantumCircuit.__iadd__() method is being deprecated. Use the ' - 'compose() (potentially with the inplace=True argument) and tensor() ' - 'methods which are more flexible w.r.t circuit register compatibility.') + @deprecate_function( + "The QuantumCircuit.__iadd__() method is being deprecated. Use the " + "compose() (potentially with the inplace=True argument) and tensor() " + "methods which are more flexible w.r.t circuit register compatibility." + ) def __iadd__(self, rhs): """Overload += to implement self.extend.""" return self.extend(rhs) @@ -925,24 +978,28 @@ def _bit_argument_conversion(bit_representation, in_array): elif isinstance(bit_representation, slice): # circuit.h(slice(0,2)) -> circuit.h([qr[0], qr[1]]) ret = in_array[bit_representation] - elif isinstance(bit_representation, list) and \ - all(isinstance(bit, Bit) for bit in bit_representation): + elif isinstance(bit_representation, list) and all( + isinstance(bit, Bit) for bit in bit_representation + ): # circuit.h([qr[0], qr[1]]) -> circuit.h([qr[0], qr[1]]) ret = bit_representation elif isinstance(QuantumCircuit.cast(bit_representation, list), (range, list)): # circuit.h([0, 1]) -> circuit.h([qr[0], qr[1]]) # circuit.h(range(0,2)) -> circuit.h([qr[0], qr[1]]) # circuit.h([qr[0],1]) -> circuit.h([qr[0], qr[1]]) - ret = [index if isinstance(index, Bit) else in_array[ - index] for index in bit_representation] + ret = [ + index if isinstance(index, Bit) else in_array[index] + for index in bit_representation + ] else: - raise CircuitError('Not able to expand a %s (%s)' % (bit_representation, - type(bit_representation))) + raise CircuitError( + "Not able to expand a %s (%s)" % (bit_representation, type(bit_representation)) + ) except IndexError as ex: - raise CircuitError('Index out of range.') from ex + raise CircuitError("Index out of range.") from ex except TypeError as ex: raise CircuitError( - f'Type error handling {bit_representation} ({type(bit_representation)})' + f"Type error handling {bit_representation} ({type(bit_representation)})" ) from ex return ret @@ -989,18 +1046,21 @@ def append(self, instruction, qargs=None, cargs=None): CircuitError: if object passed is neither subclass nor an instance of Instruction """ # Convert input to instruction - if not isinstance(instruction, Instruction) and not hasattr(instruction, 'to_instruction'): + if not isinstance(instruction, Instruction) and not hasattr(instruction, "to_instruction"): if issubclass(instruction, Instruction): - raise CircuitError('Object is a subclass of Instruction, please add () to ' - 'pass an instance of this object.') + raise CircuitError( + "Object is a subclass of Instruction, please add () to " + "pass an instance of this object." + ) - raise CircuitError('Object to append must be an Instruction or ' - 'have a to_instruction() method.') + raise CircuitError( + "Object to append must be an Instruction or " "have a to_instruction() method." + ) if not isinstance(instruction, Instruction) and hasattr(instruction, "to_instruction"): instruction = instruction.to_instruction() # Make copy of parameterized gate instances - if hasattr(instruction, 'params'): + if hasattr(instruction, "params"): is_parameter = any(isinstance(param, Parameter) for param in instruction.params) if is_parameter: instruction = copy.deepcopy(instruction) @@ -1030,7 +1090,7 @@ def _append(self, instruction, qargs, cargs): it is being attached to. """ if not isinstance(instruction, Instruction): - raise CircuitError('object is not an Instruction.') + raise CircuitError("object is not an Instruction.") # do some compatibility checks self._check_dups(qargs) @@ -1045,7 +1105,7 @@ def _append(self, instruction, qargs, cargs): # mark as normal circuit if a new instruction is added self.duration = None - self.unit = 'dt' + self.unit = "dt" return instruction @@ -1057,13 +1117,15 @@ def _update_parameter_table(self, instruction): for parameter in param.parameters: if parameter in current_parameters: - if not self._check_dup_param_spec(self._parameter_table[parameter], - instruction, param_index): + if not self._check_dup_param_spec( + self._parameter_table[parameter], instruction, param_index + ): self._parameter_table[parameter].append((instruction, param_index)) else: if parameter.name in self._parameter_table.get_names(): raise CircuitError( - 'Name conflict on adding parameter: {}'.format(parameter.name)) + "Name conflict on adding parameter: {}".format(parameter.name) + ) self._parameter_table[parameter] = [(instruction, param_index)] # clear cache if new parameter is added @@ -1086,36 +1148,34 @@ def add_register(self, *regs): # QuantumCircuit defined without registers if len(regs) == 1 and isinstance(regs[0], int): # QuantumCircuit with anonymous quantum wires e.g. QuantumCircuit(2) - regs = (QuantumRegister(regs[0], 'q'),) + regs = (QuantumRegister(regs[0], "q"),) elif len(regs) == 2 and all(isinstance(reg, int) for reg in regs): # QuantumCircuit with anonymous wires e.g. QuantumCircuit(2, 3) - regs = (QuantumRegister(regs[0], 'q'), ClassicalRegister(regs[1], 'c')) + regs = (QuantumRegister(regs[0], "q"), ClassicalRegister(regs[1], "c")) else: - raise CircuitError("QuantumCircuit parameters can be Registers or Integers." - " If Integers, up to 2 arguments. QuantumCircuit was called" - " with %s." % (regs,)) + raise CircuitError( + "QuantumCircuit parameters can be Registers or Integers." + " If Integers, up to 2 arguments. QuantumCircuit was called" + " with %s." % (regs,) + ) for register in regs: - if ( - isinstance(register, Register) - and any(register.name == reg.name for reg in self.qregs + self.cregs) + if isinstance(register, Register) and any( + register.name == reg.name for reg in self.qregs + self.cregs ): - raise CircuitError("register name \"%s\" already exists" - % register.name) + raise CircuitError('register name "%s" already exists' % register.name) if isinstance(register, AncillaRegister): self._ancillas.extend(register) if isinstance(register, QuantumRegister): self.qregs.append(register) - new_bits = [bit for bit in register - if bit not in self._qubit_set] + new_bits = [bit for bit in register if bit not in self._qubit_set] self._qubits.extend(new_bits) self._qubit_set.update(new_bits) elif isinstance(register, ClassicalRegister): self.cregs.append(register) - new_bits = [bit for bit in register - if bit not in self._clbit_set] + new_bits = [bit for bit in register if bit not in self._clbit_set] self._clbits.extend(new_bits) self._clbit_set.update(new_bits) elif isinstance(register, list): @@ -1127,8 +1187,9 @@ def add_bits(self, bits): """Add Bits to the circuit.""" duplicate_bits = set(self.qubits + self.clbits).intersection(bits) if duplicate_bits: - raise CircuitError("Attempted to add bits found already in circuit: " - "{}".format(duplicate_bits)) + raise CircuitError( + "Attempted to add bits found already in circuit: " "{}".format(duplicate_bits) + ) for bit in bits: if isinstance(bit, AncillaQubit): @@ -1141,8 +1202,10 @@ def add_bits(self, bits): self._clbits.append(bit) self._clbit_set.add(bit) else: - raise CircuitError("Expected an instance of Qubit, Clbit, or " - "AncillaQubit, but was passed {}".format(bit)) + raise CircuitError( + "Expected an instance of Qubit, Clbit, or " + "AncillaQubit, but was passed {}".format(bit) + ) def _check_dups(self, qubits): """Raise exception if list of qubits contains duplicates.""" @@ -1178,6 +1241,7 @@ def to_instruction(self, parameter_map=None): (can be decomposed back) """ from qiskit.converters.circuit_to_instruction import circuit_to_instruction + return circuit_to_instruction(self, parameter_map) def to_gate(self, parameter_map=None, label=None): @@ -1195,6 +1259,7 @@ def to_gate(self, parameter_map=None, label=None): (can be decomposed back) """ from qiskit.converters.circuit_to_gate import circuit_to_gate + return circuit_to_gate(self, parameter_map, label=label) def decompose(self): @@ -1208,6 +1273,7 @@ def decompose(self): from qiskit.transpiler.passes.basis.decompose import Decompose from qiskit.converters.circuit_to_dag import circuit_to_dag from qiskit.converters.dag_to_circuit import dag_to_circuit + pass_ = Decompose() decomposed_dag = pass_.run(circuit_to_dag(self)) return dag_to_circuit(decomposed_dag) @@ -1220,8 +1286,10 @@ def _check_compatible_regs(self, rhs): for element2 in list2: if element2.name == element1.name: if element1 != element2: - raise CircuitError("circuits are not compatible:" - f" registers {element1} and {element2} not compatible") + raise CircuitError( + "circuits are not compatible:" + f" registers {element1} and {element2} not compatible" + ) @staticmethod def _get_composite_circuit_qasm_from_instruction(instruction): @@ -1233,23 +1301,33 @@ def _get_composite_circuit_qasm_from_instruction(instruction): composite_circuit_gates = "" definition = instruction.definition - definition_bit_labels = {bit: idx - for bits in (definition.qubits, definition.clbits) - for idx, bit in enumerate(bits)} + definition_bit_labels = { + bit: idx + for bits in (definition.qubits, definition.clbits) + for idx, bit in enumerate(bits) + } for data, qargs, _ in definition: - gate_qargs = ",".join(["q%i" % index - for index in [definition_bit_labels[qubit] for qubit in qargs]]) + gate_qargs = ",".join( + ["q%i" % index for index in [definition_bit_labels[qubit] for qubit in qargs]] + ) composite_circuit_gates += "%s %s; " % (data.qasm(), gate_qargs) if composite_circuit_gates: - composite_circuit_gates = composite_circuit_gates.rstrip(' ') + composite_circuit_gates = composite_circuit_gates.rstrip(" ") if gate_parameters: - qasm_string = "gate %s(%s) %s { %s }" % (instruction.name, gate_parameters, - qubit_parameters, composite_circuit_gates) + qasm_string = "gate %s(%s) %s { %s }" % ( + instruction.name, + gate_parameters, + qubit_parameters, + composite_circuit_gates, + ) else: - qasm_string = "gate %s %s { %s }" % (instruction.name, qubit_parameters, - composite_circuit_gates) + qasm_string = "gate %s %s { %s }" % ( + instruction.name, + qubit_parameters, + composite_circuit_gates, + ) return qasm_string @@ -1271,13 +1349,53 @@ def qasm(self, formatted=False, filename=None): from qiskit.circuit.controlledgate import ControlledGate if self.num_parameters > 0: - raise QasmError('Cannot represent circuits with unbound parameters in OpenQASM 2.') - - existing_gate_names = ['ch', 'cp', 'cx', 'cy', 'cz', 'crx', 'cry', 'crz', 'ccx', 'cswap', - 'csx', 'cu', 'cu1', 'cu3', 'dcx', 'h', 'i', 'id', 'iden', 'iswap', - 'ms', 'p', 'r', 'rx', 'rxx', 'ry', 'ryy', 'rz', 'rzx', 'rzz', 's', - 'sdg', 'swap', 'sx', 'x', 'y', 'z', 't', 'tdg', 'u', 'u1', 'u2', - 'u3'] + raise QasmError("Cannot represent circuits with unbound parameters in OpenQASM 2.") + + existing_gate_names = [ + "ch", + "cp", + "cx", + "cy", + "cz", + "crx", + "cry", + "crz", + "ccx", + "cswap", + "csx", + "cu", + "cu1", + "cu3", + "dcx", + "h", + "i", + "id", + "iden", + "iswap", + "ms", + "p", + "r", + "rx", + "rxx", + "ry", + "ryy", + "rz", + "rzx", + "rzz", + "s", + "sdg", + "swap", + "sx", + "x", + "y", + "z", + "t", + "tdg", + "u", + "u1", + "u2", + "u3", + ] existing_composite_circuits = [] @@ -1295,64 +1413,81 @@ def qasm(self, formatted=False, filename=None): if set(self.qubits) != qreg_bits: regless_qubits = [bit for bit in self.qubits if bit not in qreg_bits] - string_temp += "qreg %s[%d];\n" % ('regless', len(regless_qubits)) + string_temp += "qreg %s[%d];\n" % ("regless", len(regless_qubits)) if set(self.clbits) != creg_bits: regless_clbits = [bit for bit in self.clbits if bit not in creg_bits] - string_temp += "creg %s[%d];\n" % ('regless', len(regless_clbits)) + string_temp += "creg %s[%d];\n" % ("regless", len(regless_clbits)) unitary_gates = [] - bit_labels = {bit: "%s[%d]" % (reg.name, idx) - for reg in self.qregs + self.cregs - for (idx, bit) in enumerate(reg)} + bit_labels = { + bit: "%s[%d]" % (reg.name, idx) + for reg in self.qregs + self.cregs + for (idx, bit) in enumerate(reg) + } - bit_labels.update({bit: "regless[%d]" % idx - for reg in (regless_qubits, regless_clbits) - for idx, bit in enumerate(reg)}) + bit_labels.update( + { + bit: "regless[%d]" % idx + for reg in (regless_qubits, regless_clbits) + for idx, bit in enumerate(reg) + } + ) for instruction, qargs, cargs in self._data: - if instruction.name == 'measure': + if instruction.name == "measure": qubit = qargs[0] clbit = cargs[0] - string_temp += "%s %s -> %s;\n" % (instruction.qasm(), - bit_labels[qubit], - bit_labels[clbit]) + string_temp += "%s %s -> %s;\n" % ( + instruction.qasm(), + bit_labels[qubit], + bit_labels[clbit], + ) # If instruction is a root gate or a root instruction (in that case, compositive) - elif (type(instruction) in # pylint: disable=unidiomatic-typecheck - [Gate, Instruction] or - (isinstance(instruction, ControlledGate) and instruction._open_ctrl)): + elif ( + type(instruction) + in [ # pylint: disable=unidiomatic-typecheck + Gate, + Instruction, + ] + or (isinstance(instruction, ControlledGate) and instruction._open_ctrl) + ): if instruction not in existing_composite_circuits: if instruction.name in existing_gate_names: old_name = instruction.name instruction.name += "_" + str(id(instruction)) - warnings.warn("A gate named {} already exists. " - "We have renamed " - "your gate to {}".format(old_name, instruction.name)) + warnings.warn( + "A gate named {} already exists. " + "We have renamed " + "your gate to {}".format(old_name, instruction.name) + ) # Get qasm of composite circuit qasm_string = self._get_composite_circuit_qasm_from_instruction(instruction) # Insert composite circuit qasm definition right after header and extension lib - string_temp = string_temp.replace(self.extension_lib, - "%s\n%s" % (self.extension_lib, - qasm_string)) + string_temp = string_temp.replace( + self.extension_lib, "%s\n%s" % (self.extension_lib, qasm_string) + ) existing_composite_circuits.append(instruction) existing_gate_names.append(instruction.name) # Insert qasm representation of the original instruction - string_temp += "%s %s;\n" % (instruction.qasm(), - ",".join([bit_labels[j] - for j in qargs + cargs])) + string_temp += "%s %s;\n" % ( + instruction.qasm(), + ",".join([bit_labels[j] for j in qargs + cargs]), + ) else: - string_temp += "%s %s;\n" % (instruction.qasm(), - ",".join([bit_labels[j] - for j in qargs + cargs])) - if instruction.name == 'unitary': + string_temp += "%s %s;\n" % ( + instruction.qasm(), + ",".join([bit_labels[j] for j in qargs + cargs]), + ) + if instruction.name == "unitary": unitary_gates.append(instruction) # this resets them, so if another call to qasm() is made the gate def is added again @@ -1360,27 +1495,43 @@ def qasm(self, formatted=False, filename=None): gate._qasm_def_written = False if filename: - with open(filename, 'w+') as file: + with open(filename, "w+") as file: file.write(string_temp) file.close() if formatted: if not HAS_PYGMENTS: - raise ImportError("To use the formatted output pygments>2.4 " - "must be installed. To install pygments run " - '"pip install pygments".') - code = pygments.highlight(string_temp, - OpenQASMLexer(), - Terminal256Formatter(style=QasmTerminalStyle)) + raise ImportError( + "To use the formatted output pygments>2.4 " + "must be installed. To install pygments run " + '"pip install pygments".' + ) + code = pygments.highlight( + string_temp, OpenQASMLexer(), Terminal256Formatter(style=QasmTerminalStyle) + ) print(code) return None else: return string_temp - def draw(self, output=None, scale=None, filename=None, style=None, - interactive=False, plot_barriers=True, - reverse_bits=False, justify=None, vertical_compression='medium', idle_wires=True, - with_layout=True, fold=None, ax=None, initial_state=False, cregbundle=True): + def draw( + self, + output=None, + scale=None, + filename=None, + style=None, + interactive=False, + plot_barriers=True, + reverse_bits=False, + justify=None, + vertical_compression="medium", + idle_wires=True, + with_layout=True, + fold=None, + ax=None, + initial_state=False, + cregbundle=True, + ): """Draw the quantum circuit. Use the output parameter to choose the drawing format: **text**: ASCII art TextDrawing that can be printed in the console. @@ -1495,20 +1646,24 @@ def draw(self, output=None, scale=None, filename=None, style=None, # pylint: disable=cyclic-import from qiskit.visualization import circuit_drawer - return circuit_drawer(self, scale=scale, - filename=filename, style=style, - output=output, - interactive=interactive, - plot_barriers=plot_barriers, - reverse_bits=reverse_bits, - justify=justify, - vertical_compression=vertical_compression, - idle_wires=idle_wires, - with_layout=with_layout, - fold=fold, - ax=ax, - initial_state=initial_state, - cregbundle=cregbundle) + return circuit_drawer( + self, + scale=scale, + filename=filename, + style=style, + output=output, + interactive=interactive, + plot_barriers=plot_barriers, + reverse_bits=reverse_bits, + justify=justify, + vertical_compression=vertical_compression, + idle_wires=idle_wires, + with_layout=with_layout, + fold=fold, + ax=ax, + initial_state=initial_state, + cregbundle=cregbundle, + ) def size(self): """Returns total number of gate operations in circuit. @@ -1536,8 +1691,7 @@ def depth(self): """ # Assign each bit in the circuit a unique integer # to index into op_stack. - bit_indices = {bit: idx - for idx, bit in enumerate(self.qubits + self.clbits)} + bit_indices = {bit: idx for idx, bit in enumerate(self.qubits + self.clbits)} # If no bits, return 0 if not bit_indices: @@ -1646,8 +1800,7 @@ def num_connected_components(self, unitary_only=False): """ # Convert registers to ints (as done in depth). bits = self.qubits if unitary_only else (self.qubits + self.clbits) - bit_indices = {bit: idx - for idx, bit in enumerate(bits)} + bit_indices = {bit: idx for idx, bit in enumerate(bits)} # Start with each qubit or cbit being its own subgraph. sub_graphs = [[bit] for bit in range(len(bit_indices))] @@ -1701,7 +1854,7 @@ def num_connected_components(self, unitary_only=False): _sub_graphs.append(sub_graphs[idx]) _sub_graphs.append(connections) sub_graphs = _sub_graphs - num_sub_graphs -= (num_touched - 1) + num_sub_graphs -= num_touched - 1 # Cannot go lower than one so break if num_sub_graphs == 1: break @@ -1742,20 +1895,24 @@ def copy(self, name=None): cpy._qubit_set = self._qubit_set.copy() cpy._clbit_set = self._clbit_set.copy() - instr_instances = {id(instr): instr - for instr, _, __ in self._data} + instr_instances = {id(instr): instr for instr, _, __ in self._data} - instr_copies = {id_: instr.copy() - for id_, instr in instr_instances.items()} + instr_copies = {id_: instr.copy() for id_, instr in instr_instances.items()} - cpy._parameter_table = ParameterTable({ - param: [(instr_copies[id(instr)], param_index) - for instr, param_index in self._parameter_table[param]] - for param in self._parameter_table - }) + cpy._parameter_table = ParameterTable( + { + param: [ + (instr_copies[id(instr)], param_index) + for instr, param_index in self._parameter_table[param] + ] + for param in self._parameter_table + } + ) - cpy._data = [(instr_copies[id(inst)], qargs.copy(), cargs.copy()) - for inst, qargs, cargs in self._data] + cpy._data = [ + (instr_copies[id(inst)], qargs.copy(), cargs.copy()) + for inst, qargs, cargs in self._data + ] cpy._calibrations = copy.deepcopy(self._calibrations) cpy._metadata = copy.deepcopy(self._metadata) @@ -1765,8 +1922,7 @@ def copy(self, name=None): return cpy def _create_creg(self, length, name): - """ Creates a creg, checking if ClassicalRegister with same name exists - """ + """Creates a creg, checking if ClassicalRegister with same name exists""" if name in [creg.name for creg in self.cregs]: save_prefix = ClassicalRegister.prefix ClassicalRegister.prefix = name @@ -1777,8 +1933,7 @@ def _create_creg(self, length, name): return new_creg def _create_qreg(self, length, name): - """ Creates a qreg, checking if QuantumRegister with same name exists - """ + """Creates a qreg, checking if QuantumRegister with same name exists""" if name in [qreg.name for qreg in self.qregs]: save_prefix = QuantumRegister.prefix QuantumRegister.prefix = name @@ -1801,13 +1956,14 @@ def measure_active(self, inplace=True): QuantumCircuit: Returns circuit with measurements when `inplace = False`. """ from qiskit.converters.circuit_to_dag import circuit_to_dag + if inplace: circ = self else: circ = self.copy() dag = circuit_to_dag(circ) qubits_to_measure = [qubit for qubit in circ.qubits if qubit not in dag.idle_wires()] - new_creg = circ._create_creg(len(qubits_to_measure), 'measure') + new_creg = circ._create_creg(len(qubits_to_measure), "measure") circ.add_register(new_creg) circ.barrier() circ.measure(qubits_to_measure, new_creg) @@ -1834,7 +1990,7 @@ def measure_all(self, inplace=True): else: circ = self.copy() - new_creg = circ._create_creg(len(circ.qubits), 'meas') + new_creg = circ._create_creg(len(circ.qubits), "meas") circ.add_register(new_creg) circ.barrier() circ.measure(circ.qubits, new_creg) @@ -1957,9 +2113,10 @@ def _unsorted_parameters(self): return parameters - @deprecate_arguments({'param_dict': 'parameters'}) - def assign_parameters(self, parameters, inplace=False, - param_dict=None): # pylint: disable=unused-argument + @deprecate_arguments({"param_dict": "parameters"}) + def assign_parameters( + self, parameters, inplace=False, param_dict=None + ): # pylint: disable=unused-argument """Assign parameters to new parameters or values. The keys of the parameter dictionary must be Parameter instances in the current circuit. The @@ -2041,24 +2198,32 @@ def assign_parameters(self, parameters, inplace=False, unsorted_parameters = self._unsorted_parameters() # check that all param_dict items are in the _parameter_table for this circuit - params_not_in_circuit = [param_key for param_key in unrolled_param_dict - if param_key not in unsorted_parameters] + params_not_in_circuit = [ + param_key + for param_key in unrolled_param_dict + if param_key not in unsorted_parameters + ] if len(params_not_in_circuit) > 0: - raise CircuitError('Cannot bind parameters ({}) not present in the circuit.'.format( - ', '.join(map(str, params_not_in_circuit)))) + raise CircuitError( + "Cannot bind parameters ({}) not present in the circuit.".format( + ", ".join(map(str, params_not_in_circuit)) + ) + ) # replace the parameters with a new Parameter ("substitute") or numeric value ("bind") for parameter, value in unrolled_param_dict.items(): bound_circuit._assign_parameter(parameter, value) else: if len(parameters) != self.num_parameters: - raise ValueError('Mismatching number of values and parameters. For partial binding ' - 'please pass a dictionary of {parameter: value} pairs.') + raise ValueError( + "Mismatching number of values and parameters. For partial binding " + "please pass a dictionary of {parameter: value} pairs." + ) for i, value in enumerate(parameters): bound_circuit._assign_parameter(self.parameters[i], value) return None if inplace else bound_circuit - @deprecate_arguments({'value_dict': 'values'}) + @deprecate_arguments({"value_dict": "values"}) def bind_parameters(self, values, value_dict=None): # pylint: disable=unused-argument """Assign numeric parameters to values yielding a new circuit. @@ -2079,12 +2244,14 @@ def bind_parameters(self, values, value_dict=None): # pylint: disable=unused-ar if isinstance(values, dict): if any(isinstance(value, ParameterExpression) for value in values.values()): raise TypeError( - 'Found ParameterExpression in values; use assign_parameters() instead.') + "Found ParameterExpression in values; use assign_parameters() instead." + ) return self.assign_parameters(values) else: if any(isinstance(value, ParameterExpression) for value in values): raise TypeError( - 'Found ParameterExpression in values; use assign_parameters() instead.') + "Found ParameterExpression in values; use assign_parameters() instead." + ) return self.assign_parameters(values) def _unroll_param_dict(self, value_dict): @@ -2092,9 +2259,11 @@ def _unroll_param_dict(self, value_dict): for (param, value) in value_dict.items(): if isinstance(param, ParameterVector): if not len(param) == len(value): - raise CircuitError('ParameterVector {} has length {}, which ' - 'differs from value list {} of ' - 'len {}'.format(param, len(param), value, len(value))) + raise CircuitError( + "ParameterVector {} has length {}, which " + "differs from value list {} of " + "len {}".format(param, len(param), value, len(value)) + ) unrolled_value_dict.update(zip(param, value)) # pass anything else except number through. error checking is done in assign_parameter elif isinstance(param, (ParameterExpression, str)) or param is None: @@ -2132,8 +2301,10 @@ def _assign_parameter(self, parameter, value): else: del self._parameter_table[parameter] # clear evaluated expressions - if (isinstance(self.global_phase, ParameterExpression) and - parameter in self.global_phase.parameters): + if ( + isinstance(self.global_phase, ParameterExpression) + and parameter in self.global_phase.parameters + ): self.global_phase = self.global_phase.assign(parameter, value) # clear parameter cache @@ -2147,8 +2318,10 @@ def _assign_calibration_parameters(self, parameter, value): """ for cals in self.calibrations.values(): for (qubit, cal_params), schedule in copy.copy(cals).items(): - if any(isinstance(p, ParameterExpression) and parameter in p.parameters - for p in cal_params): + if any( + isinstance(p, ParameterExpression) and parameter in p.parameters + for p in cal_params + ): del cals[(qubit, cal_params)] new_cal_params = [] for p in cal_params: @@ -2176,6 +2349,7 @@ def _rebind_definition(self, instruction, parameter, value): def barrier(self, *qargs): """Apply :class:`~qiskit.circuit.Barrier`. If qargs is None, applies to all.""" from .barrier import Barrier + qubits = [] if not qargs: # None @@ -2195,7 +2369,7 @@ def barrier(self, *qargs): return self.append(Barrier(len(qubits)), qubits, []) - def delay(self, duration, qarg=None, unit='dt'): + def delay(self, duration, qarg=None, unit="dt"): """Apply :class:`~qiskit.circuit.Delay`. If qarg is None, applies to all qubits. When applying to multiple qubits, delays with the same duration will be created. @@ -2237,18 +2411,27 @@ def delay(self, duration, qarg=None, unit='dt'): def h(self, qubit): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.HGate`.""" from .library.standard_gates.h import HGate + return self.append(HGate(), [qubit], []) - def ch(self, control_qubit, target_qubit, # pylint: disable=invalid-name - label=None, ctrl_state=None): + def ch( + self, + control_qubit, + target_qubit, # pylint: disable=invalid-name + label=None, + ctrl_state=None, + ): """Apply :class:`~qiskit.circuit.library.CHGate`.""" from .library.standard_gates.h import CHGate - return self.append(CHGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CHGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def i(self, qubit): """Apply :class:`~qiskit.circuit.library.IGate`.""" from .library.standard_gates.i import IGate + return self.append(IGate(), [qubit], []) def id(self, qubit): # pylint: disable=invalid-name @@ -2259,131 +2442,163 @@ def ms(self, theta, qubits): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.MSGate`.""" # pylint: disable=cyclic-import from .library.generalized_gates.gms import MSGate + return self.append(MSGate(len(qubits), theta), qubits) def p(self, theta, qubit): """Apply :class:`~qiskit.circuit.library.PhaseGate`.""" from .library.standard_gates.p import PhaseGate + return self.append(PhaseGate(theta), [qubit], []) def cp(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CPhaseGate`.""" from .library.standard_gates.p import CPhaseGate - return self.append(CPhaseGate(theta, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CPhaseGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def mcp(self, lam, control_qubits, target_qubit): """Apply :class:`~qiskit.circuit.library.MCPhaseGate`.""" from .library.standard_gates.p import MCPhaseGate + num_ctrl_qubits = len(control_qubits) - return self.append(MCPhaseGate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], - []) + return self.append( + MCPhaseGate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], [] + ) def r(self, theta, phi, qubit): """Apply :class:`~qiskit.circuit.library.RGate`.""" from .library.standard_gates.r import RGate + return self.append(RGate(theta, phi), [qubit], []) def rv(self, vx, vy, vz, qubit): """Apply :class:`~qiskit.circuit.library.RVGate`.""" from .library.generalized_gates.rv import RVGate + return self.append(RVGate(vx, vy, vz), [qubit], []) def rccx(self, control_qubit1, control_qubit2, target_qubit): """Apply :class:`~qiskit.circuit.library.RCCXGate`.""" from .library.standard_gates.x import RCCXGate + return self.append(RCCXGate(), [control_qubit1, control_qubit2, target_qubit], []) def rcccx(self, control_qubit1, control_qubit2, control_qubit3, target_qubit): """Apply :class:`~qiskit.circuit.library.RC3XGate`.""" from .library.standard_gates.x import RC3XGate - return self.append(RC3XGate(), - [control_qubit1, control_qubit2, control_qubit3, target_qubit], - []) + + return self.append( + RC3XGate(), [control_qubit1, control_qubit2, control_qubit3, target_qubit], [] + ) def rx(self, theta, qubit, label=None): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.RXGate`.""" from .library.standard_gates.rx import RXGate + return self.append(RXGate(theta, label=label), [qubit], []) def crx(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CRXGate`.""" from .library.standard_gates.rx import CRXGate - return self.append(CRXGate(theta, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CRXGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def rxx(self, theta, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.RXXGate`.""" from .library.standard_gates.rxx import RXXGate + return self.append(RXXGate(theta), [qubit1, qubit2], []) def ry(self, theta, qubit, label=None): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.RYGate`.""" from .library.standard_gates.ry import RYGate + return self.append(RYGate(theta, label=label), [qubit], []) def cry(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CRYGate`.""" from .library.standard_gates.ry import CRYGate - return self.append(CRYGate(theta, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CRYGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def ryy(self, theta, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.RYYGate`.""" from .library.standard_gates.ryy import RYYGate + return self.append(RYYGate(theta), [qubit1, qubit2], []) def rz(self, phi, qubit): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.RZGate`.""" from .library.standard_gates.rz import RZGate + return self.append(RZGate(phi), [qubit], []) def crz(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CRZGate`.""" from .library.standard_gates.rz import CRZGate - return self.append(CRZGate(theta, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CRZGate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def rzx(self, theta, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.RZXGate`.""" from .library.standard_gates.rzx import RZXGate + return self.append(RZXGate(theta), [qubit1, qubit2], []) def rzz(self, theta, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.RZZGate`.""" from .library.standard_gates.rzz import RZZGate + return self.append(RZZGate(theta), [qubit1, qubit2], []) def ecr(self, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.ECRGate`.""" from .library.standard_gates.ecr import ECRGate + return self.append(ECRGate(), [qubit1, qubit2], []) def s(self, qubit): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.SGate`.""" from .library.standard_gates.s import SGate + return self.append(SGate(), [qubit], []) def sdg(self, qubit): """Apply :class:`~qiskit.circuit.library.SdgGate`.""" from .library.standard_gates.s import SdgGate + return self.append(SdgGate(), [qubit], []) def swap(self, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.SwapGate`.""" from .library.standard_gates.swap import SwapGate + return self.append(SwapGate(), [qubit1, qubit2], []) def iswap(self, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.iSwapGate`.""" from .library.standard_gates.iswap import iSwapGate + return self.append(iSwapGate(), [qubit1, qubit2], []) def cswap(self, control_qubit, target_qubit1, target_qubit2, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CSwapGate`.""" from .library.standard_gates.swap import CSwapGate - return self.append(CSwapGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit1, target_qubit2], []) + + return self.append( + CSwapGate(label=label, ctrl_state=ctrl_state), + [control_qubit, target_qubit1, target_qubit2], + [], + ) def fredkin(self, control_qubit, target_qubit1, target_qubit2): """Apply :class:`~qiskit.circuit.library.CSwapGate`.""" @@ -2392,116 +2607,156 @@ def fredkin(self, control_qubit, target_qubit1, target_qubit2): def sx(self, qubit): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.SXGate`.""" from .library.standard_gates.sx import SXGate + return self.append(SXGate(), [qubit], []) def sxdg(self, qubit): """Apply :class:`~qiskit.circuit.library.SXdgGate`.""" from .library.standard_gates.sx import SXdgGate + return self.append(SXdgGate(), [qubit], []) def csx(self, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CSXGate`.""" from .library.standard_gates.sx import CSXGate - return self.append(CSXGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CSXGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def t(self, qubit): # pylint: disable=invalid-name """Apply :class:`~qiskit.circuit.library.TGate`.""" from .library.standard_gates.t import TGate + return self.append(TGate(), [qubit], []) def tdg(self, qubit): """Apply :class:`~qiskit.circuit.library.TdgGate`.""" from .library.standard_gates.t import TdgGate + return self.append(TdgGate(), [qubit], []) def u(self, theta, phi, lam, qubit): """Apply :class:`~qiskit.circuit.library.UGate`.""" from .library.standard_gates.u import UGate + return self.append(UGate(theta, phi, lam), [qubit], []) - def cu(self, theta, phi, lam, gamma, - control_qubit, target_qubit, label=None, ctrl_state=None): + def cu(self, theta, phi, lam, gamma, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CUGate`.""" from .library.standard_gates.u import CUGate - return self.append(CUGate(theta, phi, lam, gamma, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) - - @deprecate_function('The QuantumCircuit.u1 method is deprecated as of ' - '0.16.0. It will be removed no earlier than 3 months ' - 'after the release date. You should use the ' - 'QuantumCircuit.p method instead, which acts ' - 'identically.') + + return self.append( + CUGate(theta, phi, lam, gamma, label=label, ctrl_state=ctrl_state), + [control_qubit, target_qubit], + [], + ) + + @deprecate_function( + "The QuantumCircuit.u1 method is deprecated as of " + "0.16.0. It will be removed no earlier than 3 months " + "after the release date. You should use the " + "QuantumCircuit.p method instead, which acts " + "identically." + ) def u1(self, theta, qubit): """Apply :class:`~qiskit.circuit.library.U1Gate`.""" from .library.standard_gates.u1 import U1Gate + return self.append(U1Gate(theta), [qubit], []) - @deprecate_function('The QuantumCircuit.cu1 method is deprecated as of ' - '0.16.0. It will be removed no earlier than 3 months ' - 'after the release date. You should use the ' - 'QuantumCircuit.cp method instead, which acts ' - 'identically.') + @deprecate_function( + "The QuantumCircuit.cu1 method is deprecated as of " + "0.16.0. It will be removed no earlier than 3 months " + "after the release date. You should use the " + "QuantumCircuit.cp method instead, which acts " + "identically." + ) def cu1(self, theta, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CU1Gate`.""" from .library.standard_gates.u1 import CU1Gate - return self.append(CU1Gate(theta, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) - - @deprecate_function('The QuantumCircuit.mcu1 method is deprecated as of ' - '0.16.0. It will be removed no earlier than 3 months ' - 'after the release date. You should use the ' - 'QuantumCircuit.mcp method instead, which acts ' - 'identically.') + + return self.append( + CU1Gate(theta, label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) + + @deprecate_function( + "The QuantumCircuit.mcu1 method is deprecated as of " + "0.16.0. It will be removed no earlier than 3 months " + "after the release date. You should use the " + "QuantumCircuit.mcp method instead, which acts " + "identically." + ) def mcu1(self, lam, control_qubits, target_qubit): """Apply :class:`~qiskit.circuit.library.MCU1Gate`.""" from .library.standard_gates.u1 import MCU1Gate + num_ctrl_qubits = len(control_qubits) return self.append(MCU1Gate(lam, num_ctrl_qubits), control_qubits[:] + [target_qubit], []) - @deprecate_function('The QuantumCircuit.u2 method is deprecated as of ' - '0.16.0. It will be removed no earlier than 3 months ' - 'after the release date. You can use the general 1-' - 'qubit gate QuantumCircuit.u instead: u2(φ,λ) = ' - 'u(π/2, φ, λ). Alternatively, you can decompose it in' - 'terms of QuantumCircuit.p and QuantumCircuit.sx: ' - 'u2(φ,λ) = p(π/2+φ) sx p(λ-π/2) (1 pulse on hardware).') + @deprecate_function( + "The QuantumCircuit.u2 method is deprecated as of " + "0.16.0. It will be removed no earlier than 3 months " + "after the release date. You can use the general 1-" + "qubit gate QuantumCircuit.u instead: u2(φ,λ) = " + "u(π/2, φ, λ). Alternatively, you can decompose it in" + "terms of QuantumCircuit.p and QuantumCircuit.sx: " + "u2(φ,λ) = p(π/2+φ) sx p(λ-π/2) (1 pulse on hardware)." + ) def u2(self, phi, lam, qubit): """Apply :class:`~qiskit.circuit.library.U2Gate`.""" from .library.standard_gates.u2 import U2Gate + return self.append(U2Gate(phi, lam), [qubit], []) - @deprecate_function('The QuantumCircuit.u3 method is deprecated as of 0.16.0. It will be ' - 'removed no earlier than 3 months after the release date. You should use ' - 'QuantumCircuit.u instead, which acts identically. Alternatively, you can ' - 'decompose u3 in terms of QuantumCircuit.p and QuantumCircuit.sx: ' - 'u3(ϴ,φ,λ) = p(φ+π) sx p(ϴ+π) sx p(λ) (2 pulses on hardware).') + @deprecate_function( + "The QuantumCircuit.u3 method is deprecated as of 0.16.0. It will be " + "removed no earlier than 3 months after the release date. You should use " + "QuantumCircuit.u instead, which acts identically. Alternatively, you can " + "decompose u3 in terms of QuantumCircuit.p and QuantumCircuit.sx: " + "u3(ϴ,φ,λ) = p(φ+π) sx p(ϴ+π) sx p(λ) (2 pulses on hardware)." + ) def u3(self, theta, phi, lam, qubit): """Apply :class:`~qiskit.circuit.library.U3Gate`.""" from .library.standard_gates.u3 import U3Gate + return self.append(U3Gate(theta, phi, lam), [qubit], []) - @deprecate_function('The QuantumCircuit.cu3 method is deprecated as of 0.16.0. It will be ' - 'removed no earlier than 3 months after the release date. You should ' - 'use the QuantumCircuit.cu method instead, where ' - 'cu3(ϴ,φ,λ) = cu(ϴ,φ,λ,0).') + @deprecate_function( + "The QuantumCircuit.cu3 method is deprecated as of 0.16.0. It will be " + "removed no earlier than 3 months after the release date. You should " + "use the QuantumCircuit.cu method instead, where " + "cu3(ϴ,φ,λ) = cu(ϴ,φ,λ,0)." + ) def cu3(self, theta, phi, lam, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CU3Gate`.""" from .library.standard_gates.u3 import CU3Gate - return self.append(CU3Gate(theta, phi, lam, label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CU3Gate(theta, phi, lam, label=label, ctrl_state=ctrl_state), + [control_qubit, target_qubit], + [], + ) def x(self, qubit, label=None): """Apply :class:`~qiskit.circuit.library.XGate`.""" from .library.standard_gates.x import XGate + return self.append(XGate(label=label), [qubit], []) - def cx(self, control_qubit, target_qubit, # pylint: disable=invalid-name - label=None, ctrl_state=None): + def cx( + self, + control_qubit, + target_qubit, # pylint: disable=invalid-name + label=None, + ctrl_state=None, + ): """Apply :class:`~qiskit.circuit.library.CXGate`.""" from .library.standard_gates.x import CXGate - return self.append(CXGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CXGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def cnot(self, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CXGate`.""" @@ -2510,19 +2765,20 @@ def cnot(self, control_qubit, target_qubit, label=None, ctrl_state=None): def dcx(self, qubit1, qubit2): """Apply :class:`~qiskit.circuit.library.DCXGate`.""" from .library.standard_gates.dcx import DCXGate + return self.append(DCXGate(), [qubit1, qubit2], []) def ccx(self, control_qubit1, control_qubit2, target_qubit): """Apply :class:`~qiskit.circuit.library.CCXGate`.""" from .library.standard_gates.x import CCXGate - return self.append(CCXGate(), - [control_qubit1, control_qubit2, target_qubit], []) + + return self.append(CCXGate(), [control_qubit1, control_qubit2, target_qubit], []) def toffoli(self, control_qubit1, control_qubit2, target_qubit): """Apply :class:`~qiskit.circuit.library.CCXGate`.""" self.ccx(control_qubit1, control_qubit2, target_qubit) - def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode='noancilla'): + def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode="noancilla"): """Apply :class:`~qiskit.circuit.library.MCXGate`. The multi-cX gate can be implemented using different techniques, which use different numbers @@ -2533,17 +2789,18 @@ def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode='noancilla - 'v-chain-dirty': Same as for the clean ancillas (but the circuit will be longer). """ from .library.standard_gates.x import MCXGrayCode, MCXRecursive, MCXVChain + num_ctrl_qubits = len(control_qubits) available_implementations = { - 'noancilla': MCXGrayCode(num_ctrl_qubits), - 'recursion': MCXRecursive(num_ctrl_qubits), - 'v-chain': MCXVChain(num_ctrl_qubits, False), - 'v-chain-dirty': MCXVChain(num_ctrl_qubits, dirty_ancillas=True), + "noancilla": MCXGrayCode(num_ctrl_qubits), + "recursion": MCXRecursive(num_ctrl_qubits), + "v-chain": MCXVChain(num_ctrl_qubits, False), + "v-chain-dirty": MCXVChain(num_ctrl_qubits, dirty_ancillas=True), # outdated, previous names - 'advanced': MCXRecursive(num_ctrl_qubits), - 'basic': MCXVChain(num_ctrl_qubits, dirty_ancillas=False), - 'basic-dirty-ancilla': MCXVChain(num_ctrl_qubits, dirty_ancillas=True) + "advanced": MCXRecursive(num_ctrl_qubits), + "basic": MCXVChain(num_ctrl_qubits, dirty_ancillas=False), + "basic-dirty-ancilla": MCXVChain(num_ctrl_qubits, dirty_ancillas=True), } # check ancilla input @@ -2555,22 +2812,23 @@ def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode='noancilla except KeyError as ex: all_modes = list(available_implementations.keys()) raise ValueError( - f'Unsupported mode ({mode}) selected, choose one of {all_modes}' + f"Unsupported mode ({mode}) selected, choose one of {all_modes}" ) from ex - if hasattr(gate, 'num_ancilla_qubits') and gate.num_ancilla_qubits > 0: + if hasattr(gate, "num_ancilla_qubits") and gate.num_ancilla_qubits > 0: required = gate.num_ancilla_qubits if ancilla_qubits is None: - raise AttributeError('No ancillas provided, but {} are needed!'.format(required)) + raise AttributeError("No ancillas provided, but {} are needed!".format(required)) # convert ancilla qubits to a list if they were passed as int or qubit - if not hasattr(ancilla_qubits, '__len__'): + if not hasattr(ancilla_qubits, "__len__"): ancilla_qubits = [ancilla_qubits] if len(ancilla_qubits) < required: actually = len(ancilla_qubits) - raise ValueError('At least {} ancillas required, but {} given.'.format(required, - actually)) + raise ValueError( + "At least {} ancillas required, but {} given.".format(required, actually) + ) # size down if too many ancillas were provided ancilla_qubits = ancilla_qubits[:required] else: @@ -2578,37 +2836,48 @@ def mcx(self, control_qubits, target_qubit, ancilla_qubits=None, mode='noancilla return self.append(gate, control_qubits[:] + [target_qubit] + ancilla_qubits[:], []) - def mct(self, control_qubits, target_qubit, ancilla_qubits=None, mode='noancilla'): + def mct(self, control_qubits, target_qubit, ancilla_qubits=None, mode="noancilla"): """Apply :class:`~qiskit.circuit.library.MCXGate`.""" return self.mcx(control_qubits, target_qubit, ancilla_qubits, mode) def y(self, qubit): """Apply :class:`~qiskit.circuit.library.YGate`.""" from .library.standard_gates.y import YGate + return self.append(YGate(), [qubit], []) - def cy(self, control_qubit, target_qubit, # pylint: disable=invalid-name - label=None, ctrl_state=None): + def cy( + self, + control_qubit, + target_qubit, # pylint: disable=invalid-name + label=None, + ctrl_state=None, + ): """Apply :class:`~qiskit.circuit.library.CYGate`.""" from .library.standard_gates.y import CYGate - return self.append(CYGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CYGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def z(self, qubit): """Apply :class:`~qiskit.circuit.library.ZGate`.""" from .library.standard_gates.z import ZGate + return self.append(ZGate(), [qubit], []) - def cz(self, control_qubit, target_qubit, - label=None, ctrl_state=None): + def cz(self, control_qubit, target_qubit, label=None, ctrl_state=None): """Apply :class:`~qiskit.circuit.library.CZGate`.""" from .library.standard_gates.z import CZGate - return self.append(CZGate(label=label, ctrl_state=ctrl_state), - [control_qubit, target_qubit], []) + + return self.append( + CZGate(label=label, ctrl_state=ctrl_state), [control_qubit, target_qubit], [] + ) def pauli(self, pauli_string, qubits): """Apply :class:`~qiskit.circuit.library.PauliGate`.""" from qiskit.circuit.library.generalized_gates.pauli import PauliGate + return self.append(PauliGate(pauli_string), qubits, []) def add_calibration(self, gate, qubits, schedule, params=None): @@ -2661,8 +2930,9 @@ def qubit_start_time(self, *qubits: Union[Qubit, int]) -> Union[int, float]: # circuit has only delays, this is kind of scheduled for inst, _, _ in self.data: if not isinstance(inst, Delay): - raise CircuitError("qubit_start_time undefined. " - "Circuit must be scheduled first.") + raise CircuitError( + "qubit_start_time undefined. " "Circuit must be scheduled first." + ) return 0 qubits = [self.qubits[q] if isinstance(q, int) else q for q in qubits] @@ -2702,8 +2972,9 @@ def qubit_stop_time(self, *qubits: Union[Qubit, int]) -> Union[int, float]: # circuit has only delays, this is kind of scheduled for inst, _, _ in self.data: if not isinstance(inst, Delay): - raise CircuitError("qubit_stop_time undefined. " - "Circuit must be scheduled first.") + raise CircuitError( + "qubit_stop_time undefined. " "Circuit must be scheduled first." + ) return 0 qubits = [self.qubits[q] if isinstance(q, int) else q for q in qubits] @@ -2728,6 +2999,7 @@ def _circuit_from_qasm(qasm): # pylint: disable=cyclic-import from qiskit.converters import ast_to_dag from qiskit.converters import dag_to_circuit + ast = qasm.parse() dag = ast_to_dag(ast) return dag_to_circuit(dag) diff --git a/qiskit/circuit/quantumcircuitdata.py b/qiskit/circuit/quantumcircuitdata.py index 7180a95855ab..2d92707be0da 100644 --- a/qiskit/circuit/quantumcircuitdata.py +++ b/qiskit/circuit/quantumcircuitdata.py @@ -32,26 +32,23 @@ def __getitem__(self, i): def __setitem__(self, key, value): instruction, qargs, cargs = value - if not isinstance(instruction, Instruction) \ - and hasattr(instruction, 'to_instruction'): + if not isinstance(instruction, Instruction) and hasattr(instruction, "to_instruction"): instruction = instruction.to_instruction() - expanded_qargs = [self._circuit.qbit_argument_conversion(qarg) - for qarg in qargs or []] - expanded_cargs = [self._circuit.cbit_argument_conversion(carg) - for carg in cargs or []] + expanded_qargs = [self._circuit.qbit_argument_conversion(qarg) for qarg in qargs or []] + expanded_cargs = [self._circuit.cbit_argument_conversion(carg) for carg in cargs or []] - broadcast_args = list(instruction.broadcast_arguments(expanded_qargs, - expanded_cargs)) + broadcast_args = list(instruction.broadcast_arguments(expanded_qargs, expanded_cargs)) if len(broadcast_args) > 1: - raise CircuitError('QuantumCircuit.data modification does not ' - 'support argument broadcasting.') + raise CircuitError( + "QuantumCircuit.data modification does not " "support argument broadcasting." + ) qargs, cargs = broadcast_args[0] if not isinstance(instruction, Instruction): - raise CircuitError('object is not an Instruction.') + raise CircuitError("object is not an Instruction.") self._circuit._check_dups(qargs) self._circuit._check_qargs(qargs) diff --git a/qiskit/circuit/quantumregister.py b/qiskit/circuit/quantumregister.py index 21c829181b59..d84251034fd2 100644 --- a/qiskit/circuit/quantumregister.py +++ b/qiskit/circuit/quantumregister.py @@ -37,16 +37,18 @@ def __init__(self, register=None, index=None): if register is None or isinstance(register, QuantumRegister): super().__init__(register, index) else: - raise CircuitError('Qubit needs a QuantumRegister and %s was provided' % - type(register).__name__) + raise CircuitError( + "Qubit needs a QuantumRegister and %s was provided" % type(register).__name__ + ) class QuantumRegister(Register): """Implement a quantum register.""" + # Counter for the number of instances in this class. instances_counter = itertools.count() # Prefix to use for auto naming. - prefix = 'q' + prefix = "q" bit_type = Qubit def qasm(self): @@ -56,13 +58,15 @@ def qasm(self): class AncillaQubit(Qubit): """A qubit used as ancillary qubit.""" + pass class AncillaRegister(QuantumRegister): """Implement an ancilla register.""" + # Counter for the number of instances in this class. instances_counter = itertools.count() # Prefix to use for auto naming. - prefix = 'a' + prefix = "a" bit_type = AncillaQubit diff --git a/qiskit/circuit/random/utils.py b/qiskit/circuit/random/utils.py index e7144144cbb9..ed051c289a10 100644 --- a/qiskit/circuit/random/utils.py +++ b/qiskit/circuit/random/utils.py @@ -16,17 +16,40 @@ from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.circuit import Reset -from qiskit.circuit.library.standard_gates import (IGate, U1Gate, U2Gate, U3Gate, XGate, - YGate, ZGate, HGate, SGate, SdgGate, TGate, - TdgGate, RXGate, RYGate, RZGate, CXGate, - CYGate, CZGate, CHGate, CRZGate, CU1Gate, - CU3Gate, SwapGate, RZZGate, - CCXGate, CSwapGate) +from qiskit.circuit.library.standard_gates import ( + IGate, + U1Gate, + U2Gate, + U3Gate, + XGate, + YGate, + ZGate, + HGate, + SGate, + SdgGate, + TGate, + TdgGate, + RXGate, + RYGate, + RZGate, + CXGate, + CYGate, + CZGate, + CHGate, + CRZGate, + CU1Gate, + CU3Gate, + SwapGate, + RZZGate, + CCXGate, + CSwapGate, +) from qiskit.circuit.exceptions import CircuitError -def random_circuit(num_qubits, depth, max_operands=3, measure=False, - conditional=False, reset=False, seed=None): +def random_circuit( + num_qubits, depth, max_operands=3, measure=False, conditional=False, reset=False, seed=None +): """Generate random circuit of arbitrary size and form. This function will generate a random circuit by randomly selecting gates @@ -57,20 +80,34 @@ def random_circuit(num_qubits, depth, max_operands=3, measure=False, if max_operands < 1 or max_operands > 3: raise CircuitError("max_operands must be between 1 and 3") - one_q_ops = [IGate, U1Gate, U2Gate, U3Gate, XGate, YGate, ZGate, - HGate, SGate, SdgGate, TGate, TdgGate, RXGate, RYGate, RZGate] + one_q_ops = [ + IGate, + U1Gate, + U2Gate, + U3Gate, + XGate, + YGate, + ZGate, + HGate, + SGate, + SdgGate, + TGate, + TdgGate, + RXGate, + RYGate, + RZGate, + ] one_param = [U1Gate, RXGate, RYGate, RZGate, RZZGate, CU1Gate, CRZGate] two_param = [U2Gate] three_param = [U3Gate, CU3Gate] - two_q_ops = [CXGate, CYGate, CZGate, CHGate, CRZGate, - CU1Gate, CU3Gate, SwapGate, RZZGate] + two_q_ops = [CXGate, CYGate, CZGate, CHGate, CRZGate, CU1Gate, CU3Gate, SwapGate, RZZGate] three_q_ops = [CCXGate, CSwapGate] - qr = QuantumRegister(num_qubits, 'q') + qr = QuantumRegister(num_qubits, "q") qc = QuantumCircuit(num_qubits) if measure or conditional: - cr = ClassicalRegister(num_qubits, 'c') + cr = ClassicalRegister(num_qubits, "c") qc.add_register(cr) if reset: diff --git a/qiskit/circuit/register.py b/qiskit/circuit/register.py index edde14e757fc..d85d5872264c 100644 --- a/qiskit/circuit/register.py +++ b/qiskit/circuit/register.py @@ -25,16 +25,16 @@ class Register: """Implement a generic register.""" - __slots__ = ['_name', '_size', '_bits', '_hash', '_repr'] + __slots__ = ["_name", "_size", "_bits", "_hash", "_repr"] # Register name should conform to OpenQASM 2.0 specification # See appendix A of https://arxiv.org/pdf/1707.03429v2.pdf - name_format = re.compile('[a-z][a-zA-Z0-9_]*') + name_format = re.compile("[a-z][a-zA-Z0-9_]*") # Counter for the number of instances in this class. instances_counter = itertools.count() # Prefix to use for auto naming. - prefix = 'reg' + prefix = "reg" bit_type = None def __init__(self, size=None, name=None, bits=None): @@ -60,13 +60,11 @@ def __init__(self, size=None, name=None, bits=None): CircuitError: if ``bits`` contained bits of an incorrect type. """ - if ( - (size, bits) == (None, None) - or (size is not None and bits is not None) - ): - raise CircuitError("Exactly one of the size or bits arguments can be " - "provided. Provided size=%s bits=%s." - % (size, bits)) + if (size, bits) == (None, None) or (size is not None and bits is not None): + raise CircuitError( + "Exactly one of the size or bits arguments can be " + "provided. Provided size=%s bits=%s." % (size, bits) + ) # validate (or cast) size if bits is not None: @@ -78,26 +76,34 @@ def __init__(self, size=None, name=None, bits=None): valid_size = False if not valid_size: - raise CircuitError("Register size must be an integer. (%s '%s' was provided)" - % (type(size).__name__, size)) + raise CircuitError( + "Register size must be an integer. (%s '%s' was provided)" + % (type(size).__name__, size) + ) size = int(size) # cast to int if size <= 0: - raise CircuitError("Register size must be positive (%s '%s' was provided)" - % (type(size).__name__, size)) + raise CircuitError( + "Register size must be positive (%s '%s' was provided)" + % (type(size).__name__, size) + ) # validate (or cast) name if name is None: - name = '%s%i' % (self.prefix, next(self.instances_counter)) + name = "%s%i" % (self.prefix, next(self.instances_counter)) else: try: name = str(name) except Exception as ex: - raise CircuitError("The circuit name should be castable to a string " - "(or None for autogenerate a name).") from ex + raise CircuitError( + "The circuit name should be castable to a string " + "(or None for autogenerate a name)." + ) from ex if self.name_format.match(name) is None: - raise CircuitError("%s is an invalid OPENQASM register name. See appendix" - " A of https://arxiv.org/pdf/1707.03429v2.pdf." % name) + raise CircuitError( + "%s is an invalid OPENQASM register name. See appendix" + " A of https://arxiv.org/pdf/1707.03429v2.pdf." % name + ) self._name = name self._size = size @@ -107,8 +113,9 @@ def __init__(self, size=None, name=None, bits=None): if bits is not None: # pylint: disable=isinstance-second-argument-not-valid-type if any(not isinstance(bit, self.bit_type) for bit in bits): - raise CircuitError("Provided bits did not all match " - "register type. bits=%s" % bits) + raise CircuitError( + "Provided bits did not all match " "register type. bits=%s" % bits + ) self._bits = list(bits) else: self._bits = [self.bit_type(self, idx) for idx in range(size)] @@ -153,7 +160,7 @@ def __getitem__(self, key): if max(key) < len(self): return [self._bits[idx] for idx in key] else: - raise CircuitError('register index out of range') + raise CircuitError("register index out of range") else: return self._bits[key] @@ -178,16 +185,16 @@ def __eq__(self, other): return True res = False - if type(self) is type(other) and \ - self._repr == other._repr and \ - all( - # For new-style bits, check bitwise equality. - sbit == obit - for sbit, obit in zip(self, other) - if None in - (sbit._register, sbit._index, - obit._register, obit._index) - ): + if ( + type(self) is type(other) + and self._repr == other._repr + and all( + # For new-style bits, check bitwise equality. + sbit == obit + for sbit, obit in zip(self, other) + if None in (sbit._register, sbit._index, obit._register, obit._index) + ) + ): res = True return res diff --git a/qiskit/circuit/tools/pi_check.py b/qiskit/circuit/tools/pi_check.py index 67efe26c3ebc..6bb2a07f8894 100644 --- a/qiskit/circuit/tools/pi_check.py +++ b/qiskit/circuit/tools/pi_check.py @@ -19,14 +19,14 @@ from qiskit.exceptions import QiskitError MAX_FRAC = 16 -N, D = np.meshgrid(np.arange(1, MAX_FRAC+1), np.arange(1, MAX_FRAC+1)) +N, D = np.meshgrid(np.arange(1, MAX_FRAC + 1), np.arange(1, MAX_FRAC + 1)) FRAC_MESH = N / D * np.pi RECIP_MESH = N / D / np.pi POW_LIST = np.pi ** np.arange(2, 5) -def pi_check(inpt, eps=1e-6, output='text', ndigits=5): - """ Computes if a number is close to an integer +def pi_check(inpt, eps=1e-6, output="text", ndigits=5): + """Computes if a number is close to an integer fraction or multiple of PI and returns the corresponding string. @@ -56,7 +56,7 @@ def pi_check(inpt, eps=1e-6, output='text', ndigits=5): except (ValueError, TypeError): # Strip leading '-' from pi since must replace with abs(sym) # in order to preserve spacing around minuses in expression - if pi[0] == '-': + if pi[0] == "-": pi = pi[1:] param_str = param_str.replace(str(abs(sym)), pi) return param_str @@ -65,21 +65,20 @@ def pi_check(inpt, eps=1e-6, output='text', ndigits=5): def normalize(single_inpt): if abs(single_inpt) < 1e-14: - return '0' - - if output == 'text': - pi = 'π' - elif output == 'qasm': - pi = 'pi' - elif output == 'latex': - pi = '\\pi' - elif output == 'mpl': - pi = '$\\pi$' + return "0" + + if output == "text": + pi = "π" + elif output == "qasm": + pi = "pi" + elif output == "latex": + pi = "\\pi" + elif output == "mpl": + pi = "$\\pi$" else: - raise QiskitError('pi_check parameter output should be text, ' - 'latex, mpl, or qasm.') + raise QiskitError("pi_check parameter output should be text, " "latex, mpl, or qasm.") - neg_str = '-' if single_inpt < 0 else '' + neg_str = "-" if single_inpt < 0 else "" # First check is for whole multiples of pi val = single_inpt / np.pi @@ -87,32 +86,32 @@ def normalize(single_inpt): if abs(abs(val) - abs(round(val))) < eps: val = int(abs(round(val))) if abs(val) == 1: - str_out = '{}{}'.format(neg_str, pi) + str_out = "{}{}".format(neg_str, pi) else: - if output == 'qasm': - str_out = '{}{}*{}'.format(neg_str, val, pi) + if output == "qasm": + str_out = "{}{}*{}".format(neg_str, val, pi) else: - str_out = '{}{}{}'.format(neg_str, val, pi) + str_out = "{}{}{}".format(neg_str, val, pi) return str_out # Second is a check for powers of pi if abs(single_inpt) > np.pi: power = np.where(abs(abs(single_inpt) - POW_LIST) < eps) if power[0].shape[0]: - if output == 'qasm': - str_out = '{:.{}g}'.format(single_inpt, ndigits) - elif output == 'latex': - str_out = '{}{}^{}'.format(neg_str, pi, power[0][0] + 2) - elif output == 'mpl': - str_out = '{}{}$^{}$'.format(neg_str, pi, power[0][0] + 2) + if output == "qasm": + str_out = "{:.{}g}".format(single_inpt, ndigits) + elif output == "latex": + str_out = "{}{}^{}".format(neg_str, pi, power[0][0] + 2) + elif output == "mpl": + str_out = "{}{}$^{}$".format(neg_str, pi, power[0][0] + 2) else: - str_out = '{}{}**{}'.format(neg_str, pi, power[0][0] + 2) + str_out = "{}{}**{}".format(neg_str, pi, power[0][0] + 2) return str_out # Third is a check for a number larger than MAX_FRAC * pi, not a # multiple or power of pi, since no fractions will exceed MAX_FRAC * pi if abs(single_inpt) >= (MAX_FRAC * np.pi): - str_out = '{:.{}g}'.format(single_inpt, ndigits) + str_out = "{:.{}g}".format(single_inpt, ndigits) return str_out # Fourth check is for fractions for 1*pi in the numer and any @@ -120,10 +119,10 @@ def normalize(single_inpt): val = np.pi / single_inpt if abs(abs(val) - abs(round(val))) < eps: val = int(abs(round(val))) - if output == 'latex': - str_out = '\\frac{%s%s}{%s}' % (neg_str, pi, val) + if output == "latex": + str_out = "\\frac{%s%s}{%s}" % (neg_str, pi, val) else: - str_out = '{}{}/{}'.format(neg_str, pi, val) + str_out = "{}{}/{}".format(neg_str, pi, val) return str_out # Fifth check is for fractions where the numer > 1*pi and numer @@ -133,12 +132,12 @@ def normalize(single_inpt): if frac[0].shape[0]: numer = int(frac[1][0]) + 1 denom = int(frac[0][0]) + 1 - if output == 'latex': - str_out = '\\frac{%s%s%s}{%s}' % (neg_str, numer, pi, denom) - elif output == 'qasm': - str_out = '{}{}*{}/{}'.format(neg_str, numer, pi, denom) + if output == "latex": + str_out = "\\frac{%s%s%s}{%s}" % (neg_str, numer, pi, denom) + elif output == "qasm": + str_out = "{}{}*{}/{}".format(neg_str, numer, pi, denom) else: - str_out = '{}{}{}/{}'.format(neg_str, numer, pi, denom) + str_out = "{}{}{}/{}".format(neg_str, numer, pi, denom) return str_out # Sixth check is for fractions where the numer > 1 and numer @@ -148,32 +147,32 @@ def normalize(single_inpt): if frac[0].shape[0]: numer = int(frac[1][0]) + 1 denom = int(frac[0][0]) + 1 - if denom == 1 and output != 'qasm': - denom = '' - if output == 'latex': - str_out = '\\frac{%s%s}{%s%s}' % (neg_str, numer, denom, pi) - elif output == 'qasm': - str_out = '{}{}/({}*{})'.format(neg_str, numer, denom, pi) + if denom == 1 and output != "qasm": + denom = "" + if output == "latex": + str_out = "\\frac{%s%s}{%s%s}" % (neg_str, numer, denom, pi) + elif output == "qasm": + str_out = "{}{}/({}*{})".format(neg_str, numer, denom, pi) else: - str_out = '{}{}/{}{}'.format(neg_str, numer, denom, pi) + str_out = "{}{}/{}{}".format(neg_str, numer, denom, pi) return str_out # Nothing found - str_out = '{:.{}g}'.format(single_inpt, ndigits) + str_out = "{:.{}g}".format(single_inpt, ndigits) return str_out complex_inpt = complex(inpt) real, imag = map(normalize, [complex_inpt.real, complex_inpt.imag]) - jstr = '\\jmath' if output == 'latex' else 'j' - if real == '0' and imag != '0': + jstr = "\\jmath" if output == "latex" else "j" + if real == "0" and imag != "0": str_out = imag + jstr - elif real != '0' and imag != '0': - op_str = '+' + elif real != "0" and imag != "0": + op_str = "+" # Remove + if imag negative except for latex fractions - if complex_inpt.imag < 0 and (output != 'latex' or '\\frac' not in imag): - op_str = '' - str_out = '{}{}{}{}'.format(real, op_str, imag, jstr) + if complex_inpt.imag < 0 and (output != "latex" or "\\frac" not in imag): + op_str = "" + str_out = "{}{}{}{}".format(real, op_str, imag, jstr) else: str_out = real return str_out diff --git a/qiskit/compiler/assembler.py b/qiskit/compiler/assembler.py index 202b9e72acb0..96fc77e4943b 100644 --- a/qiskit/compiler/assembler.py +++ b/qiskit/compiler/assembler.py @@ -40,31 +40,43 @@ def _log_assembly_time(start_time, end_time): # TODO: parallelize over the experiments (serialize each separately, then add global header/config) -def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], - Schedule, List[Schedule], - ScheduleBlock, Union[ScheduleBlock]], - backend: Optional[Union[Backend, BaseBackend]] = None, - qobj_id: Optional[str] = None, - qobj_header: Optional[Union[QobjHeader, Dict]] = None, - shots: Optional[int] = None, memory: Optional[bool] = False, - max_credits: Optional[int] = None, - seed_simulator: Optional[int] = None, - qubit_lo_freq: Optional[List[int]] = None, - meas_lo_freq: Optional[List[int]] = None, - qubit_lo_range: Optional[List[int]] = None, - meas_lo_range: Optional[List[int]] = None, - schedule_los: Optional[Union[List[Union[Dict[PulseChannel, float], LoConfig]], - Union[Dict[PulseChannel, float], LoConfig]]] = None, - meas_level: Union[int, MeasLevel] = MeasLevel.CLASSIFIED, - meas_return: Union[str, MeasReturnType] = MeasReturnType.AVERAGE, - meas_map: Optional[List[List[Qubit]]] = None, - memory_slot_size: int = 100, - rep_time: Optional[int] = None, - rep_delay: Optional[float] = None, - parameter_binds: Optional[List[Dict[Parameter, float]]] = None, - parametric_pulses: Optional[List[str]] = None, - init_qubits: bool = True, - **run_config: Dict) -> Qobj: +def assemble( + experiments: Union[ + QuantumCircuit, + List[QuantumCircuit], + Schedule, + List[Schedule], + ScheduleBlock, + Union[ScheduleBlock], + ], + backend: Optional[Union[Backend, BaseBackend]] = None, + qobj_id: Optional[str] = None, + qobj_header: Optional[Union[QobjHeader, Dict]] = None, + shots: Optional[int] = None, + memory: Optional[bool] = False, + max_credits: Optional[int] = None, + seed_simulator: Optional[int] = None, + qubit_lo_freq: Optional[List[int]] = None, + meas_lo_freq: Optional[List[int]] = None, + qubit_lo_range: Optional[List[int]] = None, + meas_lo_range: Optional[List[int]] = None, + schedule_los: Optional[ + Union[ + List[Union[Dict[PulseChannel, float], LoConfig]], + Union[Dict[PulseChannel, float], LoConfig], + ] + ] = None, + meas_level: Union[int, MeasLevel] = MeasLevel.CLASSIFIED, + meas_return: Union[str, MeasReturnType] = MeasReturnType.AVERAGE, + meas_map: Optional[List[List[Qubit]]] = None, + memory_slot_size: int = 100, + rep_time: Optional[int] = None, + rep_delay: Optional[float] = None, + parameter_binds: Optional[List[Dict[Parameter, float]]] = None, + parametric_pulses: Optional[List[str]] = None, + init_qubits: bool = True, + **run_config: Dict, +) -> Qobj: """Assemble a list of circuits or pulse schedules into a ``Qobj``. This function serializes the payloads, which could be either circuits or schedules, @@ -138,47 +150,85 @@ def assemble(experiments: Union[QuantumCircuit, List[QuantumCircuit], """ start_time = time() experiments = experiments if isinstance(experiments, list) else [experiments] - qobj_id, qobj_header, run_config_common_dict = _parse_common_args(backend, qobj_id, qobj_header, - shots, memory, max_credits, - seed_simulator, init_qubits, - rep_delay, **run_config) + qobj_id, qobj_header, run_config_common_dict = _parse_common_args( + backend, + qobj_id, + qobj_header, + shots, + memory, + max_credits, + seed_simulator, + init_qubits, + rep_delay, + **run_config, + ) # assemble either circuits or schedules if all(isinstance(exp, QuantumCircuit) for exp in experiments): - run_config = _parse_circuit_args(parameter_binds, backend, meas_level, - meas_return, parametric_pulses, - **run_config_common_dict) + run_config = _parse_circuit_args( + parameter_binds, + backend, + meas_level, + meas_return, + parametric_pulses, + **run_config_common_dict, + ) # If circuits are parameterized, bind parameters and remove from run_config - bound_experiments, run_config = _expand_parameters(circuits=experiments, - run_config=run_config) + bound_experiments, run_config = _expand_parameters( + circuits=experiments, run_config=run_config + ) end_time = time() _log_assembly_time(start_time, end_time) - return assemble_circuits(circuits=bound_experiments, qobj_id=qobj_id, - qobj_header=qobj_header, run_config=run_config) + return assemble_circuits( + circuits=bound_experiments, + qobj_id=qobj_id, + qobj_header=qobj_header, + run_config=run_config, + ) elif all(isinstance(exp, (ScheduleBlock, Schedule, Instruction)) for exp in experiments): - run_config = _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, - qubit_lo_range, meas_lo_range, - schedule_los, meas_level, meas_return, - meas_map, memory_slot_size, - rep_time, parametric_pulses, - **run_config_common_dict) + run_config = _parse_pulse_args( + backend, + qubit_lo_freq, + meas_lo_freq, + qubit_lo_range, + meas_lo_range, + schedule_los, + meas_level, + meas_return, + meas_map, + memory_slot_size, + rep_time, + parametric_pulses, + **run_config_common_dict, + ) end_time = time() _log_assembly_time(start_time, end_time) - return assemble_schedules(schedules=experiments, qobj_id=qobj_id, - qobj_header=qobj_header, run_config=run_config) + return assemble_schedules( + schedules=experiments, qobj_id=qobj_id, qobj_header=qobj_header, run_config=run_config + ) else: - raise QiskitError("bad input to assemble() function; " - "must be either circuits or schedules") + raise QiskitError( + "bad input to assemble() function; " "must be either circuits or schedules" + ) # TODO: rework to return a list of RunConfigs (one for each experiments), and a global one -def _parse_common_args(backend, qobj_id, qobj_header, shots, - memory, max_credits, seed_simulator, - init_qubits, rep_delay, **run_config): +def _parse_common_args( + backend, + qobj_id, + qobj_header, + shots, + memory, + max_credits, + seed_simulator, + init_qubits, + rep_delay, + **run_config, +): """Resolve the various types of args allowed to the assemble() function through duck typing, overriding args, etc. Refer to the assemble() docstring for details on what types of inputs are allowed. @@ -202,8 +252,9 @@ def _parse_common_args(backend, qobj_id, qobj_header, shots, backend_config = backend.configuration() # check for memory flag applied to backend that does not support memory if memory and not backend_config.memory: - raise QiskitError("memory not supported by backend {}" - .format(backend_config.backend_name)) + raise QiskitError( + "memory not supported by backend {}".format(backend_config.backend_name) + ) # an identifier for the Qobj qobj_id = qobj_id or str(uuid.uuid4()) @@ -213,27 +264,29 @@ def _parse_common_args(backend, qobj_id, qobj_header, shots, qobj_header = qobj_header or {} if isinstance(qobj_header, QobjHeader): qobj_header = qobj_header.to_dict() - backend_name = getattr(backend_config, 'backend_name', None) - backend_version = getattr(backend_config, 'backend_version', None) - qobj_header = {**dict(backend_name=backend_name, backend_version=backend_version), - **qobj_header} + backend_name = getattr(backend_config, "backend_name", None) + backend_version = getattr(backend_config, "backend_version", None) + qobj_header = { + **dict(backend_name=backend_name, backend_version=backend_version), + **qobj_header, + } qobj_header = QobjHeader(**{k: v for k, v in qobj_header.items() if v is not None}) - max_shots = getattr(backend_config, 'max_shots', None) + max_shots = getattr(backend_config, "max_shots", None) if shots is None: if max_shots: shots = min(1024, max_shots) else: shots = 1024 elif not isinstance(shots, int): - raise QiskitError( - "Argument 'shots' should be of type 'int'") + raise QiskitError("Argument 'shots' should be of type 'int'") elif max_shots and max_shots < shots: raise QiskitError( - 'Number of shots specified: %s exceeds max_shots property of the ' - 'backend: %s.' % (shots, max_shots)) + "Number of shots specified: %s exceeds max_shots property of the " + "backend: %s." % (shots, max_shots) + ) - dynamic_reprate_enabled = getattr(backend_config, 'dynamic_reprate_enabled', False) + dynamic_reprate_enabled = getattr(backend_config, "dynamic_reprate_enabled", False) if dynamic_reprate_enabled: default_rep_delay = getattr(backend_config, "default_rep_delay", None) rep_delay_range = getattr(backend_config, "rep_delay_range", None) @@ -247,23 +300,34 @@ def _parse_common_args(backend, qobj_id, qobj_header, shots, ) # create run configuration and populate - run_config_dict = dict(shots=shots, - memory=memory, - max_credits=max_credits, - seed_simulator=seed_simulator, - init_qubits=init_qubits, - rep_delay=rep_delay, - **run_config) + run_config_dict = dict( + shots=shots, + memory=memory, + max_credits=max_credits, + seed_simulator=seed_simulator, + init_qubits=init_qubits, + rep_delay=rep_delay, + **run_config, + ) return qobj_id, qobj_header, run_config_dict -def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range, - meas_lo_range, schedule_los, meas_level, - meas_return, meas_map, - memory_slot_size, - rep_time, parametric_pulses, - **run_config): +def _parse_pulse_args( + backend, + qubit_lo_freq, + meas_lo_freq, + qubit_lo_range, + meas_lo_range, + schedule_los, + meas_level, + meas_return, + meas_map, + memory_slot_size, + rep_time, + parametric_pulses, + **run_config, +): """Build a pulse RunConfig replacing unset arguments with defaults derived from the `backend`. See `assemble` for more information on the required arguments. @@ -280,63 +344,72 @@ def _parse_pulse_args(backend, qubit_lo_freq, meas_lo_freq, qubit_lo_range, backend_default = backend.defaults() backend_config = backend.configuration() - if meas_level not in getattr(backend_config, 'meas_levels', [MeasLevel.CLASSIFIED]): + if meas_level not in getattr(backend_config, "meas_levels", [MeasLevel.CLASSIFIED]): raise SchemaValidationError( - ('meas_level = {} not supported for backend {}, only {} is supported' - ).format(meas_level, backend_config.backend_name, backend_config.meas_levels) + ("meas_level = {} not supported for backend {}, only {} is supported").format( + meas_level, backend_config.backend_name, backend_config.meas_levels + ) ) - meas_map = meas_map or getattr(backend_config, 'meas_map', None) + meas_map = meas_map or getattr(backend_config, "meas_map", None) schedule_los = schedule_los or [] if isinstance(schedule_los, (LoConfig, dict)): schedule_los = [schedule_los] # Convert to LoConfig if LO configuration supplied as dictionary - schedule_los = [lo_config if isinstance(lo_config, LoConfig) else LoConfig(lo_config) - for lo_config in schedule_los] + schedule_los = [ + lo_config if isinstance(lo_config, LoConfig) else LoConfig(lo_config) + for lo_config in schedule_los + ] - if not qubit_lo_freq and hasattr(backend_default, 'qubit_freq_est'): + if not qubit_lo_freq and hasattr(backend_default, "qubit_freq_est"): qubit_lo_freq = backend_default.qubit_freq_est - if not meas_lo_freq and hasattr(backend_default, 'meas_freq_est'): + if not meas_lo_freq and hasattr(backend_default, "meas_freq_est"): meas_lo_freq = backend_default.meas_freq_est - qubit_lo_range = qubit_lo_range or getattr(backend_config, 'qubit_lo_range', None) - meas_lo_range = meas_lo_range or getattr(backend_config, 'meas_lo_range', None) + qubit_lo_range = qubit_lo_range or getattr(backend_config, "qubit_lo_range", None) + meas_lo_range = meas_lo_range or getattr(backend_config, "meas_lo_range", None) - dynamic_reprate_enabled = getattr(backend_config, 'dynamic_reprate_enabled', False) + dynamic_reprate_enabled = getattr(backend_config, "dynamic_reprate_enabled", False) - rep_time = rep_time or getattr(backend_config, 'rep_times', None) + rep_time = rep_time or getattr(backend_config, "rep_times", None) if rep_time: if dynamic_reprate_enabled: - warnings.warn("Dynamic rep rates are supported on this backend. 'rep_delay' will be " - "used instead of 'rep_time'.", RuntimeWarning) + warnings.warn( + "Dynamic rep rates are supported on this backend. 'rep_delay' will be " + "used instead of 'rep_time'.", + RuntimeWarning, + ) if isinstance(rep_time, list): rep_time = rep_time[0] rep_time = int(rep_time * 1e6) # convert sec to μs - parametric_pulses = parametric_pulses or getattr(backend_config, 'parametric_pulses', []) + parametric_pulses = parametric_pulses or getattr(backend_config, "parametric_pulses", []) # create run configuration and populate - run_config_dict = dict(qubit_lo_freq=qubit_lo_freq, - meas_lo_freq=meas_lo_freq, - qubit_lo_range=qubit_lo_range, - meas_lo_range=meas_lo_range, - schedule_los=schedule_los, - meas_level=meas_level, - meas_return=meas_return, - meas_map=meas_map, - memory_slot_size=memory_slot_size, - rep_time=rep_time, - parametric_pulses=parametric_pulses, - **run_config) + run_config_dict = dict( + qubit_lo_freq=qubit_lo_freq, + meas_lo_freq=meas_lo_freq, + qubit_lo_range=qubit_lo_range, + meas_lo_range=meas_lo_range, + schedule_los=schedule_los, + meas_level=meas_level, + meas_return=meas_return, + meas_map=meas_map, + memory_slot_size=memory_slot_size, + rep_time=rep_time, + parametric_pulses=parametric_pulses, + **run_config, + ) run_config = RunConfig(**{k: v for k, v in run_config_dict.items() if v is not None}) return run_config -def _parse_circuit_args(parameter_binds, backend, meas_level, meas_return, - parametric_pulses, **run_config): +def _parse_circuit_args( + parameter_binds, backend, meas_level, meas_return, parametric_pulses, **run_config +): """Build a circuit RunConfig replacing unset arguments with defaults derived from the `backend`. See `assemble` for more information on the required arguments. @@ -348,27 +421,26 @@ def _parse_circuit_args(parameter_binds, backend, meas_level, meas_return, # create run configuration and populate run_config_dict = dict(parameter_binds=parameter_binds, **run_config) if backend: - run_config_dict['parametric_pulses'] = getattr(backend.configuration(), 'parametric_pulses', - []) + run_config_dict["parametric_pulses"] = getattr( + backend.configuration(), "parametric_pulses", [] + ) if parametric_pulses: - run_config_dict['parametric_pulses'] = parametric_pulses + run_config_dict["parametric_pulses"] = parametric_pulses if meas_level: - run_config_dict['meas_level'] = meas_level + run_config_dict["meas_level"] = meas_level # only enable `meas_return` if `meas_level` isn't classified if meas_level != MeasLevel.CLASSIFIED: - run_config_dict['meas_return'] = meas_return + run_config_dict["meas_return"] = meas_return - run_config = RunConfig( - **{k: v - for k, v in run_config_dict.items() if v is not None}) + run_config = RunConfig(**{k: v for k, v in run_config_dict.items() if v is not None}) return run_config -def _parse_rep_delay(rep_delay: float, - default_rep_delay: float, - rep_delay_range: List[float]) -> float: +def _parse_rep_delay( + rep_delay: float, default_rep_delay: float, rep_delay_range: List[float] +) -> float: """Parse and set ``rep_delay`` parameter in runtime config. Args: @@ -422,33 +494,40 @@ def _expand_parameters(circuits, run_config): """ parameter_binds = run_config.parameter_binds - if parameter_binds or \ - any(circuit.parameters for circuit in circuits): + if parameter_binds or any(circuit.parameters for circuit in circuits): # Unroll params here in order to handle ParamVects - all_bind_parameters = [QuantumCircuit()._unroll_param_dict(bind).keys() - for bind in parameter_binds] + all_bind_parameters = [ + QuantumCircuit()._unroll_param_dict(bind).keys() for bind in parameter_binds + ] all_circuit_parameters = [circuit.parameters for circuit in circuits] # Collect set of all unique parameters across all circuits and binds - unique_parameters = {param - for param_list in all_bind_parameters + all_circuit_parameters - for param in param_list} + unique_parameters = { + param + for param_list in all_bind_parameters + all_circuit_parameters + for param in param_list + } # Check that all parameters are common to all circuits and binds - if not all_bind_parameters \ - or not all_circuit_parameters \ - or any(unique_parameters != bind_params for bind_params in all_bind_parameters) \ - or any(unique_parameters != parameters for parameters in all_circuit_parameters): + if ( + not all_bind_parameters + or not all_circuit_parameters + or any(unique_parameters != bind_params for bind_params in all_bind_parameters) + or any(unique_parameters != parameters for parameters in all_circuit_parameters) + ): raise QiskitError( - ('Mismatch between run_config.parameter_binds and all circuit parameters. ' + - 'Parameter binds: {} ' + - 'Circuit parameters: {}').format(all_bind_parameters, all_circuit_parameters)) + ( + "Mismatch between run_config.parameter_binds and all circuit parameters. " + + "Parameter binds: {} " + + "Circuit parameters: {}" + ).format(all_bind_parameters, all_circuit_parameters) + ) - circuits = [circuit.bind_parameters(binds) - for circuit in circuits - for binds in parameter_binds] + circuits = [ + circuit.bind_parameters(binds) for circuit in circuits for binds in parameter_binds + ] # All parameters have been expanded and bound, so remove from run_config run_config = copy.deepcopy(run_config) diff --git a/qiskit/compiler/scheduler.py b/qiskit/compiler/scheduler.py index 51cfa9c3ec55..090074751531 100644 --- a/qiskit/compiler/scheduler.py +++ b/qiskit/compiler/scheduler.py @@ -35,12 +35,14 @@ def _log_schedule_time(start_time, end_time): logger.info(log_msg) -def schedule(circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Optional[Union[Backend, BaseBackend]] = None, - inst_map: Optional[InstructionScheduleMap] = None, - meas_map: Optional[List[List[int]]] = None, - dt: Optional[float] = None, - method: Optional[Union[str, List[str]]] = None) -> Union[Schedule, List[Schedule]]: +def schedule( + circuits: Union[QuantumCircuit, List[QuantumCircuit]], + backend: Optional[Union[Backend, BaseBackend]] = None, + inst_map: Optional[InstructionScheduleMap] = None, + meas_map: Optional[List[List[int]]] = None, + dt: Optional[float] = None, + method: Optional[Union[str, List[str]]] = None, +) -> Union[Schedule, List[Schedule]]: """ Schedule a circuit to a pulse ``Schedule``, using the backend, according to any specified methods. Supported methods are documented in :py:mod:`qiskit.scheduler.schedule_circuit`. @@ -66,12 +68,14 @@ def schedule(circuits: Union[QuantumCircuit, List[QuantumCircuit]], start_time = time() if inst_map is None: if backend is None: - raise QiskitError("Must supply either a backend or InstructionScheduleMap for " - "scheduling passes.") + raise QiskitError( + "Must supply either a backend or InstructionScheduleMap for " "scheduling passes." + ) defaults = backend.defaults() if defaults is None: - raise QiskitError("The backend defaults are unavailable. The backend may not " - "support pulse.") + raise QiskitError( + "The backend defaults are unavailable. The backend may not " "support pulse." + ) inst_map = defaults.instruction_schedule_map if meas_map is None: if backend is None: diff --git a/qiskit/compiler/sequencer.py b/qiskit/compiler/sequencer.py index 0942cdfced2a..da502a1eda05 100644 --- a/qiskit/compiler/sequencer.py +++ b/qiskit/compiler/sequencer.py @@ -24,11 +24,13 @@ from qiskit.scheduler.sequence import sequence as _sequence -def sequence(scheduled_circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Optional[Union[Backend, BaseBackend]] = None, - inst_map: Optional[InstructionScheduleMap] = None, - meas_map: Optional[List[List[int]]] = None, - dt: Optional[float] = None) -> Union[Schedule, List[Schedule]]: +def sequence( + scheduled_circuits: Union[QuantumCircuit, List[QuantumCircuit]], + backend: Optional[Union[Backend, BaseBackend]] = None, + inst_map: Optional[InstructionScheduleMap] = None, + meas_map: Optional[List[List[int]]] = None, + dt: Optional[float] = None, +) -> Union[Schedule, List[Schedule]]: """ Schedule a scheduled circuit to a pulse ``Schedule``, using the backend. diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index 81ad5f629697..6c0fc799aac1 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -33,34 +33,36 @@ from qiskit.transpiler.instruction_durations import InstructionDurations, InstructionDurationsType from qiskit.transpiler.passes import ApplyLayout from qiskit.transpiler.passmanager_config import PassManagerConfig -from qiskit.transpiler.preset_passmanagers import (level_0_pass_manager, - level_1_pass_manager, - level_2_pass_manager, - level_3_pass_manager) +from qiskit.transpiler.preset_passmanagers import ( + level_0_pass_manager, + level_1_pass_manager, + level_2_pass_manager, + level_3_pass_manager, +) logger = logging.getLogger(__name__) -def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]], - backend: Optional[Union[Backend, BaseBackend]] = None, - basis_gates: Optional[List[str]] = None, - coupling_map: Optional[Union[CouplingMap, List[List[int]]]] = None, - backend_properties: Optional[BackendProperties] = None, - initial_layout: Optional[Union[Layout, Dict, List]] = None, - layout_method: Optional[str] = None, - routing_method: Optional[str] = None, - translation_method: Optional[str] = None, - scheduling_method: Optional[str] = None, - instruction_durations: Optional[InstructionDurationsType] = None, - dt: Optional[float] = None, - approximation_degree: Optional[float] = None, - seed_transpiler: Optional[int] = None, - optimization_level: Optional[int] = None, - pass_manager: Optional[PassManager] = None, - callback: Optional[Callable[[BasePass, DAGCircuit, float, - PropertySet, int], Any]] = None, - output_name: Optional[Union[str, List[str]]] = None) -> Union[QuantumCircuit, - List[QuantumCircuit]]: +def transpile( + circuits: Union[QuantumCircuit, List[QuantumCircuit]], + backend: Optional[Union[Backend, BaseBackend]] = None, + basis_gates: Optional[List[str]] = None, + coupling_map: Optional[Union[CouplingMap, List[List[int]]]] = None, + backend_properties: Optional[BackendProperties] = None, + initial_layout: Optional[Union[Layout, Dict, List]] = None, + layout_method: Optional[str] = None, + routing_method: Optional[str] = None, + translation_method: Optional[str] = None, + scheduling_method: Optional[str] = None, + instruction_durations: Optional[InstructionDurationsType] = None, + dt: Optional[float] = None, + approximation_degree: Optional[float] = None, + seed_transpiler: Optional[int] = None, + optimization_level: Optional[int] = None, + pass_manager: Optional[PassManager] = None, + callback: Optional[Callable[[BasePass, DAGCircuit, float, PropertySet, int], Any]] = None, + output_name: Optional[Union[str, List[str]]] = None, +) -> Union[QuantumCircuit, List[QuantumCircuit]]: """Transpile one or more circuits, according to some desired transpilation targets. All arguments may be given as either a singleton or list. In case of a list, @@ -207,37 +209,61 @@ def callback_func(**kwargs): return circuits[0] if pass_manager is not None: - _check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates, - coupling_map=coupling_map, seed_transpiler=seed_transpiler, - backend_properties=backend_properties, - initial_layout=initial_layout, layout_method=layout_method, - routing_method=routing_method, - translation_method=translation_method, - approximation_degree=approximation_degree, - backend=backend) - - warnings.warn("The parameter pass_manager in transpile is being deprecated. " - "The preferred way to tranpile a circuit using a custom pass manager is" - " pass_manager.run(circuit)", DeprecationWarning, stacklevel=2) + _check_conflicting_argument( + optimization_level=optimization_level, + basis_gates=basis_gates, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + backend_properties=backend_properties, + initial_layout=initial_layout, + layout_method=layout_method, + routing_method=routing_method, + translation_method=translation_method, + approximation_degree=approximation_degree, + backend=backend, + ) + + warnings.warn( + "The parameter pass_manager in transpile is being deprecated. " + "The preferred way to tranpile a circuit using a custom pass manager is" + " pass_manager.run(circuit)", + DeprecationWarning, + stacklevel=2, + ) return pass_manager.run(circuits, output_name=output_name, callback=callback) if optimization_level is None: # Take optimization level from the configuration or 1 as default. config = user_config.get_config() - optimization_level = config.get('transpile_optimization_level', 1) + optimization_level = config.get("transpile_optimization_level", 1) if scheduling_method is not None and backend is None and not instruction_durations: - warnings.warn("When scheduling circuits without backend," - " 'instruction_durations' should be usually provided.", - UserWarning) + warnings.warn( + "When scheduling circuits without backend," + " 'instruction_durations' should be usually provided.", + UserWarning, + ) # Get transpile_args to configure the circuit transpilation job(s) - transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map, - backend_properties, initial_layout, - layout_method, routing_method, translation_method, - scheduling_method, instruction_durations, dt, - approximation_degree, seed_transpiler, - optimization_level, callback, output_name) + transpile_args = _parse_transpile_args( + circuits, + backend, + basis_gates, + coupling_map, + backend_properties, + initial_layout, + layout_method, + routing_method, + translation_method, + scheduling_method, + instruction_durations, + dt, + approximation_degree, + seed_transpiler, + optimization_level, + callback, + output_name, + ) _check_circuits_coupling_map(circuits, transpile_args, backend) @@ -256,14 +282,17 @@ def callback_func(**kwargs): def _check_conflicting_argument(**kargs): conflicting_args = [arg for arg, value in kargs.items() if value] if conflicting_args: - raise TranspilerError("The parameters pass_manager conflicts with the following " - "parameter(s): {}.".format(', '.join(conflicting_args))) + raise TranspilerError( + "The parameters pass_manager conflicts with the following " + "parameter(s): {}.".format(", ".join(conflicting_args)) + ) def _check_circuits_coupling_map(circuits, transpile_args, backend): # Check circuit width against number of qubits in coupling_map(s) - coupling_maps_list = list(config['pass_manager_config'].coupling_map for config in - transpile_args) + coupling_maps_list = list( + config["pass_manager_config"].coupling_map for config in transpile_args + ) for circuit, parsed_coupling_map in zip(circuits, coupling_maps_list): # If coupling_map is not None or num_qubits == 1 num_qubits = len(circuit.qubits) @@ -276,10 +305,12 @@ def _check_circuits_coupling_map(circuits, transpile_args, backend): max_qubits = backend.configuration().n_qubits if max_qubits is not None and (num_qubits > max_qubits): - raise TranspilerError('Number of qubits ({}) '.format(num_qubits) + - 'in {} '.format(circuit.name) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'in the coupling_map') + raise TranspilerError( + "Number of qubits ({}) ".format(num_qubits) + + "in {} ".format(circuit.name) + + "is greater than maximum ({}) ".format(max_qubits) + + "in the coupling_map" + ) def _log_transpile_time(start_time, end_time): @@ -305,14 +336,15 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua """ circuit, transpile_config = circuit_config_tuple - pass_manager_config = transpile_config['pass_manager_config'] + pass_manager_config = transpile_config["pass_manager_config"] - if transpile_config['faulty_qubits_map']: + if transpile_config["faulty_qubits_map"]: pass_manager_config.initial_layout = _remap_layout_faulty_backend( - pass_manager_config.initial_layout, transpile_config['faulty_qubits_map']) + pass_manager_config.initial_layout, transpile_config["faulty_qubits_map"] + ) # we choose an appropriate one based on desired optimization level - level = transpile_config['optimization_level'] + level = transpile_config["optimization_level"] if level == 0: pass_manager = level_0_pass_manager(pass_manager_config) @@ -325,28 +357,33 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua else: raise TranspilerError("optimization_level can range from 0 to 3.") - result = pass_manager.run(circuit, callback=transpile_config['callback'], - output_name=transpile_config['output_name']) + result = pass_manager.run( + circuit, callback=transpile_config["callback"], output_name=transpile_config["output_name"] + ) - if transpile_config['faulty_qubits_map']: - return _remap_circuit_faulty_backend(result, transpile_config['backend_num_qubits'], - pass_manager_config.backend_properties, - transpile_config['faulty_qubits_map']) + if transpile_config["faulty_qubits_map"]: + return _remap_circuit_faulty_backend( + result, + transpile_config["backend_num_qubits"], + pass_manager_config.backend_properties, + transpile_config["faulty_qubits_map"], + ) return result def _remap_circuit_faulty_backend(circuit, num_qubits, backend_prop, faulty_qubits_map): faulty_qubits = backend_prop.faulty_qubits() if backend_prop else [] - disconnected_qubits = {k for k, v in faulty_qubits_map.items() - if v is None}.difference(faulty_qubits) + disconnected_qubits = {k for k, v in faulty_qubits_map.items() if v is None}.difference( + faulty_qubits + ) faulty_qubits_map_reverse = {v: k for k, v in faulty_qubits_map.items()} if faulty_qubits: - faulty_qreg = circuit._create_qreg(len(faulty_qubits), 'faulty') + faulty_qreg = circuit._create_qreg(len(faulty_qubits), "faulty") else: faulty_qreg = [] if disconnected_qubits: - disconnected_qreg = circuit._create_qreg(len(disconnected_qubits), 'disconnected') + disconnected_qreg = circuit._create_qreg(len(disconnected_qubits), "disconnected") else: disconnected_qreg = [] @@ -371,7 +408,7 @@ def _remap_circuit_faulty_backend(circuit, num_qubits, backend_prop, faulty_qubi physical_layout_dict[qubit] = new_layout[qubit] dag_circuit = circuit_to_dag(circuit) apply_layout_pass = ApplyLayout() - apply_layout_pass.property_set['layout'] = Layout(physical_layout_dict) + apply_layout_pass.property_set["layout"] = Layout(physical_layout_dict) circuit = dag_to_circuit(apply_layout_pass.run(dag_circuit)) circuit._layout = new_layout return circuit @@ -383,18 +420,32 @@ def _remap_layout_faulty_backend(layout, faulty_qubits_map): new_layout = Layout() for virtual, physical in layout.get_virtual_bits().items(): if faulty_qubits_map[physical] is None: - raise TranspilerError("The initial_layout parameter refers to faulty" - " or disconnected qubits") + raise TranspilerError( + "The initial_layout parameter refers to faulty" " or disconnected qubits" + ) new_layout[virtual] = faulty_qubits_map[physical] return new_layout -def _parse_transpile_args(circuits, backend, - basis_gates, coupling_map, backend_properties, - initial_layout, layout_method, routing_method, translation_method, - scheduling_method, instruction_durations, dt, - approximation_degree, seed_transpiler, optimization_level, - callback, output_name) -> List[Dict]: +def _parse_transpile_args( + circuits, + backend, + basis_gates, + coupling_map, + backend_properties, + initial_layout, + layout_method, + routing_method, + translation_method, + scheduling_method, + instruction_durations, + dt, + approximation_degree, + seed_transpiler, + optimization_level, + callback, + output_name, +) -> List[Dict]: """Resolve the various types of args allowed to the transpile() function through duck typing, overriding args, etc. Refer to the transpile() docstring for details on what types of inputs are allowed. @@ -410,8 +461,7 @@ def _parse_transpile_args(circuits, backend, TranspilerError: If instruction_durations are required but not supplied or found. """ if initial_layout is not None and layout_method is not None: - warnings.warn("initial_layout provided; layout_method is ignored.", - UserWarning) + warnings.warn("initial_layout provided; layout_method is ignored.", UserWarning) # Each arg could be single or a list. If list, it must be the same size as # number of circuits. If single, duplicate to create a list of that size. num_circuits = len(circuits) @@ -433,30 +483,50 @@ def _parse_transpile_args(circuits, backend, durations = _parse_instruction_durations(backend, instruction_durations, dt, circuits) scheduling_method = _parse_scheduling_method(scheduling_method, num_circuits) if scheduling_method and any(d is None for d in durations): - raise TranspilerError("Transpiling a circuit with a scheduling method" - "requires a backend or instruction_durations.") + raise TranspilerError( + "Transpiling a circuit with a scheduling method" + "requires a backend or instruction_durations." + ) list_transpile_args = [] - for args in zip(basis_gates, coupling_map, backend_properties, initial_layout, - layout_method, routing_method, translation_method, scheduling_method, - durations, approximation_degree, seed_transpiler, optimization_level, - output_name, callback, backend_num_qubits, faulty_qubits_map): - transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0], - coupling_map=args[1], - backend_properties=args[2], - initial_layout=args[3], - layout_method=args[4], - routing_method=args[5], - translation_method=args[6], - scheduling_method=args[7], - instruction_durations=args[8], - approximation_degree=args[9], - seed_transpiler=args[10]), - 'optimization_level': args[11], - 'output_name': args[12], - 'callback': args[13], - 'backend_num_qubits': args[14], - 'faulty_qubits_map': args[15]} + for args in zip( + basis_gates, + coupling_map, + backend_properties, + initial_layout, + layout_method, + routing_method, + translation_method, + scheduling_method, + durations, + approximation_degree, + seed_transpiler, + optimization_level, + output_name, + callback, + backend_num_qubits, + faulty_qubits_map, + ): + transpile_args = { + "pass_manager_config": PassManagerConfig( + basis_gates=args[0], + coupling_map=args[1], + backend_properties=args[2], + initial_layout=args[3], + layout_method=args[4], + routing_method=args[5], + translation_method=args[6], + scheduling_method=args[7], + instruction_durations=args[8], + approximation_degree=args[9], + seed_transpiler=args[10], + ), + "optimization_level": args[11], + "output_name": args[12], + "callback": args[13], + "backend_num_qubits": args[14], + "faulty_qubits_map": args[15], + } list_transpile_args.append(transpile_args) return list_transpile_args @@ -464,7 +534,7 @@ def _parse_transpile_args(circuits, backend, def _create_faulty_qubits_map(backend): """If the backend has faulty qubits, those should be excluded. A faulty_qubit_map is a map - from working qubit in the backend to dummy qubits that are consecutive and connected.""" + from working qubit in the backend to dummy qubits that are consecutive and connected.""" faulty_qubits_map = None if backend is not None: if backend.properties(): @@ -478,9 +548,11 @@ def _create_faulty_qubits_map(backend): faulty_qubits_map = {} configuration = backend.configuration() full_coupling_map = configuration.coupling_map - functional_cm_list = [edge for edge in full_coupling_map - if (set(edge).isdisjoint(faulty_qubits) and - edge not in faulty_edges)] + functional_cm_list = [ + edge + for edge in full_coupling_map + if (set(edge).isdisjoint(faulty_qubits) and edge not in faulty_edges) + ] connected_working_qubits = CouplingMap(functional_cm_list).largest_connected_component() dummy_qubit_counter = 0 @@ -496,11 +568,12 @@ def _create_faulty_qubits_map(backend): def _parse_basis_gates(basis_gates, backend, circuits): # try getting basis_gates from user, else backend if basis_gates is None: - if getattr(backend, 'configuration', None): - basis_gates = getattr(backend.configuration(), 'basis_gates', None) + if getattr(backend, "configuration", None): + basis_gates = getattr(backend.configuration(), "basis_gates", None) # basis_gates could be None, or a list of basis, e.g. ['u3', 'cx'] - if basis_gates is None or (isinstance(basis_gates, list) and - all(isinstance(i, str) for i in basis_gates)): + if basis_gates is None or ( + isinstance(basis_gates, list) and all(isinstance(i, str) for i in basis_gates) + ): basis_gates = [basis_gates] * len(circuits) return basis_gates @@ -509,9 +582,9 @@ def _parse_basis_gates(basis_gates, backend, circuits): def _parse_coupling_map(coupling_map, backend, num_circuits): # try getting coupling_map from user, else backend if coupling_map is None: - if getattr(backend, 'configuration', None): + if getattr(backend, "configuration", None): configuration = backend.configuration() - if hasattr(configuration, 'coupling_map') and configuration.coupling_map: + if hasattr(configuration, "coupling_map") and configuration.coupling_map: faulty_map = _create_faulty_qubits_map(backend) if faulty_map: coupling_map = CouplingMap() @@ -524,8 +597,9 @@ def _parse_coupling_map(coupling_map, backend, num_circuits): # coupling_map could be None, or a list of lists, e.g. [[0, 1], [2, 1]] if coupling_map is None or isinstance(coupling_map, CouplingMap): coupling_map = [coupling_map] * num_circuits - elif isinstance(coupling_map, list) and all(isinstance(i, list) and len(i) == 2 - for i in coupling_map): + elif isinstance(coupling_map, list) and all( + isinstance(i, list) and len(i) == 2 for i in coupling_map + ): coupling_map = [coupling_map] * num_circuits coupling_map = [CouplingMap(cm) if isinstance(cm, list) else cm for cm in coupling_map] @@ -536,10 +610,11 @@ def _parse_coupling_map(coupling_map, backend, num_circuits): def _parse_backend_properties(backend_properties, backend, num_circuits): # try getting backend_properties from user, else backend if backend_properties is None: - if getattr(backend, 'properties', None): + if getattr(backend, "properties", None): backend_properties = backend.properties() - if backend_properties and \ - (backend_properties.faulty_qubits() or backend_properties.faulty_gates()): + if backend_properties and ( + backend_properties.faulty_qubits() or backend_properties.faulty_gates() + ): faulty_qubits = sorted(backend_properties.faulty_qubits(), reverse=True) faulty_edges = [gates.qubits for gates in backend_properties.faulty_gates()] # remove faulty qubits in backend_properties.qubits @@ -551,14 +626,16 @@ def _parse_backend_properties(backend_properties, backend, num_circuits): # remove gates using faulty edges or with faulty qubits (and remap the # gates in terms of faulty_qubits_map) faulty_qubits_map = _create_faulty_qubits_map(backend) - if any(faulty_qubits_map[qubits] is not None for qubits in gate.qubits) or \ - gate.qubits in faulty_edges: + if ( + any(faulty_qubits_map[qubits] is not None for qubits in gate.qubits) + or gate.qubits in faulty_edges + ): continue gate_dict = gate.to_dict() replacement_gate = Gate.from_dict(gate_dict) - gate_dict['qubits'] = [faulty_qubits_map[qubit] for qubit in gate.qubits] - args = '_'.join([str(qubit) for qubit in gate_dict['qubits']]) - gate_dict['name'] = "%s%s" % (gate_dict['gate'], args) + gate_dict["qubits"] = [faulty_qubits_map[qubit] for qubit in gate.qubits] + args = "_".join([str(qubit) for qubit in gate_dict["qubits"]]) + gate_dict["name"] = "%s%s" % (gate_dict["gate"], args) gates.append(replacement_gate) backend_properties.gates = gates @@ -596,10 +673,13 @@ def _layout_from_raw(initial_layout, circuit): return initial_layout # multiple layouts? - if isinstance(initial_layout, list) and \ - any(isinstance(i, (list, dict)) for i in initial_layout): - initial_layout = [_layout_from_raw(lo, circ) if isinstance(lo, (list, dict)) else lo - for lo, circ in zip(initial_layout, circuits)] + if isinstance(initial_layout, list) and any( + isinstance(i, (list, dict)) for i in initial_layout + ): + initial_layout = [ + _layout_from_raw(lo, circ) if isinstance(lo, (list, dict)) else lo + for lo, circ in zip(initial_layout, circuits) + ] else: # even if one layout, but multiple circuits, the layout needs to be adapted for each initial_layout = [_layout_from_raw(initial_layout, circ) for circ in circuits] @@ -661,7 +741,7 @@ def _parse_instruction_durations(backend, inst_durations, dt, circuits): circ_durations.update(cal_durations, circ_durations.dt) if inst_durations: - circ_durations.update(inst_durations, dt or getattr(inst_durations, 'dt', None)) + circ_durations.update(inst_durations, dt or getattr(inst_durations, "dt", None)) durations.append(circ_durations) return durations @@ -720,20 +800,27 @@ def _parse_output_name(output_name, circuits): return [output_name] # multiple circuits else: - raise TranspilerError("Expected a list object of length equal " + - "to that of the number of circuits " + - "being transpiled") + raise TranspilerError( + "Expected a list object of length equal " + + "to that of the number of circuits " + + "being transpiled" + ) elif isinstance(output_name, list): - if len(circuits) == len(output_name) and \ - all(isinstance(name, str) for name in output_name): + if len(circuits) == len(output_name) and all( + isinstance(name, str) for name in output_name + ): return output_name else: - raise TranspilerError("The length of output_name list " - "must be equal to the number of " - "transpiled circuits and the output_name " - "list should be strings.") + raise TranspilerError( + "The length of output_name list " + "must be equal to the number of " + "transpiled circuits and the output_name " + "list should be strings." + ) else: - raise TranspilerError("The parameter output_name should be a string or a" - "list of strings: %s was used." % type(output_name)) + raise TranspilerError( + "The parameter output_name should be a string or a" + "list of strings: %s was used." % type(output_name) + ) else: return [circuit.name for circuit in circuits] diff --git a/qiskit/converters/__init__.py b/qiskit/converters/__init__.py index b64202e1b1a5..2e7c0a9092f0 100644 --- a/qiskit/converters/__init__.py +++ b/qiskit/converters/__init__.py @@ -43,7 +43,7 @@ def isinstanceint(obj): - """ Like isinstance(obj,int), but with casting. Except for strings.""" + """Like isinstance(obj,int), but with casting. Except for strings.""" if isinstance(obj, str): return False try: @@ -54,7 +54,7 @@ def isinstanceint(obj): def isinstancelist(obj): - """ Like isinstance(obj, list), but with casting. Except for strings and dicts.""" + """Like isinstance(obj, list), but with casting. Except for strings and dicts.""" if isinstance(obj, (str, dict)): return False try: diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index 38805388ff7a..9893e03b6cd2 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -103,42 +103,44 @@ def ast_to_dag(ast): class AstInterpreter: """Interprets an OpenQASM by expanding subroutines and unrolling loops.""" - standard_extension = {"u1": U1Gate, - "u2": U2Gate, - "u3": U3Gate, - "u": UGate, - "p": PhaseGate, - "x": XGate, - "y": YGate, - "z": ZGate, - "t": TGate, - "tdg": TdgGate, - "s": SGate, - "sdg": SdgGate, - "sx": SXGate, - "sxdg": SXdgGate, - "swap": SwapGate, - "rx": RXGate, - "rxx": RXXGate, - "ry": RYGate, - "rz": RZGate, - "rzz": RZZGate, - "id": IGate, - "h": HGate, - "cx": CXGate, - "cy": CYGate, - "cz": CZGate, - "ch": CHGate, - "crx": CRXGate, - "cry": CRYGate, - "crz": CRZGate, - "csx": CSXGate, - "cu1": CU1Gate, - "cp": CPhaseGate, - "cu": CUGate, - "cu3": CU3Gate, - "ccx": CCXGate, - "cswap": CSwapGate} + standard_extension = { + "u1": U1Gate, + "u2": U2Gate, + "u3": U3Gate, + "u": UGate, + "p": PhaseGate, + "x": XGate, + "y": YGate, + "z": ZGate, + "t": TGate, + "tdg": TdgGate, + "s": SGate, + "sdg": SdgGate, + "sx": SXGate, + "sxdg": SXdgGate, + "swap": SwapGate, + "rx": RXGate, + "rxx": RXXGate, + "ry": RYGate, + "rz": RZGate, + "rzz": RZZGate, + "id": IGate, + "h": HGate, + "cx": CXGate, + "cy": CYGate, + "cz": CZGate, + "ch": CHGate, + "crx": CRXGate, + "cry": CRYGate, + "crz": CRZGate, + "csx": CSXGate, + "cu1": CU1Gate, + "cp": CPhaseGate, + "cu": CUGate, + "cu3": CU3Gate, + "ccx": CCXGate, + "cswap": CSwapGate, + } def __init__(self, dag): """Initialize interpreter's data.""" @@ -167,9 +169,9 @@ def _process_bit_id(self, node): elif node.name in self.dag.cregs: reg = self.dag.cregs[node.name] else: - raise QiskitError("expected qreg or creg name:", - "line=%s" % node.line, - "file=%s" % node.file) + raise QiskitError( + "expected qreg or creg name:", "line=%s" % node.line, "file=%s" % node.file + ) if node.type == "indexed_id": # An indexed bit or qubit @@ -183,9 +185,9 @@ def _process_bit_id(self, node): # local scope if node.name in self.bit_stack[-1]: return [self.bit_stack[-1][node.name]] - raise QiskitError("expected local bit name:", - "line=%s" % node.line, - "file=%s" % node.file) + raise QiskitError( + "expected local bit name:", "line=%s" % node.line, "file=%s" % node.file + ) return None def _process_custom_unitary(self, node): @@ -195,21 +197,21 @@ def _process_custom_unitary(self, node): args = self._process_node(node.arguments) else: args = [] - bits = [self._process_bit_id(node_element) - for node_element in node.bitlist.children] + bits = [self._process_bit_id(node_element) for node_element in node.bitlist.children] if name in self.gates: self._arguments(name, bits, args) else: - raise QiskitError("internal error undefined gate:", - "line=%s" % node.line, "file=%s" % node.file) + raise QiskitError( + "internal error undefined gate:", "line=%s" % node.line, "file=%s" % node.file + ) def _process_u(self, node): """Process a U gate node.""" args = self._process_node(node.arguments) bits = [self._process_bit_id(node.bitlist)] - self._arguments('u', bits, args) + self._arguments("u", bits, args) def _arguments(self, name, bits, args): gargs = self.gates[name]["args"] @@ -217,16 +219,15 @@ def _arguments(self, name, bits, args): maxidx = max(map(len, bits)) for idx in range(maxidx): - self.arg_stack.append({gargs[j]: args[j] - for j in range(len(gargs))}) + self.arg_stack.append({gargs[j]: args[j] for j in range(len(gargs))}) # Only index into register arguments. - element = [idx * x for x in - [len(bits[j]) > 1 for j in range(len(bits))]] - self.bit_stack.append({gbits[j]: bits[j][element[j]] - for j in range(len(gbits))}) - self._create_dag_op(name, - [self.arg_stack[-1][s].sym() for s in gargs], - [self.bit_stack[-1][s] for s in gbits]) + element = [idx * x for x in [len(bits[j]) > 1 for j in range(len(bits))]] + self.bit_stack.append({gbits[j]: bits[j][element[j]] for j in range(len(gbits))}) + self._create_dag_op( + name, + [self.arg_stack[-1][s].sym() for s in gargs], + [self.bit_stack[-1][s] for s in gbits], + ) self.arg_stack.pop() self.bit_stack.pop() @@ -258,8 +259,9 @@ def _process_cnot(self, node): id0 = self._process_bit_id(node.children[0]) id1 = self._process_bit_id(node.children[1]) if not (len(id0) == len(id1) or len(id0) == 1 or len(id1) == 1): - raise QiskitError("internal error: qreg size mismatch", - "line=%s" % node.line, "file=%s" % node.file) + raise QiskitError( + "internal error: qreg size mismatch", "line=%s" % node.line, "file=%s" % node.file + ) maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): cx_gate = CXGate() @@ -276,8 +278,9 @@ def _process_measure(self, node): id0 = self._process_bit_id(node.children[0]) id1 = self._process_bit_id(node.children[1]) if len(id0) != len(id1): - raise QiskitError("internal error: reg size mismatch", - "line=%s" % node.line, "file=%s" % node.file) + raise QiskitError( + "internal error: reg size mismatch", "line=%s" % node.line, "file=%s" % node.file + ) for idx, idy in zip(id0, id1): meas_gate = Measure() meas_gate.condition = self.condition @@ -324,8 +327,7 @@ def _process_node(self, node): elif node.type == "id_list": # We process id_list nodes when they are leaves of barriers. - return [self._process_bit_id(node_children) - for node_children in node.children] + return [self._process_bit_id(node_children) for node_children in node.children] elif node.type == "primary_list": # We should only be called for a barrier. @@ -383,25 +385,28 @@ def _process_node(self, node): raise QiskitError("internal error: _process_node on external") else: - raise QiskitError("internal error: undefined node type", - node.type, "line=%s" % node.line, - "file=%s" % node.file) + raise QiskitError( + "internal error: undefined node type", + node.type, + "line=%s" % node.line, + "file=%s" % node.file, + ) return None def _gate_rules_to_qiskit_circuit(self, node, params): """From a gate definition in qasm, to a QuantumCircuit format.""" rules = [] - qreg = QuantumRegister(node['n_bits']) - bit_args = {node['bits'][i]: q for i, q in enumerate(qreg)} - exp_args = {node['args'][i]: Real(q) for i, q in enumerate(params)} + qreg = QuantumRegister(node["n_bits"]) + bit_args = {node["bits"][i]: q for i, q in enumerate(qreg)} + exp_args = {node["args"][i]: Real(q) for i, q in enumerate(params)} - for child_op in node['body'].children: + for child_op in node["body"].children: qparams = [] eparams = [] for param_list in child_op.children[1:]: - if param_list.type == 'id_list': + if param_list.type == "id_list": qparams = [bit_args[param.name] for param in param_list.children] - elif param_list.type == 'expression_list': + elif param_list.type == "expression_list": for param in param_list.children: eparams.append(param.sym(nested_scope=[exp_args])) op = self._create_op(child_op.name, params=eparams) @@ -431,11 +436,10 @@ def _create_op(self, name, params): if name in self.standard_extension: op = self.standard_extension[name](*params) elif name in self.gates: - op = Gate(name=name, num_qubits=self.gates[name]['n_bits'], params=params) - if not self.gates[name]['opaque']: + op = Gate(name=name, num_qubits=self.gates[name]["n_bits"], params=params) + if not self.gates[name]["opaque"]: # call a custom gate (otherwise, opaque) - op.definition = self._gate_rules_to_qiskit_circuit(self.gates[name], - params=params) + op.definition = self._gate_rules_to_qiskit_circuit(self.gates[name], params=params) else: raise QiskitError("unknown operation for ast node name %s" % name) return op diff --git a/qiskit/converters/circuit_to_dagdependency.py b/qiskit/converters/circuit_to_dagdependency.py index 634870ef7c83..220fa0782cf4 100644 --- a/qiskit/converters/circuit_to_dagdependency.py +++ b/qiskit/converters/circuit_to_dagdependency.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -'''Helper function for converting a circuit to a dag dependency''' +"""Helper function for converting a circuit to a dag dependency""" from qiskit.dagcircuit.dagdependency import DAGDependency diff --git a/qiskit/converters/circuit_to_gate.py b/qiskit/converters/circuit_to_gate.py index 21a6235b07b4..7122edfe3231 100644 --- a/qiskit/converters/circuit_to_gate.py +++ b/qiskit/converters/circuit_to_gate.py @@ -45,15 +45,18 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label """ # pylint: disable=cyclic-import from qiskit.circuit.quantumcircuit import QuantumCircuit + if circuit.clbits: - raise QiskitError('Circuit with classical bits cannot be converted ' - 'to gate.') + raise QiskitError("Circuit with classical bits cannot be converted " "to gate.") for inst, _, _ in circuit.data: if not isinstance(inst, Gate): - raise QiskitError(('One or more instructions cannot be converted to' - ' a gate. "{}" is not a gate instruction').format( - inst.name)) + raise QiskitError( + ( + "One or more instructions cannot be converted to" + ' a gate. "{}" is not a gate instruction' + ).format(inst.name) + ) if parameter_map is None: parameter_dict = {p: p for p in circuit.parameters} @@ -61,14 +64,19 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: - raise QiskitError(('parameter_map should map all circuit parameters. ' - 'Circuit parameters: {}, parameter_map: {}').format( - circuit.parameters, parameter_dict)) - - gate = Gate(name=circuit.name, - num_qubits=sum([qreg.size for qreg in circuit.qregs]), - params=[*parameter_dict.values()], - label=label) + raise QiskitError( + ( + "parameter_map should map all circuit parameters. " + "Circuit parameters: {}, parameter_map: {}" + ).format(circuit.parameters, parameter_dict) + ) + + gate = Gate( + name=circuit.name, + num_qubits=sum([qreg.size for qreg in circuit.qregs]), + params=[*parameter_dict.values()], + label=label, + ) gate.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) @@ -79,15 +87,14 @@ def circuit_to_gate(circuit, parameter_map=None, equivalence_library=None, label rules = target.data if gate.num_qubits > 0: - q = QuantumRegister(gate.num_qubits, 'q') + q = QuantumRegister(gate.num_qubits, "q") qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} # The 3rd parameter in the output tuple) is hard coded to [] because # Gate objects do not have cregs set and we've verified that all # instructions are gates - rules = [(inst, [qubit_map[y] for y in qargs], []) - for inst, qargs, _ in rules] + rules = [(inst, [qubit_map[y] for y in qargs], []) for inst, qargs, _ in rules] qc = QuantumCircuit(q, name=gate.name, global_phase=target.global_phase) for instr, qargs, cargs in rules: qc._append(instr, qargs, cargs) diff --git a/qiskit/converters/circuit_to_instruction.py b/qiskit/converters/circuit_to_instruction.py index 9e840d029d56..136cd418922d 100644 --- a/qiskit/converters/circuit_to_instruction.py +++ b/qiskit/converters/circuit_to_instruction.py @@ -67,14 +67,19 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None parameter_dict = circuit._unroll_param_dict(parameter_map) if parameter_dict.keys() != circuit.parameters: - raise QiskitError(('parameter_map should map all circuit parameters. ' - 'Circuit parameters: {}, parameter_map: {}').format( - circuit.parameters, parameter_dict)) - - instruction = Instruction(name=circuit.name, - num_qubits=sum([qreg.size for qreg in circuit.qregs]), - num_clbits=sum([creg.size for creg in circuit.cregs]), - params=[*parameter_dict.values()]) + raise QiskitError( + ( + "parameter_map should map all circuit parameters. " + "Circuit parameters: {}, parameter_map: {}" + ).format(circuit.parameters, parameter_dict) + ) + + instruction = Instruction( + name=circuit.name, + num_qubits=sum([qreg.size for qreg in circuit.qregs]), + num_clbits=sum([creg.size for creg in circuit.cregs]), + params=[*parameter_dict.values()], + ) instruction.condition = None target = circuit.assign_parameters(parameter_dict, inplace=False) @@ -86,20 +91,20 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None regs = [] if instruction.num_qubits > 0: - q = QuantumRegister(instruction.num_qubits, 'q') + q = QuantumRegister(instruction.num_qubits, "q") regs.append(q) if instruction.num_clbits > 0: - c = ClassicalRegister(instruction.num_clbits, 'c') + c = ClassicalRegister(instruction.num_clbits, "c") regs.append(c) qubit_map = {bit: q[idx] for idx, bit in enumerate(circuit.qubits)} clbit_map = {bit: c[idx] for idx, bit in enumerate(circuit.clbits)} - definition = [(inst, - [qubit_map[y] for y in qargs], - [clbit_map[y] for y in cargs]) - for inst, qargs, cargs in definition] + definition = [ + (inst, [qubit_map[y] for y in qargs], [clbit_map[y] for y in cargs]) + for inst, qargs, cargs in definition + ] # fix condition for rule in definition: @@ -109,8 +114,10 @@ def circuit_to_instruction(circuit, parameter_map=None, equivalence_library=None if reg.size == c.size: rule[0].condition = (c, val) else: - raise QiskitError('Cannot convert condition in circuit with ' - 'multiple classical registers to instruction') + raise QiskitError( + "Cannot convert condition in circuit with " + "multiple classical registers to instruction" + ) qc = QuantumCircuit(*regs, name=instruction.name) for instr, qargs, cargs in definition: diff --git a/qiskit/converters/dag_to_circuit.py b/qiskit/converters/dag_to_circuit.py index 2bd947af1507..ffd232dd899b 100644 --- a/qiskit/converters/dag_to_circuit.py +++ b/qiskit/converters/dag_to_circuit.py @@ -47,10 +47,14 @@ def dag_to_circuit(dag): """ name = dag.name or None - circuit = QuantumCircuit(dag.qubits, dag.clbits, - *dag.qregs.values(), *dag.cregs.values(), - name=name, - global_phase=dag.global_phase) + circuit = QuantumCircuit( + dag.qubits, + dag.clbits, + *dag.qregs.values(), + *dag.cregs.values(), + name=name, + global_phase=dag.global_phase, + ) circuit.metadata = dag.metadata circuit.calibrations = dag.calibrations diff --git a/qiskit/converters/dagdependency_to_circuit.py b/qiskit/converters/dagdependency_to_circuit.py index 33096c1f7ff4..95e2b958695a 100644 --- a/qiskit/converters/dagdependency_to_circuit.py +++ b/qiskit/converters/dagdependency_to_circuit.py @@ -25,9 +25,13 @@ def dagdependency_to_circuit(dagdependency): """ name = dagdependency.name or None - circuit = QuantumCircuit(dagdependency.qubits, dagdependency.clbits, - *dagdependency.qregs.values(), *dagdependency.cregs.values(), - name=name) + circuit = QuantumCircuit( + dagdependency.qubits, + dagdependency.clbits, + *dagdependency.qregs.values(), + *dagdependency.cregs.values(), + name=name, + ) circuit.metadata = dagdependency.metadata circuit.calibrations = dagdependency.calibrations diff --git a/qiskit/dagcircuit/dagcircuit.py b/qiskit/dagcircuit/dagcircuit.py index 37d6654e3a83..aedba187499f 100644 --- a/qiskit/dagcircuit/dagcircuit.py +++ b/qiskit/dagcircuit/dagcircuit.py @@ -88,23 +88,23 @@ def __init__(self): self._calibrations = defaultdict(dict) self.duration = None - self.unit = 'dt' + self.unit = "dt" def to_networkx(self): """Returns a copy of the DAGCircuit in networkx format.""" try: import networkx as nx except ImportError as ex: - raise ImportError("Networkx is needed to use to_networkx(). It " - "can be installed with 'pip install networkx'") from ex + raise ImportError( + "Networkx is needed to use to_networkx(). It " + "can be installed with 'pip install networkx'" + ) from ex G = nx.MultiDiGraph() for node in self._multi_graph.nodes(): G.add_node(node) for node_id in rx.topological_sort(self._multi_graph): for source_id, dest_id, edge in self._multi_graph.in_edges(node_id): - G.add_edge(self._multi_graph[source_id], - self._multi_graph[dest_id], - wire=edge) + G.add_edge(self._multi_graph[source_id], self._multi_graph[dest_id], wire=edge) return G @classmethod @@ -126,21 +126,22 @@ def from_networkx(cls, graph): try: import networkx as nx except ImportError as ex: - raise ImportError("Networkx is needed to use from_networkx(). It " - "can be installed with 'pip install networkx'") from ex + raise ImportError( + "Networkx is needed to use from_networkx(). It " + "can be installed with 'pip install networkx'" + ) from ex dag = DAGCircuit() for node in nx.topological_sort(graph): - if node.type == 'out': + if node.type == "out": continue - if node.type == 'in': + if node.type == "in": if isinstance(node.wire, Qubit): dag.add_qubits([node.wire]) elif isinstance(node.wire, Clbit): dag.add_clbits([node.wire]) else: - raise DAGCircuitError('unknown node wire type: {}'.format( - node.wire)) - elif node.type == 'op': + raise DAGCircuitError("unknown node wire type: {}".format(node.wire)) + elif node.type == "op": dag.apply_operation_back(node.op.copy(), node.qargs, node.cargs) return dag @@ -301,17 +302,14 @@ def _add_wire(self, wire): if wire not in self._wires: self._wires.add(wire) - inp_node = DAGNode(type='in', wire=wire) - outp_node = DAGNode(type='out', wire=wire) - input_map_id, output_map_id = self._multi_graph.add_nodes_from( - [inp_node, outp_node]) + inp_node = DAGNode(type="in", wire=wire) + outp_node = DAGNode(type="out", wire=wire) + input_map_id, output_map_id = self._multi_graph.add_nodes_from([inp_node, outp_node]) inp_node._node_id = input_map_id outp_node._node_id = output_map_id self.input_map[wire] = inp_node self.output_map[wire] = outp_node - self._multi_graph.add_edge(inp_node._node_id, - outp_node._node_id, - wire) + self._multi_graph.add_edge(inp_node._node_id, outp_node._node_id, wire) else: raise DAGCircuitError("duplicate wire %s" % (wire,)) @@ -344,8 +342,7 @@ def _check_bits(self, args, amap): # Check for each wire for wire in args: if wire not in amap: - raise DAGCircuitError("(qu)bit %s not found in %s" % - (wire, amap)) + raise DAGCircuitError("(qu)bit %s not found in %s" % (wire, amap)) def _bits_in_condition(self, cond): """Return a list of bits in the given condition. @@ -407,8 +404,10 @@ def apply_operation_back(self, op, qargs=None, cargs=None, condition=None): """ if condition: - warnings.warn("Use of condition arg is deprecated, set condition in instruction", - DeprecationWarning) + warnings.warn( + "Use of condition arg is deprecated, set condition in instruction", + DeprecationWarning, + ) op.condition = condition if op.condition is None else op.condition qargs = qargs or [] @@ -429,8 +428,8 @@ def apply_operation_back(self, op, qargs=None, cargs=None, condition=None): al = [qargs, all_cbits] self._multi_graph.insert_node_on_in_edges_multiple( - node_index, - [self.output_map[q]._node_id for q in itertools.chain(*al)]) + node_index, [self.output_map[q]._node_id for q in itertools.chain(*al)] + ) return self._multi_graph[node_index] def apply_operation_front(self, op, qargs, cargs, condition=None): @@ -448,8 +447,10 @@ def apply_operation_front(self, op, qargs, cargs, condition=None): DAGCircuitError: if initial nodes connected to multiple out edges """ if condition: - warnings.warn("Use of condition arg is deprecated, set condition in instruction", - DeprecationWarning) + warnings.warn( + "Use of condition arg is deprecated, set condition in instruction", + DeprecationWarning, + ) op.condition = condition if op.condition is None else op.condition all_cbits = self._bits_in_condition(op.condition) @@ -465,8 +466,8 @@ def apply_operation_front(self, op, qargs, cargs, condition=None): # and adding new edges to the operation node from each input node al = [qargs, all_cbits] self._multi_graph.insert_node_on_out_edges_multiple( - node_index, - [self.input_map[q]._node_id for q in itertools.chain(*al)]) + node_index, [self.input_map[q]._node_id for q in itertools.chain(*al)] + ) return self._multi_graph[node_index] def _check_edgemap_registers(self, inbound_wires, inbound_regs): @@ -504,10 +505,7 @@ def _check_edgemap_registers(self, inbound_wires, inbound_regs): if len(s) == 2: raise DAGCircuitError("inbound_wires fragments reg %s" % inbound_reg) if s == {False}: - if ( - inbound_reg.name in self.qregs - or inbound_reg.name in self.cregs - ): + if inbound_reg.name in self.qregs or inbound_reg.name in self.cregs: raise DAGCircuitError("unmapped duplicate reg %s" % inbound_reg) # Add registers that appear only in inbound_regs @@ -570,33 +568,37 @@ def _map_condition(wire_map, condition, target_cregs): for bit in wire_map: if bit in cond_creg: try: - candidate_creg = next(creg - for creg in target_cregs - if wire_map[bit] in creg) + candidate_creg = next( + creg for creg in target_cregs if wire_map[bit] in creg + ) except StopIteration as ex: - raise DAGCircuitError('Did not find creg containing ' - 'mapped clbit in conditional.') from ex + raise DAGCircuitError( + "Did not find creg containing " "mapped clbit in conditional." + ) from ex if new_creg is None: new_creg = candidate_creg elif new_creg != candidate_creg: # Raise if wire_map maps condition creg on to more than one # creg in target DAG. - raise DAGCircuitError('wire_map maps conditional ' - 'register onto more than one creg.') + raise DAGCircuitError( + "wire_map maps conditional " "register onto more than one creg." + ) - if 2**(cond_creg[:].index(bit)) & cond_val: - new_cond_val += 2**(new_creg[:].index(wire_map[bit])) + if 2 ** (cond_creg[:].index(bit)) & cond_val: + new_cond_val += 2 ** (new_creg[:].index(wire_map[bit])) if new_creg is None: raise DAGCircuitError("Condition registers not found in wire_map.") new_condition = (new_creg, new_cond_val) return new_condition def extend_back(self, dag, edge_map=None): - """DEPRECATED: Add `dag` at the end of `self`, using `edge_map`. - """ - warnings.warn("dag.extend_back is deprecated, please use dag.compose.", - DeprecationWarning, stacklevel=2) + """DEPRECATED: Add `dag` at the end of `self`, using `edge_map`.""" + warnings.warn( + "dag.extend_back is deprecated, please use dag.compose.", + DeprecationWarning, + stacklevel=2, + ) edge_map = edge_map or {} for qreg in dag.qregs.values(): if qreg.name not in self.qregs: @@ -611,10 +613,12 @@ def extend_back(self, dag, edge_map=None): self.compose_back(dag, edge_map) def compose_back(self, input_circuit, edge_map=None): - """DEPRECATED: use DAGCircuit.compose() instead. - """ - warnings.warn("dag.compose_back is deprecated, please use dag.compose.", - DeprecationWarning, stacklevel=2) + """DEPRECATED: use DAGCircuit.compose() instead.""" + warnings.warn( + "dag.compose_back is deprecated, please use dag.compose.", + DeprecationWarning, + stacklevel=2, + ) self.compose(input_circuit, edge_map) def compose(self, other, edge_map=None, qubits=None, clbits=None, front=False, inplace=True): @@ -644,16 +648,19 @@ def compose(self, other, edge_map=None, qubits=None, clbits=None, front=False, i if front: raise DAGCircuitError("Front composition not supported yet.") - if len(other.qubits) > len(self.qubits) or \ - len(other.clbits) > len(self.clbits): - raise DAGCircuitError("Trying to compose with another DAGCircuit " - "which has more 'in' edges.") + if len(other.qubits) > len(self.qubits) or len(other.clbits) > len(self.clbits): + raise DAGCircuitError( + "Trying to compose with another DAGCircuit " "which has more 'in' edges." + ) if edge_map is not None: - warnings.warn("edge_map arg as a dictionary is deprecated. " - "Use qubits and clbits args to specify a list of " - "self edges to compose onto.", DeprecationWarning, - stacklevel=2) + warnings.warn( + "edge_map arg as a dictionary is deprecated. " + "Use qubits and clbits args to specify a list of " + "self edges to compose onto.", + DeprecationWarning, + stacklevel=2, + ) # number of qubits and clbits must match number in circuit or None identity_qubit_map = dict(zip(other.qubits, self.qubits)) @@ -661,19 +668,27 @@ def compose(self, other, edge_map=None, qubits=None, clbits=None, front=False, i if qubits is None: qubit_map = identity_qubit_map elif len(qubits) != len(other.qubits): - raise DAGCircuitError("Number of items in qubits parameter does not" - " match number of qubits in the circuit.") + raise DAGCircuitError( + "Number of items in qubits parameter does not" + " match number of qubits in the circuit." + ) else: - qubit_map = {other.qubits[i]: (self.qubits[q] if isinstance(q, int) else q) - for i, q in enumerate(qubits)} + qubit_map = { + other.qubits[i]: (self.qubits[q] if isinstance(q, int) else q) + for i, q in enumerate(qubits) + } if clbits is None: clbit_map = identity_clbit_map elif len(clbits) != len(other.clbits): - raise DAGCircuitError("Number of items in clbits parameter does not" - " match number of clbits in the circuit.") + raise DAGCircuitError( + "Number of items in clbits parameter does not" + " match number of clbits in the circuit." + ) else: - clbit_map = {other.clbits[i]: (self.clbits[c] if isinstance(c, int) else c) - for i, c in enumerate(clbits)} + clbit_map = { + other.clbits[i]: (self.clbits[c] if isinstance(c, int) else c) + for i, c in enumerate(clbits) + } edge_map = edge_map or {**qubit_map, **clbit_map} or None # if no edge_map, try to do a 1-1 mapping in order @@ -700,11 +715,14 @@ def compose(self, other, edge_map=None, qubits=None, clbits=None, front=False, i m_wire = edge_map.get(nd.wire, nd.wire) # the mapped wire should already exist if m_wire not in dag.output_map: - raise DAGCircuitError("wire %s[%d] not in self" % ( - m_wire.register.name, m_wire.index)) + raise DAGCircuitError( + "wire %s[%d] not in self" % (m_wire.register.name, m_wire.index) + ) if nd.wire not in other._wires: - raise DAGCircuitError("inconsistent wire type for %s[%d] in other" - % (nd.register.name, nd.wire.index)) + raise DAGCircuitError( + "inconsistent wire type for %s[%d] in other" + % (nd.register.name, nd.wire.index) + ) elif nd.type == "out": # ignore output nodes pass @@ -733,6 +751,7 @@ def reverse_ops(self): # TODO: speed up # pylint: disable=cyclic-import from qiskit.converters import dag_to_circuit, circuit_to_dag + qc = dag_to_circuit(self) reversed_qc = qc.reverse_ops() reversed_dag = circuit_to_dag(reversed_qc) @@ -750,8 +769,9 @@ def idle_wires(self, ignore=None): if ignore is None: ignore = [] for wire in self._wires: - nodes = [node for node in self.nodes_on_wire(wire, only_ops=False) - if node.name not in ignore] + nodes = [ + node for node in self.nodes_on_wire(wire, only_ops=False) if node.name not in ignore + ] if len(nodes) == 2: yield wire @@ -774,19 +794,19 @@ def depth(self): def width(self): """Return the total number of qubits + clbits used by the circuit. - This function formerly returned the number of qubits by the calculation - return len(self._wires) - self.num_clbits() - but was changed by issue #2564 to return number of qubits + clbits - with the new function DAGCircuit.num_qubits replacing the former - semantic of DAGCircuit.width(). + This function formerly returned the number of qubits by the calculation + return len(self._wires) - self.num_clbits() + but was changed by issue #2564 to return number of qubits + clbits + with the new function DAGCircuit.num_qubits replacing the former + semantic of DAGCircuit.width(). """ return len(self._wires) def num_qubits(self): """Return the total number of qubits used by the circuit. - num_qubits() replaces former use of width(). - DAGCircuit.width() now returns qubits + clbits for - consistency with Circuit.width() [qiskit-terra #2564]. + num_qubits() replaces former use of width(). + DAGCircuit.width() now returns qubits + clbits for + consistency with Circuit.width() [qiskit-terra #2564]. """ return len(self.qubits) @@ -821,8 +841,7 @@ def _check_wires_list(self, wires, node): wire_tot += node.op.condition[0].size if len(wires) != wire_tot: - raise DAGCircuitError("expected %d wires, got %d" - % (wire_tot, len(wires))) + raise DAGCircuitError("expected %d wires, got %d" % (wire_tot, len(wires))) def _make_pred_succ_maps(self, node): """Return predecessor and successor dictionaries. @@ -836,14 +855,11 @@ def _make_pred_succ_maps(self, node): predecessor (successor) nodes of the input node. """ - pred_map = {e[2]: e[0] for e in - self._multi_graph.in_edges(node._node_id)} - succ_map = {e[2]: e[1] for e in - self._multi_graph.out_edges(node._node_id)} + pred_map = {e[2]: e[0] for e in self._multi_graph.in_edges(node._node_id)} + succ_map = {e[2]: e[1] for e in self._multi_graph.out_edges(node._node_id)} return pred_map, succ_map - def _full_pred_succ_maps(self, pred_map, succ_map, input_circuit, - wire_map): + def _full_pred_succ_maps(self, pred_map, succ_map, input_circuit, wire_map): """Map all wires of the input circuit. Map all wires of the input circuit to predecessor and @@ -873,11 +889,11 @@ def _full_pred_succ_maps(self, pred_map, succ_map, input_circuit, # Otherwise, use the corresponding output nodes of self # and compute the predecessor. full_succ_map[w] = self.output_map[w] - full_pred_map[w] = self._multi_graph.predecessors( - self.output_map[w])[0] + full_pred_map[w] = self._multi_graph.predecessors(self.output_map[w])[0] if len(self._multi_graph.predecessors(self.output_map[w])) != 1: - raise DAGCircuitError("too many predecessors for %s[%d] " - "output node" % (w.register, w.index)) + raise DAGCircuitError( + "too many predecessors for %s[%d] " "output node" % (w.register, w.index) + ) return full_pred_map, full_succ_map @@ -889,7 +905,9 @@ def __eq__(self, other): try: self_phase = float(self.global_phase) other_phase = float(other.global_phase) - if abs((self_phase - other_phase + np.pi) % (2*np.pi) - np.pi) > 1.E-10: # TODO: atol? + if ( + abs((self_phase - other_phase + np.pi) % (2 * np.pi) - np.pi) > 1.0e-10 + ): # TODO: atol? return False except TypeError: if self.global_phase != other.global_phase: @@ -897,35 +915,31 @@ def __eq__(self, other): if self.calibrations != other.calibrations: return False - self_bit_indices = {bit: idx - for idx, bit in - enumerate(self.qubits + self.clbits)} - other_bit_indices = {bit: idx - for idx, bit in - enumerate(other.qubits + other.clbits)} - - self_qreg_indices = [(regname, [self_bit_indices[bit] for bit in reg]) - for regname, reg in self.qregs.items()] - self_creg_indices = [(regname, [self_bit_indices[bit] for bit in reg]) - for regname, reg in self.cregs.items()] - - other_qreg_indices = [(regname, [other_bit_indices[bit] for bit in reg]) - for regname, reg in other.qregs.items()] - other_creg_indices = [(regname, [other_bit_indices[bit] for bit in reg]) - for regname, reg in other.cregs.items()] - if ( - self_qreg_indices != other_qreg_indices - or self_creg_indices != other_creg_indices - ): + self_bit_indices = {bit: idx for idx, bit in enumerate(self.qubits + self.clbits)} + other_bit_indices = {bit: idx for idx, bit in enumerate(other.qubits + other.clbits)} + + self_qreg_indices = [ + (regname, [self_bit_indices[bit] for bit in reg]) for regname, reg in self.qregs.items() + ] + self_creg_indices = [ + (regname, [self_bit_indices[bit] for bit in reg]) for regname, reg in self.cregs.items() + ] + + other_qreg_indices = [ + (regname, [other_bit_indices[bit] for bit in reg]) + for regname, reg in other.qregs.items() + ] + other_creg_indices = [ + (regname, [other_bit_indices[bit] for bit in reg]) + for regname, reg in other.cregs.items() + ] + if self_qreg_indices != other_qreg_indices or self_creg_indices != other_creg_indices: return False def node_eq(node_self, node_other): - return DAGNode.semantic_eq(node_self, node_other, - self_bit_indices, other_bit_indices) + return DAGNode.semantic_eq(node_self, node_other, self_bit_indices, other_bit_indices) - return rx.is_isomorphic_node_match(self._multi_graph, - other._multi_graph, - node_eq) + return rx.is_isomorphic_node_match(self._multi_graph, other._multi_graph, node_eq) def topological_nodes(self): """ @@ -938,8 +952,7 @@ def topological_nodes(self): def _key(x): return x.sort_key - return iter(rx.lexicographical_topological_sort( - self._multi_graph, key=_key)) + return iter(rx.lexicographical_topological_sort(self._multi_graph, key=_key)) def topological_op_nodes(self): """ @@ -948,7 +961,7 @@ def topological_op_nodes(self): Returns: generator(DAGNode): op node in topological order """ - return (nd for nd in self.topological_nodes() if nd.type == 'op') + return (nd for nd in self.topological_nodes() if nd.type == "op") def substitute_node_with_dag(self, node, input_dag, wires=None): """Replace one node with dag. @@ -964,7 +977,7 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): DAGCircuitError: if met with unexpected predecessor/successors """ in_dag = input_dag - condition = None if node.type != 'op' else node.op.condition + condition = None if node.type != "op" else node.op.condition # the dag must be amended if used in a # conditional context. delete the op nodes and replay @@ -980,11 +993,11 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): for input_node in in_dag.op_nodes(): in_dag.remove_op_node(input_node) for replay_node in to_replay: - in_dag.apply_operation_back(replay_node.op, replay_node.qargs, - replay_node.cargs) + in_dag.apply_operation_back(replay_node.op, replay_node.qargs, replay_node.cargs) if in_dag.global_phase: from sympy import evaluate + with evaluate(False): self.global_phase += in_dag.global_phase @@ -1008,16 +1021,16 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): # If a gate is conditioned, we expect the replacement subcircuit # to depend on those condition bits as well. if node.type != "op": - raise DAGCircuitError("expected node type \"op\", got %s" - % node.type) + raise DAGCircuitError('expected node type "op", got %s' % node.type) condition_bit_list = self._bits_in_condition(condition) wire_map = dict(zip(wires, list(node.qargs) + list(node.cargs) + list(condition_bit_list))) self._check_wiremap_validity(wire_map, wires, self.input_map) pred_map, succ_map = self._make_pred_succ_maps(node) - full_pred_map, full_succ_map = self._full_pred_succ_maps(pred_map, succ_map, - in_dag, wire_map) + full_pred_map, full_succ_map = self._full_pred_succ_maps( + pred_map, succ_map, in_dag, wire_map + ) if condition_bit_list: # If we are replacing a conditional node, map input dag through @@ -1029,8 +1042,9 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): mapped_cargs = {wire_map[carg] for carg in op_node.cargs} if condition_bits & mapped_cargs: - raise DAGCircuitError('Mapped DAG would alter clbits ' - 'on which it would be conditioned.') + raise DAGCircuitError( + "Mapped DAG would alter clbits " "on which it would be conditioned." + ) # Now that we know the connections, delete node self._multi_graph.remove_node(node._node_id) @@ -1038,13 +1052,9 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): # Iterate over nodes of input_circuit for sorted_node in in_dag.topological_op_nodes(): # Insert a new node - condition = self._map_condition(wire_map, - sorted_node.op.condition, - self.cregs.values()) - m_qargs = list(map(lambda x: wire_map.get(x, x), - sorted_node.qargs)) - m_cargs = list(map(lambda x: wire_map.get(x, x), - sorted_node.cargs)) + condition = self._map_condition(wire_map, sorted_node.op.condition, self.cregs.values()) + m_qargs = list(map(lambda x: wire_map.get(x, x), sorted_node.qargs)) + m_cargs = list(map(lambda x: wire_map.get(x, x), sorted_node.cargs)) node_index = self._add_op_node(sorted_node.op, m_qargs, m_cargs) # Add edges from predecessor nodes to new node @@ -1053,17 +1063,13 @@ def substitute_node_with_dag(self, node, input_dag, wires=None): all_cbits.extend(m_cargs) al = [m_qargs, all_cbits] for q in itertools.chain(*al): - self._multi_graph.add_edge(full_pred_map[q], - node_index, - q) + self._multi_graph.add_edge(full_pred_map[q], node_index, q) full_pred_map[q] = node_index # Connect all predecessors and successors, and remove # residual edges between input and output nodes for w in full_pred_map: - self._multi_graph.add_edge(full_pred_map[w], - full_succ_map[w], - w) + self._multi_graph.add_edge(full_pred_map[w], full_succ_map[w], w) o_pred = self._multi_graph.predecessors(self.output_map[w]._node_id) if len(o_pred) > 1: if len(o_pred) != 2: @@ -1097,18 +1103,16 @@ def substitute_node(self, node, op, inplace=False): location of target node. """ - if node.type != 'op': + if node.type != "op": raise DAGCircuitError('Only DAGNodes of type "op" can be replaced.') - if ( - node.op.num_qubits != op.num_qubits - or node.op.num_clbits != op.num_clbits - ): + if node.op.num_qubits != op.num_qubits or node.op.num_clbits != op.num_clbits: raise DAGCircuitError( - 'Cannot replace node of width ({} qubits, {} clbits) with ' - 'instruction of mismatched width ({} qubits, {} clbits).'.format( - node.op.num_qubits, node.op.num_clbits, - op.num_qubits, op.num_clbits)) + "Cannot replace node of width ({} qubits, {} clbits) with " + "instruction of mismatched width ({} qubits, {} clbits).".format( + node.op.num_qubits, node.op.num_clbits, op.num_qubits, op.num_clbits + ) + ) if inplace: save_condition = node.op.condition @@ -1167,9 +1171,7 @@ def edges(self, nodes=None): for node in nodes: raw_nodes = self._multi_graph.out_edges(node._node_id) for source, dest, edge in raw_nodes: - yield (self._multi_graph[source], - self._multi_graph[dest], - edge) + yield (self._multi_graph[source], self._multi_graph[dest], edge) def op_nodes(self, op=None, include_directives=True): """Get the list of "op" nodes in the dag. @@ -1207,15 +1209,18 @@ def named_nodes(self, *names): """Get the set of "op" nodes with the given name.""" named_nodes = [] for node in self._multi_graph.nodes(): - if node.type == 'op' and node.op.name in names: + if node.type == "op" and node.op.name in names: named_nodes.append(node) return named_nodes def twoQ_gates(self): """Get list of 2-qubit gates. Ignore snapshot, barriers, and the like.""" - warnings.warn('deprecated function, use dag.two_qubit_ops(). ' - 'filter output by isinstance(op, Gate) to only get unitary Gates.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "deprecated function, use dag.two_qubit_ops(). " + "filter output by isinstance(op, Gate) to only get unitary Gates.", + DeprecationWarning, + stacklevel=2, + ) two_q_gates = [] for node in self.gate_nodes(): if len(node.qargs) == 2: @@ -1224,9 +1229,12 @@ def twoQ_gates(self): def threeQ_or_more_gates(self): """Get list of 3-or-more-qubit gates: (id, data).""" - warnings.warn('deprecated function, use dag.multi_qubit_ops(). ' - 'filter output by isinstance(op, Gate) to only get unitary Gates.', - DeprecationWarning, stacklevel=2) + warnings.warn( + "deprecated function, use dag.multi_qubit_ops(). " + "filter output by isinstance(op, Gate) to only get unitary Gates.", + DeprecationWarning, + stacklevel=2, + ) three_q_gates = [] for node in self.gate_nodes(): if len(node.qargs) >= 3: @@ -1265,22 +1273,21 @@ def quantum_predecessors(self, node): """Returns iterator of the predecessors of a node that are connected by a quantum edge as DAGNodes.""" for predecessor in self.predecessors(node): - if any(isinstance(edge_data, Qubit) for edge_data in - self._multi_graph.get_all_edge_data( - predecessor._node_id, node._node_id)): + if any( + isinstance(edge_data, Qubit) + for edge_data in self._multi_graph.get_all_edge_data( + predecessor._node_id, node._node_id + ) + ): yield predecessor def ancestors(self, node): """Returns set of the ancestors of a node as DAGNodes.""" - return { - self._multi_graph[x] for x in rx.ancestors( - self._multi_graph, node._node_id)} + return {self._multi_graph[x] for x in rx.ancestors(self._multi_graph, node._node_id)} def descendants(self, node): """Returns set of the descendants of a node as DAGNodes.""" - return { - self._multi_graph[x] for x in rx.descendants( - self._multi_graph, node._node_id)} + return {self._multi_graph[x] for x in rx.descendants(self._multi_graph, node._node_id)} def bfs_successors(self, node): """ @@ -1293,9 +1300,12 @@ def quantum_successors(self, node): """Returns iterator of the successors of a node that are connected by a quantum edge as DAGNodes.""" for successor in self.successors(node): - if any(isinstance(edge_data, Qubit) for edge_data in - self._multi_graph.get_all_edge_data( - node._node_id, successor._node_id)): + if any( + isinstance(edge_data, Qubit) + for edge_data in self._multi_graph.get_all_edge_data( + node._node_id, successor._node_id + ) + ): yield successor def remove_op_node(self, node): @@ -1303,13 +1313,15 @@ def remove_op_node(self, node): Add edges from predecessors to successors. """ - if node.type != 'op': - raise DAGCircuitError('The method remove_op_node only works on op node types. An "%s" ' - 'node type was wrongly provided.' % node.type) + if node.type != "op": + raise DAGCircuitError( + 'The method remove_op_node only works on op node types. An "%s" ' + "node type was wrongly provided." % node.type + ) self._multi_graph.remove_node_retain_edges( - node._node_id, use_outgoing=False, - condition=lambda edge1, edge2: edge1 == edge2) + node._node_id, use_outgoing=False, condition=lambda edge1, edge2: edge1 == edge2 + ) def remove_ancestors_of(self, node): """Remove all of the ancestor operation nodes of node.""" @@ -1345,8 +1357,7 @@ def remove_nondescendants_of(self, node): self.remove_op_node(n) def front_layer(self): - """Return a list of op nodes in the first layer of this dag. - """ + """Return a list of op nodes in the first layer of this dag.""" graph_layers = self.multigraph_layers() try: next(graph_layers) # Remove input nodes @@ -1402,15 +1413,11 @@ def layers(self): for node in op_nodes: # this creates new DAGNodes in the new_layer - new_layer.apply_operation_back(node.op, - node.qargs, - node.cargs) + new_layer.apply_operation_back(node.op, node.qargs, node.cargs) # The quantum registers that have an operation in this layer. support_list = [ - op_node.qargs - for op_node in new_layer.op_nodes() - if not op_node.op._directive + op_node.qargs for op_node in new_layer.op_nodes() if not op_node.op._directive ] yield {"graph": new_layer, "partition": support_list} @@ -1469,11 +1476,15 @@ def collect_1q_runs(self): """Return a set of non-conditional runs of 1q "op" nodes.""" def filter_fn(node): - return node.type == 'op' and len(node.qargs) == 1 \ - and len(node.cargs) == 0 and node.op.condition is None \ - and not node.op.is_parameterized() \ - and isinstance(node.op, Gate) \ - and hasattr(node.op, '__array__') + return ( + node.type == "op" + and len(node.qargs) == 1 + and len(node.cargs) == 0 + and node.op.condition is None + and not node.op.is_parameterized() + and isinstance(node.op, Gate) + and hasattr(node.op, "__array__") + ) return rx.collect_runs(self._multi_graph, filter_fn) @@ -1494,19 +1505,19 @@ def nodes_on_wire(self, wire, only_ops=False): current_node = self.input_map.get(wire, None) if not current_node: - raise DAGCircuitError('The given wire %s is not present in the circuit' - % str(wire)) + raise DAGCircuitError("The given wire %s is not present in the circuit" % str(wire)) more_nodes = True while more_nodes: more_nodes = False # allow user to just get ops on the wire - not the input/output nodes - if current_node.type == 'op' or not only_ops: + if current_node.type == "op" or not only_ops: yield current_node try: current_node = self._multi_graph.find_adjacent_node_by_edge( - current_node._node_id, lambda x: wire == x) + current_node._node_id, lambda x: wire == x + ) more_nodes = True except rx.NoSuitableNeighbors: pass @@ -1532,7 +1543,7 @@ def count_ops_longest_path(self): """ op_dict = {} path = self.longest_path() - path = path[1:-1] # remove qubits at beginning and end of path + path = path[1:-1] # remove qubits at beginning and end of path for node in path: name = node.name if name not in op_dict: @@ -1543,16 +1554,18 @@ def count_ops_longest_path(self): def properties(self): """Return a dictionary of circuit properties.""" - summary = {"size": self.size(), - "depth": self.depth(), - "width": self.width(), - "qubits": self.num_qubits(), - "bits": self.num_clbits(), - "factors": self.num_tensor_factors(), - "operations": self.count_ops()} + summary = { + "size": self.size(), + "depth": self.depth(), + "width": self.width(), + "qubits": self.num_qubits(), + "bits": self.num_clbits(), + "factors": self.num_tensor_factors(), + "operations": self.count_ops(), + } return summary - def draw(self, scale=0.7, filename=None, style='color'): + def draw(self, scale=0.7, filename=None, style="color"): """ Draws the dag circuit. @@ -1571,4 +1584,5 @@ def draw(self, scale=0.7, filename=None, style='color'): otherwise None. """ from qiskit.visualization.dag_visualization import dag_drawer + return dag_drawer(dag=self, scale=scale, filename=filename, style=style) diff --git a/qiskit/dagcircuit/dagdependency.py b/qiskit/dagcircuit/dagdependency.py index 636af3f1ca05..9aaefe2dddb8 100644 --- a/qiskit/dagcircuit/dagdependency.py +++ b/qiskit/dagcircuit/dagdependency.py @@ -89,7 +89,7 @@ def __init__(self): self._calibrations = defaultdict(dict) self.duration = None - self.unit = 'dt' + self.unit = "dt" @property def global_phase(self): @@ -104,6 +104,7 @@ def global_phase(self, angle): angle (float, ParameterExpression) """ from qiskit.circuit.parameterexpression import ParameterExpression # needed? + if isinstance(angle, ParameterExpression): self._global_phase = angle else: @@ -140,24 +141,25 @@ def to_networkx(self): try: import networkx as nx except ImportError as ex: - raise ImportError("Networkx is needed to use to_networkx(). It " - "can be installed with 'pip install networkx'") from ex + raise ImportError( + "Networkx is needed to use to_networkx(). It " + "can be installed with 'pip install networkx'" + ) from ex dag_networkx = nx.MultiDiGraph() for node in self.get_nodes(): dag_networkx.add_node(node) for node in self.topological_nodes(): - for source_id, dest_id, edge in \ - self.get_in_edges(node.node_id): + for source_id, dest_id, edge in self.get_in_edges(node.node_id): dag_networkx.add_edge(self.get_node(source_id), self.get_node(dest_id), **edge) return dag_networkx def to_retworkx(self): - """ Returns the DAGDependency in retworkx format.""" + """Returns the DAGDependency in retworkx format.""" return self._multi_graph def size(self): - """ Returns the number of gates in the circuit""" + """Returns the number of gates in the circuit""" return len(self._multi_graph) def depth(self): @@ -276,10 +278,11 @@ def get_all_edges(self): List: corresponding to the label. """ - return [(src, dest, data) - for src_node in self._multi_graph.nodes() - for (src, dest, data) - in self._multi_graph.out_edges(src_node.node_id)] + return [ + (src, dest, data) + for src_node in self._multi_graph.nodes() + for (src, dest, data) in self._multi_graph.out_edges(src_node.node_id) + ] def get_in_edges(self, node_id): """ @@ -364,9 +367,7 @@ def topological_nodes(self): def _key(x): return x.sort_key - return iter(rx.lexicographical_topological_sort( - self._multi_graph, - key=_key)) + return iter(rx.lexicographical_topological_sort(self._multi_graph, key=_key)) def add_op_node(self, operation, qargs, cargs): """Add a DAGDepNode to the graph and update the edges. @@ -376,7 +377,7 @@ def add_op_node(self, operation, qargs, cargs): qargs (list[Qubit]): list of qubits on which the operation acts cargs (list[Clbit]): list of classical wires to attach to. """ - directives = ['measure'] + directives = ["measure"] if not operation._directive and operation.name not in directives: qindices_list = [] for elem in qargs: @@ -394,9 +395,17 @@ def add_op_node(self, operation, qargs, cargs): qindices_list = [] cindices_list = [] - new_node = DAGDepNode(type="op", op=operation, name=operation.name, qargs=qargs, - cargs=cargs, successors=[], predecessors=[], - qindices=qindices_list, cindices=cindices_list) + new_node = DAGDepNode( + type="op", + op=operation, + name=operation.name, + qargs=qargs, + cargs=cargs, + successors=[], + predecessors=[], + qindices=qindices_list, + cindices=cindices_list, + ) self._add_multi_graph_node(new_node) self._update_edges() @@ -451,7 +460,8 @@ def _list_pred(self, node_id): direct_pred = self.direct_predecessors(node_id) self._multi_graph = self._gather_pred(node_id, direct_pred) self._multi_graph.get_node_data(node_id).predecessors = list( - merge_no_duplicates(*(self._multi_graph.get_node_data(node_id).predecessors))) + merge_no_duplicates(*(self._multi_graph.get_node_data(node_id).predecessors)) + ) def _update_edges(self): """ @@ -468,8 +478,9 @@ def _update_edges(self): # Check the commutation relation with reachable node, it adds edges if it does not commute for prev_node_id in range(max_node_id - 1, -1, -1): if self._multi_graph.get_node_data(prev_node_id).reachable and not _does_commute( - self._multi_graph.get_node_data(prev_node_id), max_node): - self._multi_graph.add_edge(prev_node_id, max_node_id, {'commute': False}) + self._multi_graph.get_node_data(prev_node_id), max_node + ): + self._multi_graph.add_edge(prev_node_id, max_node_id, {"commute": False}) self._list_pred(max_node_id) list_predecessors = self._multi_graph.get_node_data(max_node_id).predecessors for pred_id in list_predecessors: @@ -487,7 +498,8 @@ def _add_successors(self): self._multi_graph = self._gather_succ(node_id, direct_successors) self._multi_graph.get_node_data(node_id).successors = list( - merge_no_duplicates(*self._multi_graph.get_node_data(node_id).successors)) + merge_no_duplicates(*self._multi_graph.get_node_data(node_id).successors) + ) def copy(self): """ @@ -507,7 +519,7 @@ def copy(self): dag._multi_graph.add_edge(edges[0], edges[1], edges[2]) return dag - def draw(self, scale=0.7, filename=None, style='color'): + def draw(self, scale=0.7, filename=None, style="color"): """ Draws the DAGDependency graph. @@ -525,8 +537,8 @@ def draw(self, scale=0.7, filename=None, style='color'): otherwise None. """ from qiskit.visualization.dag_visualization import dag_drawer - return dag_drawer(dag=self, scale=scale, filename=filename, - style=style) + + return dag_drawer(dag=self, scale=scale, filename=filename, style=style) def merge_no_duplicates(*iterables): @@ -568,7 +580,7 @@ def _does_commute(node1, node2): # if and only if the qubits are different. # TODO: qubits can be the same if conditions are identical and # the non-conditional gates commute. - if node1.type == 'op' and node2.type == 'op': + if node1.type == "op" and node2.type == "op": if node1.op.condition or node2.op.condition: intersection = set(qarg1).intersection(set(qarg2)) return not intersection @@ -576,12 +588,10 @@ def _does_commute(node1, node2): # Commutation for non-unitary or parameterized or opaque ops # (e.g. measure, reset, directives or pulse gates) # if and only if the qubits and clbits are different. - non_unitaries = ['measure', 'reset', 'initialize', 'delay'] + non_unitaries = ["measure", "reset", "initialize", "delay"] def _unknown_commutator(n): - return (n.op._directive or - n.name in non_unitaries or - n.op.is_parameterized()) + return n.op._directive or n.name in non_unitaries or n.op.is_parameterized() if _unknown_commutator(node1) or _unknown_commutator(node2): intersection_q = set(qarg1).intersection(set(qarg2)) @@ -589,7 +599,7 @@ def _unknown_commutator(n): return not (intersection_q or intersection_c) # Known non-commuting gates (TODO: add more). - non_commute_gates = [{'x', 'y'}, {'x', 'z'}] + non_commute_gates = [{"x", "y"}, {"x", "z"}] if qarg1 == qarg2 and ({node1.name, node2.name} in non_commute_gates): return False diff --git a/qiskit/dagcircuit/dagdepnode.py b/qiskit/dagcircuit/dagdepnode.py index b03ea66c9dd6..78a2979bc349 100644 --- a/qiskit/dagcircuit/dagdepnode.py +++ b/qiskit/dagcircuit/dagdepnode.py @@ -26,15 +26,42 @@ class DAGDepNode: be supplied to functions that take a node. """ - __slots__ = ['type', '_op', 'name', '_qargs', 'cargs', - 'sort_key', 'node_id', 'successors', 'predecessors', - 'reachable', 'matchedwith', 'isblocked', 'successorstovisit', - 'qindices', 'cindices'] - - def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, - condition=None, successors=None, predecessors=None, reachable=None, - matchedwith=None, successorstovisit=None, isblocked=None, qindices=None, - cindices=None, nid=-1): + __slots__ = [ + "type", + "_op", + "name", + "_qargs", + "cargs", + "sort_key", + "node_id", + "successors", + "predecessors", + "reachable", + "matchedwith", + "isblocked", + "successorstovisit", + "qindices", + "cindices", + ] + + def __init__( + self, + type=None, + op=None, + name=None, + qargs=None, + cargs=None, + condition=None, + successors=None, + predecessors=None, + reachable=None, + matchedwith=None, + successorstovisit=None, + isblocked=None, + qindices=None, + cindices=None, + nid=-1, + ): self.type = type self._op = op @@ -47,7 +74,9 @@ def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, "as of 0.18.0 and will be removed no earlier than 3 months after the " "release date. You can use 'DAGDepNode.op.condition' if the DAGDepNode " "is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) self.node_id = nid self.sort_key = str(self._qargs) self.successors = successors if successors is not None else [] @@ -62,7 +91,7 @@ def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, @property def op(self): """Returns the Instruction object corresponding to the op for the node, else None""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) return self._op @@ -73,25 +102,29 @@ def op(self, data): @property def condition(self): """Returns the condition of the node.op""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) warnings.warn( "The DAGDepNode 'condition' attribute is deprecated as of 0.18.0 and " "will be removed no earlier than 3 months after the release date. " "You can use 'DAGDepNode.op.condition' if the DAGDepNode is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) return self._op.condition @condition.setter def condition(self, new_condition): """Sets the node.condition which sets the node.op.condition.""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) warnings.warn( "The DAGDepNode 'condition' attribute is deprecated as of 0.18.0 and " "will be removed no earlier than 3 months after the release date. " "You can use 'DAGDepNode.op.condition' if the DAGDepNode is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) self._op.condition = new_condition @property @@ -120,7 +153,7 @@ def semantic_eq(node1, node2): Bool: If node1 == node2 """ # For barriers, qarg order is not significant so compare as sets - if 'barrier' == node1.name == node2.name: + if "barrier" == node1.name == node2.name: return set(node1._qargs) == set(node2._qargs) if node1.type == node2.type: @@ -128,7 +161,7 @@ def semantic_eq(node1, node2): if node1.name == node2.name: if node1._qargs == node2._qargs: if node1.cargs == node2.cargs: - if node1.type == 'op': + if node1.type == "op": if node1._op.condition != node2._op.condition: return False return True diff --git a/qiskit/dagcircuit/dagnode.py b/qiskit/dagcircuit/dagnode.py index 6ca7ccba4f61..6934da6a3db3 100644 --- a/qiskit/dagcircuit/dagnode.py +++ b/qiskit/dagcircuit/dagnode.py @@ -26,11 +26,10 @@ class DAGNode: be supplied to functions that take a node. """ - __slots__ = ['type', '_op', '_qargs', 'cargs', '_wire', 'sort_key', '_node_id'] + __slots__ = ["type", "_op", "_qargs", "cargs", "_wire", "sort_key", "_node_id"] - def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, - wire=None, nid=-1): - """Create a node """ + def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, wire=None, nid=-1): + """Create a node""" self.type = type self._op = op if name is not None: @@ -38,7 +37,9 @@ def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, "The DAGNode 'name' attribute is deprecated as of 0.18.0 and " "will be removed no earlier than 3 months after the release date. " "You can use 'DAGNode.op.name' if the DAGNode is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) self._qargs = qargs if qargs is not None else [] self.cargs = cargs if cargs is not None else [] self._wire = wire @@ -48,7 +49,7 @@ def __init__(self, type=None, op=None, name=None, qargs=None, cargs=None, @property def op(self): """Returns the Instruction object corresponding to the op for the node, else None""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) return self._op @@ -59,37 +60,41 @@ def op(self, data): @property def name(self): """Returns the Instruction name corresponding to the op for this node""" - if self.type and self.type == 'op': + if self.type and self.type == "op": return self._op.name return None @name.setter def name(self, name): - if self.type and self.type == 'op': + if self.type and self.type == "op": self._op.name = name @property def condition(self): """Returns the condition of the node.op""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) warnings.warn( "The DAGNode 'condition' attribute is deprecated as of 0.18.0 and " "will be removed no earlier than 3 months after the release date. " "You can use 'DAGNode.op.condition' if the DAGNode is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) return self._op.condition @condition.setter def condition(self, new_condition): """Sets the node.condition which sets the node.op.condition.""" - if not self.type or self.type != 'op': + if not self.type or self.type != "op": raise QiskitError("The node %s is not an op node" % (str(self))) warnings.warn( "The DAGNode 'condition' attribute is deprecated as of 0.18.0 and " "will be removed no earlier than 3 months after the release date. " "You can use 'DAGNode.op.condition' if the DAGNode is of type 'op'.", - DeprecationWarning, 2) + DeprecationWarning, + 2, + ) self._op.condition = new_condition @property @@ -110,8 +115,8 @@ def wire(self): """ Returns the Bit object, else None. """ - if self.type not in ['in', 'out']: - raise QiskitError('The node %s is not an input/output node' % str(self)) + if self.type not in ["in", "out"]: + raise QiskitError("The node %s is not an input/output node" % str(self)) return self._wire @wire.setter @@ -147,12 +152,13 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None): """ if bit_indices1 is None or bit_indices2 is None: warnings.warn( - 'DAGNode.semantic_eq now expects two bit-to-circuit index ' - 'mappings as arguments. To ease the transition, these will be ' - 'pre-populated based on the values found in Bit.index and ' - 'Bit.register. However, this behavior is deprecated and a future ' - 'release will require the mappings to be provided as arguments.', - DeprecationWarning) + "DAGNode.semantic_eq now expects two bit-to-circuit index " + "mappings as arguments. To ease the transition, these will be " + "pre-populated based on the values found in Bit.index and " + "Bit.register. However, this behavior is deprecated and a future " + "release will require the mappings to be provided as arguments.", + DeprecationWarning, + ) bit_indices1 = {arg: arg for arg in node1.qargs + node1.cargs} bit_indices2 = {arg: arg for arg in node2.qargs + node2.cargs} @@ -164,7 +170,7 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None): node2_cargs = [bit_indices2[carg] for carg in node2.cargs] # For barriers, qarg order is not significant so compare as sets - if 'barrier' == node1.name == node2.name: + if "barrier" == node1.name == node2.name: return set(node1_qargs) == set(node2_qargs) if node1.type == node2.type: @@ -172,12 +178,11 @@ def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None): if node1.name == node2.name: if node1_qargs == node2_qargs: if node1_cargs == node2_cargs: - if node1.type == 'op': + if node1.type == "op": if node1._op.condition != node2._op.condition: return False - if ( - bit_indices1.get(node1._wire, None) - == bit_indices2.get(node2._wire, None) + if bit_indices1.get(node1._wire, None) == bit_indices2.get( + node2._wire, None ): return True return False diff --git a/qiskit/dagcircuit/exceptions.py b/qiskit/dagcircuit/exceptions.py index f3dc98e2e032..372b1e1bac39 100644 --- a/qiskit/dagcircuit/exceptions.py +++ b/qiskit/dagcircuit/exceptions.py @@ -22,7 +22,7 @@ class DAGCircuitError(QiskitError): def __init__(self, *msg): """Set the error message.""" super().__init__(*msg) - self.msg = ' '.join(msg) + self.msg = " ".join(msg) def __str__(self): """Return the message.""" @@ -35,7 +35,7 @@ class DAGDependencyError(QiskitError): def __init__(self, *msg): """Set the error message.""" super().__init__(*msg) - self.msg = ' '.join(msg) + self.msg = " ".join(msg) def __str__(self): """Return the message.""" diff --git a/qiskit/exceptions.py b/qiskit/exceptions.py index 1047daec50bc..1f5126229db9 100644 --- a/qiskit/exceptions.py +++ b/qiskit/exceptions.py @@ -20,8 +20,8 @@ class QiskitError(Exception): def __init__(self, *message): """Set the error message.""" - super().__init__(' '.join(message)) - self.message = ' '.join(message) + super().__init__(" ".join(message)) + self.message = " ".join(message) def __str__(self): """Return the message.""" @@ -30,22 +30,22 @@ def __str__(self): class QiskitIndexError(QiskitError, IndexError): """Raised when a sequence subscript is out of range.""" + pass class QiskitUserConfigError(QiskitError): """Raised when an error is encountered reading a user config file.""" + message = "User config invalid" class MissingOptionalLibraryError(QiskitError): """Raised when an optional library is missing.""" - def __init__(self, - libname: str, - name: str, - pip_install: Optional[str] = None, - msg: Optional[str] = None) -> None: + def __init__( + self, libname: str, name: str, pip_install: Optional[str] = None, msg: Optional[str] = None + ) -> None: """Set the error message. Args: libname: Name of missing library @@ -57,10 +57,10 @@ def __init__(self, if pip_install: message.append("You can install it with '{}'.".format(pip_install)) if msg: - message.append(' {}.'.format(msg)) + message.append(" {}.".format(msg)) - super().__init__(' '.join(message)) - self.message = ' '.join(message) + super().__init__(" ".join(message)) + self.message = " ".join(message) def __str__(self) -> str: """Return the message.""" diff --git a/qiskit/execute_function.py b/qiskit/execute_function.py index 78ea202a258c..36c9e5981483 100644 --- a/qiskit/execute_function.py +++ b/qiskit/execute_function.py @@ -32,24 +32,43 @@ def _log_submission_time(start_time, end_time): - log_msg = ("Total Job Submission Time - %.5f (ms)" - % ((end_time - start_time) * 1000)) + log_msg = "Total Job Submission Time - %.5f (ms)" % ((end_time - start_time) * 1000) logger.info(log_msg) -def execute(experiments, backend, - basis_gates=None, coupling_map=None, # circuit transpile options - backend_properties=None, initial_layout=None, - seed_transpiler=None, optimization_level=None, pass_manager=None, - qobj_id=None, qobj_header=None, shots=None, # common run options - memory=False, max_credits=None, seed_simulator=None, - default_qubit_los=None, default_meas_los=None, # schedule run options - schedule_los=None, meas_level=None, - meas_return=None, - memory_slots=None, memory_slot_size=None, rep_time=None, rep_delay=None, - parameter_binds=None, schedule_circuit=False, inst_map=None, meas_map=None, - scheduling_method=None, init_qubits=None, - **run_config): +def execute( + experiments, + backend, + basis_gates=None, + coupling_map=None, # circuit transpile options + backend_properties=None, + initial_layout=None, + seed_transpiler=None, + optimization_level=None, + pass_manager=None, + qobj_id=None, + qobj_header=None, + shots=None, # common run options + memory=False, + max_credits=None, + seed_simulator=None, + default_qubit_los=None, + default_meas_los=None, # schedule run options + schedule_los=None, + meas_level=None, + meas_return=None, + memory_slots=None, + memory_slot_size=None, + rep_time=None, + rep_delay=None, + parameter_binds=None, + schedule_circuit=False, + inst_map=None, + meas_map=None, + scheduling_method=None, + init_qubits=None, + **run_config, +): """Execute a list of :class:`qiskit.circuit.QuantumCircuit` or :class:`qiskit.pulse.Schedule` on a backend. @@ -233,37 +252,44 @@ def execute(experiments, backend, job = execute(qc, backend, shots=4321) """ - if isinstance(experiments, Schedule) or (isinstance(experiments, list) and - isinstance(experiments[0], Schedule)): + if isinstance(experiments, Schedule) or ( + isinstance(experiments, list) and isinstance(experiments[0], Schedule) + ): # do not transpile a schedule circuit if schedule_circuit: raise QiskitError("Must supply QuantumCircuit to schedule circuit.") elif pass_manager is not None: # transpiling using pass_manager - _check_conflicting_argument(optimization_level=optimization_level, - basis_gates=basis_gates, - coupling_map=coupling_map, - seed_transpiler=seed_transpiler, - backend_properties=backend_properties, - initial_layout=initial_layout) + _check_conflicting_argument( + optimization_level=optimization_level, + basis_gates=basis_gates, + coupling_map=coupling_map, + seed_transpiler=seed_transpiler, + backend_properties=backend_properties, + initial_layout=initial_layout, + ) experiments = pass_manager.run(experiments) else: # transpiling the circuits using given transpile options - experiments = transpile(experiments, - basis_gates=basis_gates, - coupling_map=coupling_map, - backend_properties=backend_properties, - initial_layout=initial_layout, - seed_transpiler=seed_transpiler, - optimization_level=optimization_level, - backend=backend) + experiments = transpile( + experiments, + basis_gates=basis_gates, + coupling_map=coupling_map, + backend_properties=backend_properties, + initial_layout=initial_layout, + seed_transpiler=seed_transpiler, + optimization_level=optimization_level, + backend=backend, + ) if schedule_circuit: - experiments = schedule(circuits=experiments, - backend=backend, - inst_map=inst_map, - meas_map=meas_map, - method=scheduling_method) + experiments = schedule( + circuits=experiments, + backend=backend, + inst_map=inst_map, + meas_map=meas_map, + method=scheduling_method, + ) if isinstance(backend, BaseBackend): # assembling the circuits into a qobj to be run on the backend @@ -278,26 +304,28 @@ def execute(experiments, backend, if memory_slot_size is None: memory_slot_size = 100 - qobj = assemble(experiments, - qobj_id=qobj_id, - qobj_header=qobj_header, - shots=shots, - memory=memory, - max_credits=max_credits, - seed_simulator=seed_simulator, - default_qubit_los=default_qubit_los, - default_meas_los=default_meas_los, - schedule_los=schedule_los, - meas_level=meas_level, - meas_return=meas_return, - memory_slots=memory_slots, - memory_slot_size=memory_slot_size, - rep_time=rep_time, - rep_delay=rep_delay, - parameter_binds=parameter_binds, - backend=backend, - init_qubits=init_qubits, - **run_config) + qobj = assemble( + experiments, + qobj_id=qobj_id, + qobj_header=qobj_header, + shots=shots, + memory=memory, + max_credits=max_credits, + seed_simulator=seed_simulator, + default_qubit_los=default_qubit_los, + default_meas_los=default_meas_los, + schedule_los=schedule_los, + meas_level=meas_level, + meas_return=meas_return, + memory_slots=memory_slots, + memory_slot_size=memory_slot_size, + rep_time=rep_time, + rep_delay=rep_delay, + parameter_binds=parameter_binds, + backend=backend, + init_qubits=init_qubits, + **run_config, + ) # executing the circuits on the backend and returning the job start_time = time() @@ -307,38 +335,42 @@ def execute(experiments, backend, elif isinstance(backend, Backend): start_time = time() run_kwargs = { - 'shots': shots, - 'memory': memory, - 'seed_simulator': seed_simulator, - 'default_qubit_los': default_qubit_los, - 'default_meas_los': default_meas_los, - 'schedule_los': schedule_los, - 'meas_level': meas_level, - 'meas_return': meas_return, - 'memory_slots': memory_slots, - 'memory_slot_size': memory_slot_size, - 'rep_time': rep_time, - 'rep_delay': rep_delay, - 'init_qubits': init_qubits, + "shots": shots, + "memory": memory, + "seed_simulator": seed_simulator, + "default_qubit_los": default_qubit_los, + "default_meas_los": default_meas_los, + "schedule_los": schedule_los, + "meas_level": meas_level, + "meas_return": meas_return, + "memory_slots": memory_slots, + "memory_slot_size": memory_slot_size, + "rep_time": rep_time, + "rep_delay": rep_delay, + "init_qubits": init_qubits, } for key in list(run_kwargs.keys()): if not hasattr(backend.options, key): if run_kwargs[key] is not None: - logger.info("%s backend doesn't support option %s so not " - "passing that kwarg to run()", backend.name, key) + logger.info( + "%s backend doesn't support option %s so not " + "passing that kwarg to run()", + backend.name, + key, + ) del run_kwargs[key] - elif key == 'shots' and run_kwargs[key] is None: + elif key == "shots" and run_kwargs[key] is None: run_kwargs[key] = 1024 - elif key == 'max_credits' and run_kwargs[key] is None: + elif key == "max_credits" and run_kwargs[key] is None: run_kwargs[key] = 10 - elif key == 'meas_level' and run_kwargs[key] is None: + elif key == "meas_level" and run_kwargs[key] is None: run_kwargs[key] = MeasLevel.CLASSIFIED - elif key == 'meas_return' and run_kwargs[key] is None: + elif key == "meas_return" and run_kwargs[key] is None: run_kwargs[key] = MeasReturnType.AVERAGE - elif key == 'memory_slot_size' and run_kwargs[key] is None: + elif key == "memory_slot_size" and run_kwargs[key] is None: run_kwargs[key] = 100 - run_kwargs['parameter_binds'] = parameter_binds + run_kwargs["parameter_binds"] = parameter_binds run_kwargs.update(run_config) job = backend.run(experiments, **run_kwargs) end_time = time() @@ -351,5 +383,7 @@ def execute(experiments, backend, def _check_conflicting_argument(**kargs): conflicting_args = [arg for arg, value in kargs.items() if value] if conflicting_args: - raise QiskitError("The parameters pass_manager conflicts with the following " - "parameter(s): {}.".format(', '.join(conflicting_args))) + raise QiskitError( + "The parameters pass_manager conflicts with the following " + "parameter(s): {}.".format(", ".join(conflicting_args)) + ) diff --git a/qiskit/extensions/exceptions.py b/qiskit/extensions/exceptions.py index a9c515d7339d..b125dfd178b8 100644 --- a/qiskit/extensions/exceptions.py +++ b/qiskit/extensions/exceptions.py @@ -22,7 +22,7 @@ class ExtensionError(QiskitError): def __init__(self, *message): """Set the error message.""" super().__init__(*message) - self.message = ' '.join(message) + self.message = " ".join(message) def __str__(self): """Return the message.""" diff --git a/qiskit/extensions/hamiltonian_gate.py b/qiskit/extensions/hamiltonian_gate.py index 23cfa2812963..12739e2c6e95 100644 --- a/qiskit/extensions/hamiltonian_gate.py +++ b/qiskit/extensions/hamiltonian_gate.py @@ -30,7 +30,7 @@ class HamiltonianGate(Gate): """Class for representing evolution by a Hermitian Hamiltonian operator as a gate. This gate resolves to a UnitaryGate U(t) = exp(-1j * t * H), which can be decomposed into basis gates if - it is 2 qubits or less, or simulated directly in Aer for more qubits. """ + it is 2 qubits or less, or simulated directly in Aer for more qubits.""" def __init__(self, data, time, label=None): """Create a gate from a hamiltonian operator and evolution time parameter t @@ -43,11 +43,11 @@ def __init__(self, data, time, label=None): Raises: ExtensionError: if input data is not an N-qubit unitary operator. """ - if hasattr(data, 'to_matrix'): + if hasattr(data, "to_matrix"): # If input is Gate subclass or some other class object that has # a to_matrix method this will call that method. data = data.to_matrix() - elif hasattr(data, 'to_operator'): + elif hasattr(data, "to_operator"): # If input is a BaseOperator subclass this attempts to convert # the object to an Operator so that we can extract the underlying # numpy matrix from `Operator.data`. @@ -62,12 +62,11 @@ def __init__(self, data, time, label=None): # Check input is N-qubit matrix input_dim, output_dim = data.shape num_qubits = int(numpy.log2(input_dim)) - if input_dim != output_dim or 2**num_qubits != input_dim: - raise ExtensionError( - "Input matrix is not an N-qubit operator.") + if input_dim != output_dim or 2 ** num_qubits != input_dim: + raise ExtensionError("Input matrix is not an N-qubit operator.") # Store instruction params - super().__init__('hamiltonian', num_qubits, [data, time], label=label) + super().__init__("hamiltonian", num_qubits, [data, time], label=label) def __eq__(self, other): if not isinstance(other, HamiltonianGate): @@ -84,8 +83,10 @@ def __array__(self, dtype=None): try: return scipy.linalg.expm(-1j * self.params[0] * float(self.params[1])) except TypeError as ex: - raise TypeError("Unable to generate Unitary matrix for " - "unbound t parameter {}".format(self.params[1])) from ex + raise TypeError( + "Unable to generate Unitary matrix for " + "unbound t parameter {}".format(self.params[1]) + ) from ex def inverse(self): """Return the adjoint of the unitary.""" @@ -105,7 +106,7 @@ def transpose(self): def _define(self): """Calculate a subcircuit that implements this unitary.""" - q = QuantumRegister(self.num_qubits, 'q') + q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) qc._append(UnitaryGate(self.to_matrix()), q[:], []) self.definition = qc @@ -121,8 +122,9 @@ def validate_parameter(self, parameter): elif isinstance(parameter, ParameterExpression) and len(parameter.parameters) == 0: return float(parameter) else: - raise CircuitError("invalid param type {0} for gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} for gate " "{1}".format(type(parameter), self.name) + ) def hamiltonian(self, operator, time, qubits, label=None): diff --git a/qiskit/extensions/quantum_initializer/diagonal.py b/qiskit/extensions/quantum_initializer/diagonal.py index 55c9bb63bb4c..8f83bcb77bfd 100644 --- a/qiskit/extensions/quantum_initializer/diagonal.py +++ b/qiskit/extensions/quantum_initializer/diagonal.py @@ -52,8 +52,9 @@ def __init__(self, diag): try: complex(z) except TypeError as ex: - raise QiskitError("Not all of the diagonal entries can be converted to " - "complex numbers.") from ex + raise QiskitError( + "Not all of the diagonal entries can be converted to " "complex numbers." + ) from ex if not np.abs(z) - 1 < _EPS: raise QiskitError("A diagonal entry has not absolute value one.") # Create new gate. @@ -95,7 +96,7 @@ def _dec_diag(self): diag_phases[i // 2], rz_angle = _extract_rz(diag_phases[i], diag_phases[i + 1]) angles_rz.append(rz_angle) num_act_qubits = int(np.log2(n)) - contr_qubits = q[self.num_qubits - num_act_qubits + 1:self.num_qubits] + contr_qubits = q[self.num_qubits - num_act_qubits + 1 : self.num_qubits] target_qubit = q[self.num_qubits - num_act_qubits] circuit.ucrz(angles_rz, contr_qubits, target_qubit) n //= 2 @@ -139,15 +140,17 @@ def diagonal(self, diag, qubit): qubit = qubit[:] # Check if q has type "list" if not isinstance(qubit, list): - raise QiskitError("The qubits must be provided as a list " - "(also if there is only one qubit).") + raise QiskitError( + "The qubits must be provided as a list " "(also if there is only one qubit)." + ) # Check if diag has type "list" if not isinstance(diag, list): raise QiskitError("The diagonal entries are not provided in a list.") num_action_qubits = math.log2(len(diag)) if not len(qubit) == num_action_qubits: - raise QiskitError("The number of diagonal entries does not correspond to" - " the number of qubits.") + raise QiskitError( + "The number of diagonal entries does not correspond to" " the number of qubits." + ) return self.append(DiagonalGate(diag), qubit) diff --git a/qiskit/extensions/quantum_initializer/initializer.py b/qiskit/extensions/quantum_initializer/initializer.py index 690051c38dd0..c1c4b09784d0 100644 --- a/qiskit/extensions/quantum_initializer/initializer.py +++ b/qiskit/extensions/quantum_initializer/initializer.py @@ -65,8 +65,10 @@ def __init__(self, params, num_qubits=None): params = params.data if not isinstance(params, int) and num_qubits is not None: - raise QiskitError("The num_qubits parameter to Initialize should only be" - " used when params is an integer") + raise QiskitError( + "The num_qubits parameter to Initialize should only be" + " used when params is an integer" + ) self._from_label = False self._from_int = False @@ -86,8 +88,7 @@ def __init__(self, params, num_qubits=None): raise QiskitError("Desired statevector length not a positive power of 2.") # Check if probabilities (amplitudes squared) sum to 1 - if not math.isclose(sum(np.absolute(params) ** 2), 1.0, - abs_tol=_EPS): + if not math.isclose(sum(np.absolute(params) ** 2), 1.0, abs_tol=_EPS): raise QiskitError("Sum of amplitudes-squared does not equal one.") num_qubits = int(num_qubits) @@ -103,45 +104,46 @@ def _define(self): self.definition = self._define_synthesis() def _define_from_label(self): - q = QuantumRegister(self.num_qubits, 'q') - initialize_circuit = QuantumCircuit(q, name='init_def') + q = QuantumRegister(self.num_qubits, "q") + initialize_circuit = QuantumCircuit(q, name="init_def") for qubit, param in enumerate(reversed(self.params)): initialize_circuit.append(Reset(), [q[qubit]]) - if param == '1': + if param == "1": initialize_circuit.append(XGate(), [q[qubit]]) - elif param == '+': + elif param == "+": initialize_circuit.append(HGate(), [q[qubit]]) - elif param == '-': + elif param == "-": initialize_circuit.append(XGate(), [q[qubit]]) initialize_circuit.append(HGate(), [q[qubit]]) - elif param == 'r': # |+i> + elif param == "r": # |+i> initialize_circuit.append(HGate(), [q[qubit]]) initialize_circuit.append(SGate(), [q[qubit]]) - elif param == 'l': # |-i> + elif param == "l": # |-i> initialize_circuit.append(HGate(), [q[qubit]]) initialize_circuit.append(SdgGate(), [q[qubit]]) return initialize_circuit def _define_from_int(self): - q = QuantumRegister(self.num_qubits, 'q') - initialize_circuit = QuantumCircuit(q, name='init_def') + q = QuantumRegister(self.num_qubits, "q") + initialize_circuit = QuantumCircuit(q, name="init_def") # Convert to int since QuantumCircuit converted to complex # and make a bit string and reverse it - intstr = f'{int(np.real(self.params[0])):0{self.num_qubits}b}'[::-1] + intstr = f"{int(np.real(self.params[0])):0{self.num_qubits}b}"[::-1] # Raise if number of bits is greater than num_qubits if len(intstr) > self.num_qubits: - raise QiskitError("Initialize integer has %s bits, but this exceeds the" - " number of qubits in the circuit, %s." % - (len(intstr), self.num_qubits)) + raise QiskitError( + "Initialize integer has %s bits, but this exceeds the" + " number of qubits in the circuit, %s." % (len(intstr), self.num_qubits) + ) for qubit, bit in enumerate(intstr): initialize_circuit.append(Reset(), [q[qubit]]) - if bit == '1': + if bit == "1": initialize_circuit.append(XGate(), [q[qubit]]) return initialize_circuit @@ -163,8 +165,8 @@ def _define_synthesis(self): # the qubits are in the zero state) initialize_instr = disentangling_circuit.to_instruction().inverse() - q = QuantumRegister(self.num_qubits, 'q') - initialize_circuit = QuantumCircuit(q, name='init_def') + q = QuantumRegister(self.num_qubits, "q") + initialize_circuit = QuantumCircuit(q, name="init_def") for qubit in q: initialize_circuit.append(Reset(), [qubit]) initialize_circuit.append(initialize_instr, q[:]) @@ -178,7 +180,7 @@ def gates_to_uncompute(self): QuantumCircuit: circuit to take self.params vector to :math:`|{00\\ldots0}\\rangle` """ q = QuantumRegister(self.num_qubits) - circuit = QuantumCircuit(q, name='disentangler') + circuit = QuantumCircuit(q, name="disentangler") # kick start the peeling loop, and disentangle one-by-one from LSB to MSB remaining_param = self.params @@ -186,9 +188,7 @@ def gates_to_uncompute(self): for i in range(self.num_qubits): # work out which rotations must be done to disentangle the LSB # qubit (we peel away one qubit at a time) - (remaining_param, - thetas, - phis) = Initialize._rotations_to_disentangle(remaining_param) + (remaining_param, thetas, phis) = Initialize._rotations_to_disentangle(remaining_param) # perform the required rotations to decouple the LSB qubit (so that # it can be "factored" out, leaving a shorter amplitude vector to peel away) @@ -199,11 +199,11 @@ def gates_to_uncompute(self): if np.linalg.norm(phis) != 0: rz_mult = self._multiplex(RZGate, phis, last_cnot=add_last_cnot) - circuit.append(rz_mult.to_instruction(), q[i:self.num_qubits]) + circuit.append(rz_mult.to_instruction(), q[i : self.num_qubits]) if np.linalg.norm(thetas) != 0: ry_mult = self._multiplex(RYGate, thetas, last_cnot=add_last_cnot) - circuit.append(ry_mult.to_instruction().reverse_ops(), q[i:self.num_qubits]) + circuit.append(ry_mult.to_instruction().reverse_ops(), q[i : self.num_qubits]) circuit.global_phase -= np.angle(sum(remaining_param)) return circuit @@ -233,9 +233,9 @@ def _rotations_to_disentangle(local_param): # (imagine a qubit state signified by the amplitudes at index 2*i # and 2*(i+1), corresponding to the select qubits of the # multiplexor being in state |i>) - (remains, - add_theta, - add_phi) = Initialize._bloch_angles(local_param[2 * i: 2 * (i + 1)]) + (remains, add_theta, add_phi) = Initialize._bloch_angles( + local_param[2 * i : 2 * (i + 1)] + ) remaining_vector.append(remains) @@ -270,7 +270,7 @@ def _bloch_angles(pair_of_complex): final_t = a_arg + b_arg phi = b_arg - a_arg - return final_r * np.exp(1.J * final_t / 2), theta, phi + return final_r * np.exp(1.0j * final_t / 2), theta, phi def _multiplex(self, target_gate, list_of_angles, last_cnot=True): """ @@ -305,14 +305,13 @@ def _multiplex(self, target_gate, list_of_angles, last_cnot=True): # calc angle weights, assuming recursion (that is the lower-level # requested angles have been correctly implemented by recursion - angle_weight = np.kron([[0.5, 0.5], [0.5, -0.5]], - np.identity(2 ** (local_num_qubits - 2))) + angle_weight = np.kron([[0.5, 0.5], [0.5, -0.5]], np.identity(2 ** (local_num_qubits - 2))) # calc the combo angles list_of_angles = angle_weight.dot(np.array(list_of_angles)).tolist() # recursive step on half the angles fulfilling the above assumption - multiplex_1 = self._multiplex(target_gate, list_of_angles[0:(list_len // 2)], False) + multiplex_1 = self._multiplex(target_gate, list_of_angles[0 : (list_len // 2)], False) circuit.append(multiplex_1.to_instruction(), q[0:-1]) # attach CNOT as follows, thereby flipping the LSB qubit @@ -321,7 +320,7 @@ def _multiplex(self, target_gate, list_of_angles, last_cnot=True): # implement extra efficiency from the paper of cancelling adjacent # CNOTs (by leaving out last CNOT and reversing (NOT inverting) the # second lower-level multiplex) - multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2):], False) + multiplex_2 = self._multiplex(target_gate, list_of_angles[(list_len // 2) :], False) if list_len > 1: circuit.append(multiplex_2.to_instruction().reverse_ops(), q[0:-1]) else: @@ -337,9 +336,11 @@ def broadcast_arguments(self, qargs, cargs): flat_qargs = [qarg for sublist in qargs for qarg in sublist] if self.num_qubits != len(flat_qargs): - raise QiskitError("Initialize parameter vector has %d elements, therefore expects %s " - "qubits. However, %s were provided." % - (2**self.num_qubits, self.num_qubits, len(flat_qargs))) + raise QiskitError( + "Initialize parameter vector has %d elements, therefore expects %s " + "qubits. However, %s were provided." + % (2 ** self.num_qubits, self.num_qubits, len(flat_qargs)) + ) yield flat_qargs, [] def validate_parameter(self, parameter): @@ -347,10 +348,12 @@ def validate_parameter(self, parameter): # Initialize instruction parameter can be str if self._from_label: - if parameter in ['0', '1', '+', '-', 'l', 'r']: + if parameter in ["0", "1", "+", "-", "l", "r"]: return parameter - raise CircuitError("invalid param label {0} for instruction {1}. Label should be " - "0, 1, +, -, l, or r ".format(type(parameter), self.name)) + raise CircuitError( + "invalid param label {0} for instruction {1}. Label should be " + "0, 1, +, -, l, or r ".format(type(parameter), self.name) + ) # Initialize instruction parameter can be int, float, and complex. if isinstance(parameter, (int, float, complex)): @@ -358,8 +361,9 @@ def validate_parameter(self, parameter): elif isinstance(parameter, np.number): return complex(parameter.item()) else: - raise CircuitError("invalid param type {0} for instruction " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} for instruction " "{1}".format(type(parameter), self.name) + ) def initialize(self, params, qubits=None): diff --git a/qiskit/extensions/quantum_initializer/isometry.py b/qiskit/extensions/quantum_initializer/isometry.py index 0a24d43cc5bf..f67b93dc1a5f 100644 --- a/qiskit/extensions/quantum_initializer/isometry.py +++ b/qiskit/extensions/quantum_initializer/isometry.py @@ -79,17 +79,21 @@ def __init__(self, isometry, num_ancillas_zero, num_ancillas_dirty): n = np.log2(isometry.shape[0]) m = np.log2(isometry.shape[1]) if not n.is_integer() or n < 0: - raise QiskitError("The number of rows of the isometry is not a non negative" - " power of 2.") + raise QiskitError( + "The number of rows of the isometry is not a non negative" " power of 2." + ) if not m.is_integer() or m < 0: - raise QiskitError("The number of columns of the isometry is not a non negative" - " power of 2.") + raise QiskitError( + "The number of columns of the isometry is not a non negative" " power of 2." + ) if m > n: - raise QiskitError("The input matrix has more columns than rows and hence " - "it can't be an isometry.") + raise QiskitError( + "The input matrix has more columns than rows and hence " "it can't be an isometry." + ) if not is_isometry(isometry, _EPS): - raise QiskitError("The input matrix has non orthonormal columns and hence " - "it is not an isometry.") + raise QiskitError( + "The input matrix has non orthonormal columns and hence " "it is not an isometry." + ) num_qubits = int(n) + num_ancillas_zero + num_ancillas_dirty @@ -112,8 +116,12 @@ def _gates_to_uncompute(self): """ q = QuantumRegister(self.num_qubits) circuit = QuantumCircuit(q) - (q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty) \ - = self._define_qubit_role(q) + ( + q_input, + q_ancillas_for_output, + q_ancillas_zero, + q_ancillas_dirty, + ) = self._define_qubit_role(q) # Copy the isometry (this is computationally expensive for large isometries but guarantees # to keep a copyof the input isometry) remaining_isometry = self.params[0].astype(complex) # note: "astype" does copy the isometry @@ -162,10 +170,14 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): if _k_s(k, s) == 0 and _b(k, s + 1) != 0 and np.abs(v[index2, k_prime]) > _EPS: # Find the MCG, decompose it and apply it to the remaining isometry gate = _reverse_qubit_state([v[index1, k_prime], v[index2, k_prime]], 0) - control_labels = [i for i, x in enumerate(_get_binary_rep_as_list(k, n)) - if x == 1 and i != target_label] - diagonal_mcg = \ - self._append_mcg_up_to_diagonal(circuit, q, gate, control_labels, target_label) + control_labels = [ + i + for i, x in enumerate(_get_binary_rep_as_list(k, n)) + if x == 1 and i != target_label + ] + diagonal_mcg = self._append_mcg_up_to_diagonal( + circuit, q, gate, control_labels, target_label + ) # apply the MCG to the remaining isometry _apply_multi_controlled_gate(v, control_labels, target_label, gate) # correct for the implementation "up to diagonal" @@ -179,16 +191,18 @@ def _disentangle(self, circuit, q, diag, remaining_isometry, column_index, s): single_qubit_gates = self._find_squs_for_disentangling(v, k, s) if not _ucg_is_identity_up_to_global_phase(single_qubit_gates): control_labels = list(range(target_label)) - diagonal_ucg = self._append_ucg_up_to_diagonal(circuit, q, single_qubit_gates, - control_labels, target_label) + diagonal_ucg = self._append_ucg_up_to_diagonal( + circuit, q, single_qubit_gates, control_labels, target_label + ) # merge the diagonal into the UCGate for efficient application of both together diagonal_ucg_inverse = np.conj(diagonal_ucg).tolist() single_qubit_gates = _merge_UCGate_and_diag(single_qubit_gates, diagonal_ucg_inverse) # apply the UCGate (with the merged diagonal gate) to the remaining isometry _apply_ucg(v, len(control_labels), single_qubit_gates) # update the diag according to the applied diagonal gate - _apply_diagonal_gate_to_diag(diag, control_labels + [target_label], - diagonal_ucg_inverse, n) + _apply_diagonal_gate_to_diag( + diag, control_labels + [target_label], diagonal_ucg_inverse, n + ) # # correct for the implementation "up to diagonal" # diag_inv = np.conj(diag).tolist() # _apply_diagonal_gate(v, control_labels + [target_label], diag_inv) @@ -204,15 +218,26 @@ def _find_squs_for_disentangling(self, v, k, s): else: i_start = _a(k, s + 1) + 1 id_list = [np.eye(2, 2) for _ in range(i_start)] - squs = [_reverse_qubit_state([v[2 * i * 2 ** s + _b(k, s), k_prime], - v[(2 * i + 1) * 2 ** s + _b(k, s), k_prime]], _k_s(k, s)) - for i in range(i_start, 2 ** (n - s - 1))] + squs = [ + _reverse_qubit_state( + [ + v[2 * i * 2 ** s + _b(k, s), k_prime], + v[(2 * i + 1) * 2 ** s + _b(k, s), k_prime], + ], + _k_s(k, s), + ) + for i in range(i_start, 2 ** (n - s - 1)) + ] return id_list + squs # Append a UCGate up to diagonal to the circuit circ. def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels, target_label): - (q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty) = \ - self._define_qubit_role(q) + ( + q_input, + q_ancillas_for_output, + q_ancillas_zero, + q_ancillas_dirty, + ) = self._define_qubit_role(q) n = int(np.log2(self.params[0].shape[0])) qubits = q_input + q_ancillas_for_output # Note that we have to reverse the control labels, since controls are provided by @@ -229,8 +254,12 @@ def _append_ucg_up_to_diagonal(self, circ, q, single_qubit_gates, control_labels # below). But for simplicity, the current code version ignores this future optimization # possibility. def _append_mcg_up_to_diagonal(self, circ, q, gate, control_labels, target_label): - (q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty) = \ - self._define_qubit_role(q) + ( + q_input, + q_ancillas_for_output, + q_ancillas_zero, + q_ancillas_dirty, + ) = self._define_qubit_role(q) n = int(np.log2(self.params[0].shape[0])) qubits = q_input + q_ancillas_for_output control_qubits = _reverse_qubit_oder(_get_qubits_by_label(control_labels, qubits, n)) @@ -238,12 +267,16 @@ def _append_mcg_up_to_diagonal(self, circ, q, gate, control_labels, target_label # The qubits on which we neither act nor control on with the MCG, can be used # as dirty ancillas ancilla_dirty_labels = [i for i in range(n) if i not in control_labels + [target_label]] - ancillas_dirty = _reverse_qubit_oder(_get_qubits_by_label(ancilla_dirty_labels, - qubits, n)) + q_ancillas_dirty - mcg_up_to_diag = \ - MCGupDiag(gate, len(control_qubits), len(q_ancillas_zero), len(ancillas_dirty)) - circ.append(mcg_up_to_diag, [target_qubit] + control_qubits + q_ancillas_zero - + ancillas_dirty) + ancillas_dirty = ( + _reverse_qubit_oder(_get_qubits_by_label(ancilla_dirty_labels, qubits, n)) + + q_ancillas_dirty + ) + mcg_up_to_diag = MCGupDiag( + gate, len(control_qubits), len(q_ancillas_zero), len(ancillas_dirty) + ) + circ.append( + mcg_up_to_diag, [target_qubit] + control_qubits + q_ancillas_zero + ancillas_dirty + ) return mcg_up_to_diag._get_diagonal() def _define_qubit_role(self, q): @@ -252,8 +285,8 @@ def _define_qubit_role(self, q): # Define the role of the qubits q_input = q[:m] q_ancillas_for_output = q[m:n] - q_ancillas_zero = q[n:n + self.num_ancillas_zero] - q_ancillas_dirty = q[n + self.num_ancillas_zero:] + q_ancillas_zero = q[n : n + self.num_ancillas_zero] + q_ancillas_dirty = q[n + self.num_ancillas_zero :] return q_input, q_ancillas_for_output, q_ancillas_zero, q_ancillas_dirty def validate_parameter(self, parameter): @@ -261,8 +294,9 @@ def validate_parameter(self, parameter): if isinstance(parameter, np.ndarray): return parameter else: - raise CircuitError("invalid param type {0} for gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} for gate " "{1}".format(type(parameter), self.name) + ) def inverse(self): """Return the adjoint of the unitary.""" @@ -317,10 +351,9 @@ def _apply_ucg(m, k, single_qubit_gates): i = (j // spacing) * spacing + j gate_index = i // (2 ** (num_qubits - k)) for col in range(num_col): - m[np.array([i, i + spacing]), np.array([col, col])] = \ - np.ndarray.flatten( - single_qubit_gates[gate_index].dot(np.array([[m[i, col]], - [m[i + spacing, col]]]))).tolist() + m[np.array([i, i + spacing]), np.array([col, col])] = np.ndarray.flatten( + single_qubit_gates[gate_index].dot(np.array([[m[i, col]], [m[i + spacing, col]]])) + ).tolist() return m @@ -358,7 +391,7 @@ def _apply_diagonal_gate_to_diag(m_diagonal, action_qubit_labels, diag, num_qubi if not m_diagonal: return m_diagonal basis_states = list(itertools.product([0, 1], repeat=num_qubits)) - for state in basis_states[:len(m_diagonal)]: + for state in basis_states[: len(m_diagonal)]: state_on_action_qubits = [state[i] for i in action_qubit_labels] diag_index = _bin_to_int(state_on_action_qubits) i = _bin_to_int(state) @@ -383,8 +416,9 @@ def _apply_multi_controlled_gate(m, control_labels, target_label, gate): for state_free in basis_states_free: (e1, e2) = _construct_basis_states(state_free, control_labels, target_label) for i in range(num_cols): - m[np.array([e1, e2]), np.array([i, i])] = \ - np.ndarray.flatten(gate.dot(np.array([[m[e1, i]], [m[e2, i]]]))).tolist() + m[np.array([e1, e2]), np.array([i, i])] = np.ndarray.flatten( + gate.dot(np.array([[m[e1, i]], [m[e2, i]]])) + ).tolist() return m @@ -421,6 +455,7 @@ def _construct_basis_states(state_free, control_labels, target_label): # Remark: We labeled the qubits with decreasing significance. So we have to transform the labels to # be compatible with the standard convention of Qiskit. + def _get_qubits_by_label(labels, qubits, num_qubits): return [qubits[num_qubits - label - 1] for label in labels] @@ -454,8 +489,7 @@ def _get_binary_rep_as_list(n, num_digits): def _merge_UCGate_and_diag(single_qubit_gates, diag): for (i, gate) in enumerate(single_qubit_gates): - single_qubit_gates[i] = \ - np.array([[diag[2 * i], 0.], [0., diag[2 * i + 1]]]).dot(gate) + single_qubit_gates[i] = np.array([[diag[2 * i], 0.0], [0.0, diag[2 * i + 1]]]).dot(gate) return single_qubit_gates @@ -477,6 +511,7 @@ def _b(k, s): # given a binary representation of k with binary digits [k_{n-1},..,k_1,k_0], # the method k_s(k, s) returns k_s + def _k_s(k, s): if k == 0: return 0 @@ -490,7 +525,7 @@ def _k_s(k, s): def _ucg_is_identity_up_to_global_phase(single_qubit_gates): if not np.abs(single_qubit_gates[0][0, 0]) < _EPS: - global_phase = 1. / (single_qubit_gates[0][0, 0]) + global_phase = 1.0 / (single_qubit_gates[0][0, 0]) else: return False for gate in single_qubit_gates: @@ -501,7 +536,7 @@ def _ucg_is_identity_up_to_global_phase(single_qubit_gates): def _diag_is_identity_up_to_global_phase(diag): if not np.abs(diag[0]) < _EPS: - global_phase = 1. / (diag[0]) + global_phase = 1.0 / (diag[0]) else: return False for d in diag: @@ -510,8 +545,9 @@ def _diag_is_identity_up_to_global_phase(diag): return True -def iso(self, isometry, q_input, q_ancillas_for_output, q_ancillas_zero=None, - q_ancillas_dirty=None): +def iso( + self, isometry, q_input, q_ancillas_for_output, q_ancillas_zero=None, q_ancillas_dirty=None +): """ Attach an arbitrary isometry from m to n qubits to a circuit. In particular, this allows to attach arbitrary unitaries on n qubits (m=n) or to prepare any state @@ -558,8 +594,10 @@ def iso(self, isometry, q_input, q_ancillas_for_output, q_ancillas_zero=None, if isinstance(q_ancillas_dirty, QuantumRegister): q_ancillas_dirty = q_ancillas_dirty[:] - return self.append(Isometry(isometry, len(q_ancillas_zero), len(q_ancillas_dirty)), - q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty) + return self.append( + Isometry(isometry, len(q_ancillas_zero), len(q_ancillas_dirty)), + q_input + q_ancillas_for_output + q_ancillas_zero + q_ancillas_dirty, + ) # support both QuantumCircuit.iso and QuantumCircuit.isometry diff --git a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py index 4db27c589675..739625180c58 100644 --- a/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py +++ b/qiskit/extensions/quantum_initializer/mcg_up_to_diagonal.py @@ -41,14 +41,14 @@ class MCGupDiag(Gate): def __init__(self, gate, num_controls, num_ancillas_zero, num_ancillas_dirty): """Initialize a multi controlled gate. - Args: - gate (ndarray): 2*2 unitary (given as a (complex) ndarray) - num_controls (int): number of control qubits - num_ancillas_zero (int): number of ancilla qubits that start in the state zero - num_ancillas_dirty (int): number of ancilla qubits that are allowed to start in an - arbitrary state - Raises: - QiskitError: if the input format is wrong; if the array gate is not unitary + Args: + gate (ndarray): 2*2 unitary (given as a (complex) ndarray) + num_controls (int): number of control qubits + num_ancillas_zero (int): number of ancilla qubits that start in the state zero + num_ancillas_dirty (int): number of ancilla qubits that are allowed to start in an + arbitrary state + Raises: + QiskitError: if the input format is wrong; if the array gate is not unitary """ self.num_controls = num_controls @@ -77,13 +77,14 @@ def inverse(self): Note that the resulting Gate object has an empty ``params`` property. """ - inverse_gate = Gate(name=self.name + '_dg', - num_qubits=self.num_qubits, - params=[]) # removing the params because arrays are deprecated + inverse_gate = Gate( + name=self.name + "_dg", num_qubits=self.num_qubits, params=[] + ) # removing the params because arrays are deprecated inverse_gate.definition = QuantumCircuit(*self.definition.qregs) - inverse_gate.definition._data = [(inst.inverse(), qargs, []) - for inst, qargs, _ in reversed(self._definition)] + inverse_gate.definition._data = [ + (inst.inverse(), qargs, []) for inst, qargs, _ in reversed(self._definition) + ] return inverse_gate @@ -124,9 +125,9 @@ def _dec_mcg_up_diag(self): def _define_qubit_role(self, q): # Define the role of the qubits q_target = q[0] - q_controls = q[1:self.num_controls + 1] - q_ancillas_zero = q[self.num_controls + 1:self.num_controls + 1 + self.num_ancillas_zero] - q_ancillas_dirty = q[self.num_controls + 1 + self.num_ancillas_zero:] + q_controls = q[1 : self.num_controls + 1] + q_ancillas_zero = q[self.num_controls + 1 : self.num_controls + 1 + self.num_ancillas_zero] + q_ancillas_dirty = q[self.num_controls + 1 + self.num_ancillas_zero :] return q_target, q_controls, q_ancillas_zero, q_ancillas_dirty def validate_parameter(self, parameter): @@ -134,5 +135,6 @@ def validate_parameter(self, parameter): if isinstance(parameter, np.ndarray): return parameter else: - raise CircuitError("invalid param type {0} in gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} in gate " "{1}".format(type(parameter), self.name) + ) diff --git a/qiskit/extensions/quantum_initializer/squ.py b/qiskit/extensions/quantum_initializer/squ.py index eada89a0300d..0136898009f3 100644 --- a/qiskit/extensions/quantum_initializer/squ.py +++ b/qiskit/extensions/quantum_initializer/squ.py @@ -43,10 +43,10 @@ class SingleQubitUnitary(Gate): """ # pylint: disable=unused-argument - @deprecate_arguments({'u': 'unitary_matrix'}) - def __init__(self, unitary_matrix, mode='ZYZ', up_to_diagonal=False, u=None): + @deprecate_arguments({"u": "unitary_matrix"}) + def __init__(self, unitary_matrix, mode="ZYZ", up_to_diagonal=False, u=None): """Create a new single qubit gate based on the unitary ``u``.""" - if mode not in ['ZYZ']: + if mode not in ["ZYZ"]: raise QiskitError("The decomposition mode is not known.") # Check if the matrix u has the right dimensions and if it is a unitary if unitary_matrix.shape != (2, 2): @@ -66,13 +66,14 @@ def inverse(self): Note that the resulting gate has an empty ``params`` property. """ - inverse_gate = Gate(name=self.name + '_dg', - num_qubits=self.num_qubits, - params=[]) # removing the params because arrays are deprecated + inverse_gate = Gate( + name=self.name + "_dg", num_qubits=self.num_qubits, params=[] + ) # removing the params because arrays are deprecated inverse_gate.definition = QuantumCircuit(*self.definition.qregs) - inverse_gate.definition._data = [(inst.inverse(), qargs, []) - for inst, qargs, _ in reversed(self._definition)] + inverse_gate.definition._data = [ + (inst.inverse(), qargs, []) for inst, qargs, _ in reversed(self._definition) + ] return inverse_gate @@ -89,10 +90,10 @@ def diag(self): def _define(self): """Define the gate using the decomposition.""" - if self.mode == 'ZYZ': + if self.mode == "ZYZ": circuit, diag = self._zyz_circuit() else: - raise QiskitError('The decomposition mode is not known.') + raise QiskitError("The decomposition mode is not known.") self._diag = diag @@ -103,7 +104,7 @@ def _zyz_circuit(self): q = QuantumRegister(self.num_qubits) qc = QuantumCircuit(q, name=self.name) - diag = [1., 1.] + diag = [1.0, 1.0] alpha, beta, gamma, _ = self._zyz_dec() if abs(alpha) > _EPS: @@ -112,7 +113,7 @@ def _zyz_circuit(self): qc.ry(beta, q[0]) if abs(gamma) > _EPS: if self.up_to_diagonal: - diag = [np.exp(-1j * gamma / 2.), np.exp(1j * gamma / 2.)] + diag = [np.exp(-1j * gamma / 2.0), np.exp(1j * gamma / 2.0)] else: qc.rz(gamma, q[0]) @@ -133,13 +134,13 @@ def _zyz_dec(self): # Note that u10 can't be zero, since u is unitary (and u00 == 0) gamma = cmath.phase(-u01 / u10) delta = cmath.phase(u01 * np.exp(-1j * gamma / 2)) - return 0., -np.pi, -gamma, delta + return 0.0, -np.pi, -gamma, delta # Handle special case if the entry (0,1) of the unitary is equal to zero if np.abs(u01) < _EPS: # Note that u11 can't be zero, since u is unitary (and u01 == 0) gamma = cmath.phase(u00 / u11) delta = cmath.phase(u00 * np.exp(-1j * gamma / 2)) - return 0., 0., -gamma, delta + return 0.0, 0.0, -gamma, delta beta = 2 * np.arccos(np.abs(u00)) if np.sin(beta / 2) - np.cos(beta / 2) > 0: gamma = cmath.phase(-u00 / u10) @@ -158,13 +159,14 @@ def validate_parameter(self, parameter): if isinstance(parameter, np.ndarray): return parameter else: - raise CircuitError("invalid param type {0} in gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} in gate " "{1}".format(type(parameter), self.name) + ) # pylint: disable=unused-argument, invalid-name, missing-type-doc, missing-param-doc -@deprecate_arguments({'u': 'unitary'}) -def squ(self, unitary_matrix, qubit, mode='ZYZ', up_to_diagonal=False, *, u=None): +@deprecate_arguments({"u": "unitary"}) +def squ(self, unitary_matrix, qubit, mode="ZYZ", up_to_diagonal=False, *, u=None): """Decompose an arbitrary 2*2 unitary into three rotation gates. Note that the decomposition is up to a global phase shift. @@ -193,8 +195,9 @@ def squ(self, unitary_matrix, qubit, mode='ZYZ', up_to_diagonal=False, *, u=None if len(qubit) == 1: qubit = qubit[0] else: - raise QiskitError("The target qubit is a QuantumRegister containing more than" - " one qubits.") + raise QiskitError( + "The target qubit is a QuantumRegister containing more than" " one qubits." + ) # Check if there is one target qubit provided if not isinstance(qubit, Qubit): raise QiskitError("The target qubit is not a single qubit from a QuantumRegister.") diff --git a/qiskit/extensions/quantum_initializer/uc.py b/qiskit/extensions/quantum_initializer/uc.py index 0fe57bfba2bf..2f6d1fa8febe 100644 --- a/qiskit/extensions/quantum_initializer/uc.py +++ b/qiskit/extensions/quantum_initializer/uc.py @@ -49,7 +49,7 @@ from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer _EPS = 1e-10 # global variable used to chop very small numbers to zero -_DECOMPOSER1Q = OneQubitEulerDecomposer('U3') +_DECOMPOSER1Q = OneQubitEulerDecomposer("U3") class UCGate(Gate): @@ -84,8 +84,9 @@ def __init__(self, gate_list, up_to_diagonal=False): # Check if number of gates in gate_list is a positive power of two num_contr = math.log2(len(gate_list)) if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a " - "non-negative power of 2.") + raise QiskitError( + "The number of controlled single-qubit gates is not a " "non-negative power of 2." + ) # Check if the single-qubit gates are unitaries for gate in gate_list: @@ -102,13 +103,14 @@ def inverse(self): This does not re-compute the decomposition for the multiplexer with the inverse of the gates but simply inverts the existing decomposition. """ - inverse_gate = Gate(name=self.name + '_dg', - num_qubits=self.num_qubits, - params=[]) # remove parameters since array is deprecated as parameter + inverse_gate = Gate( + name=self.name + "_dg", num_qubits=self.num_qubits, params=[] + ) # remove parameters since array is deprecated as parameter inverse_gate.definition = QuantumCircuit(*self.definition.qregs) - inverse_gate.definition._data = [(inst.inverse(), qargs, []) - for inst, qargs, _ in reversed(self._definition)] + inverse_gate.definition._data = [ + (inst.inverse(), qargs, []) for inst, qargs, _ in reversed(self._definition) + ] return inverse_gate @@ -152,14 +154,18 @@ def _dec_ucg(self): elif i == len(single_qubit_gates) - 1: squ = gate.dot(UCGate._rz(np.pi / 2)).dot(HGate().to_matrix()) else: - squ = HGate().to_matrix().dot(gate.dot(UCGate._rz(np.pi / 2))).dot( - HGate().to_matrix()) + squ = ( + HGate() + .to_matrix() + .dot(gate.dot(UCGate._rz(np.pi / 2))) + .dot(HGate().to_matrix()) + ) # Add single-qubit gate circuit.squ(squ, q_target) # The number of the control qubit is given by the number of zeros at the end # of the binary representation of (i+1) binary_rep = np.binary_repr(i + 1) - num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip('0')) + num_trailing_zeros = len(binary_rep) - len(binary_rep.rstrip("0")) q_contr_index = num_trailing_zeros # Add C-NOT gate if not i == len(single_qubit_gates) - 1: @@ -211,22 +217,29 @@ def _dec_ucg_help(self): # merge the UC-Rz rotation with the following UCGate, # which hasn't been decomposed yet. k = shift + len_ucg + i - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(UCGate._ct(r)) * \ - UCGate._rz(np.pi / 2).item((0, 0)) + single_qubit_gates[k] = single_qubit_gates[k].dot( + UCGate._ct(r) + ) * UCGate._rz(np.pi / 2).item((0, 0)) k = k + len_ucg // 2 - single_qubit_gates[k] = \ - single_qubit_gates[k].dot(r) * UCGate._rz(np.pi / 2).item((1, 1)) + single_qubit_gates[k] = single_qubit_gates[k].dot(r) * UCGate._rz( + np.pi / 2 + ).item((1, 1)) else: # Absorb the Rz(pi/2) rotation on the control into the UC-Rz gate and merge # the trailing UC-Rz rotation into a diagonal gate at the end of the circuit for ucg_index_2 in range(num_ucgs): shift_2 = ucg_index_2 * len_ucg k = 2 * (i + shift_2) - diag[k] = diag[k] * UCGate._ct(r).item((0, 0)) * \ - UCGate._rz(np.pi / 2).item((0, 0)) - diag[k + 1] = diag[k + 1] * UCGate._ct(r).item((1, 1)) * UCGate._rz( - np.pi / 2).item((0, 0)) + diag[k] = ( + diag[k] + * UCGate._ct(r).item((0, 0)) + * UCGate._rz(np.pi / 2).item((0, 0)) + ) + diag[k + 1] = ( + diag[k + 1] + * UCGate._ct(r).item((1, 1)) + * UCGate._rz(np.pi / 2).item((0, 0)) + ) k = len_ucg + k diag[k] *= r.item((0, 0)) * UCGate._rz(np.pi / 2).item((1, 1)) diag[k + 1] *= r.item((1, 1)) * UCGate._rz(np.pi / 2).item((1, 1)) @@ -273,8 +286,9 @@ def validate_parameter(self, parameter): if isinstance(parameter, np.ndarray): return parameter else: - raise CircuitError("invalid param type {0} in gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} in gate " "{1}".format(type(parameter), self.name) + ) def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): @@ -313,24 +327,29 @@ def uc(self, gate_list, q_controls, q_target, up_to_diagonal=False): if len(q_target) == 1: q_target = q_target[0] else: - raise QiskitError("The target qubit is a QuantumRegister containing more than" - " one qubit.") + raise QiskitError( + "The target qubit is a QuantumRegister containing more than" " one qubit." + ) # Check if q_controls has type "list" if not isinstance(q_controls, list): - raise QiskitError("The control qubits must be provided as a list" - " (also if there is only one control qubit).") + raise QiskitError( + "The control qubits must be provided as a list" + " (also if there is only one control qubit)." + ) # Check if gate_list has type "list" if not isinstance(gate_list, list): raise QiskitError("The single-qubit unitaries are not provided in a list.") # Check if number of gates in gate_list is a positive power of two num_contr = math.log2(len(gate_list)) if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled single-qubit gates is not a non negative" - " power of 2.") + raise QiskitError( + "The number of controlled single-qubit gates is not a non negative" " power of 2." + ) # Check if number of control qubits does correspond to the number of single-qubit rotations if num_contr != len(q_controls): - raise QiskitError("Number of controlled gates does not correspond to the number of" - " control qubits.") + raise QiskitError( + "Number of controlled gates does not correspond to the number of" " control qubits." + ) return self.append(UCGate(gate_list, up_to_diagonal), [q_target] + q_controls) diff --git a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py index af0896dcd4a0..87a1b1fd3642 100644 --- a/qiskit/extensions/quantum_initializer/uc_pauli_rot.py +++ b/qiskit/extensions/quantum_initializer/uc_pauli_rot.py @@ -49,24 +49,25 @@ def __init__(self, angle_list, rot_axis): self.rot_axes = rot_axis # Check if angle_list has type "list" if not isinstance(angle_list, list): - raise QiskitError('The angles are not provided in a list.') + raise QiskitError("The angles are not provided in a list.") # Check if the angles in angle_list are real numbers for angle in angle_list: try: float(angle) except TypeError as ex: raise QiskitError( - 'An angle cannot be converted to type float (real angles are expected).' + "An angle cannot be converted to type float (real angles are expected)." ) from ex num_contr = math.log2(len(angle_list)) if num_contr < 0 or not num_contr.is_integer(): raise QiskitError( - 'The number of controlled rotation gates is not a non-negative power of 2.') - if rot_axis not in ('X', 'Y', 'Z'): - raise QiskitError('Rotation axis is not supported.') + "The number of controlled rotation gates is not a non-negative power of 2." + ) + if rot_axis not in ("X", "Y", "Z"): + raise QiskitError("Rotation axis is not supported.") # Create new gate. num_qubits = int(num_contr) + 1 - super().__init__('ucr' + rot_axis.lower(), num_qubits, angle_list) + super().__init__("ucr" + rot_axis.lower(), num_qubits, angle_list) def _define(self): ucr_circuit = self._dec_ucrot() @@ -86,13 +87,13 @@ def _dec_ucrot(self): q_target = q[0] q_controls = q[1:] if not q_controls: # equivalent to: if len(q_controls) == 0 - if self.rot_axes == 'X': + if self.rot_axes == "X": if np.abs(self.params[0]) > _EPS: circuit.rx(self.params[0], q_target) - if self.rot_axes == 'Y': + if self.rot_axes == "Y": if np.abs(self.params[0]) > _EPS: circuit.ry(self.params[0], q_target) - if self.rot_axes == 'Z': + if self.rot_axes == "Z": if np.abs(self.params[0]) > _EPS: circuit.rz(self.params[0], q_target) else: @@ -102,13 +103,13 @@ def _dec_ucrot(self): UCPauliRotGate._dec_uc_rotations(angles, 0, len(angles), False) # Now, it is easy to place the C-NOT gates to get back the full decomposition. for (i, angle) in enumerate(angles): - if self.rot_axes == 'X': + if self.rot_axes == "X": if np.abs(angle) > _EPS: circuit.rx(angle, q_target) - if self.rot_axes == 'Y': + if self.rot_axes == "Y": if np.abs(angle) > _EPS: circuit.ry(angle, q_target) - if self.rot_axes == 'Z': + if self.rot_axes == "Z": if np.abs(angle) > _EPS: circuit.rz(angle, q_target) # Determine the index of the qubit we want to control the C-NOT gate. @@ -116,8 +117,7 @@ def _dec_ucrot(self): # to the number of trailing zeros in the binary representation of i+1 if not i == len(angles) - 1: binary_rep = np.binary_repr(i + 1) - q_contr_index = len(binary_rep) - \ - len(binary_rep.rstrip('0')) + q_contr_index = len(binary_rep) - len(binary_rep.rstrip("0")) else: # Handle special case: q_contr_index = len(q_controls) - 1 @@ -125,10 +125,10 @@ def _dec_ucrot(self): # C-NOT gates. They change the basis of the NOT operation, such that the # decomposition of for uniformly controlled X rotations works correctly by symmetry # with the decomposition of uniformly controlled Z or Y rotations - if self.rot_axes == 'X': + if self.rot_axes == "X": circuit.ry(np.pi / 2, q_target) circuit.cx(q_controls[q_contr_index], q_target) - if self.rot_axes == 'X': + if self.rot_axes == "X": circuit.ry(-np.pi / 2, q_target) return circuit @@ -144,20 +144,22 @@ def _dec_uc_rotations(angles, start_index, end_index, reversed_dec): interval_len_half = (end_index - start_index) // 2 for i in range(start_index, start_index + interval_len_half): if not reversed_dec: - angles[i], angles[i + interval_len_half] = \ - UCPauliRotGate._update_angles( - angles[i], angles[i + interval_len_half]) + angles[i], angles[i + interval_len_half] = UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half] + ) else: - angles[i + interval_len_half], angles[i] = \ - UCPauliRotGate._update_angles( - angles[i], angles[i + interval_len_half]) + angles[i + interval_len_half], angles[i] = UCPauliRotGate._update_angles( + angles[i], angles[i + interval_len_half] + ) if interval_len_half <= 1: return else: - UCPauliRotGate._dec_uc_rotations(angles, start_index, start_index + interval_len_half, - False) - UCPauliRotGate._dec_uc_rotations(angles, start_index + interval_len_half, end_index, - True) + UCPauliRotGate._dec_uc_rotations( + angles, start_index, start_index + interval_len_half, False + ) + UCPauliRotGate._dec_uc_rotations( + angles, start_index + interval_len_half, end_index, True + ) @staticmethod def _update_angles(angle1, angle2): diff --git a/qiskit/extensions/quantum_initializer/ucrx.py b/qiskit/extensions/quantum_initializer/ucrx.py index 90085498c031..796e51960c7b 100644 --- a/qiskit/extensions/quantum_initializer/ucrx.py +++ b/qiskit/extensions/quantum_initializer/ucrx.py @@ -75,19 +75,22 @@ def ucrx(self, angle_list, q_controls, q_target): if len(q_target) == 1: q_target = q_target[0] else: - raise QiskitError("The target qubit is a QuantumRegister containing more" - " than one qubits.") + raise QiskitError( + "The target qubit is a QuantumRegister containing more" " than one qubits." + ) # Check if q_controls has type "list" if not isinstance(angle_list, list): raise QiskitError("The angles must be provided as a list.") num_contr = math.log2(len(angle_list)) if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") + raise QiskitError( + "The number of controlled rotation gates is not a non-negative" " power of 2." + ) # Check if number of control qubits does correspond to the number of rotations if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number of" - " control-qubits.") + raise QiskitError( + "Number of controlled rotations does not correspond to the number of" " control-qubits." + ) return self.append(UCRXGate(angle_list), [q_target] + q_controls, []) diff --git a/qiskit/extensions/quantum_initializer/ucry.py b/qiskit/extensions/quantum_initializer/ucry.py index c67059a74870..d9b3984297dc 100644 --- a/qiskit/extensions/quantum_initializer/ucry.py +++ b/qiskit/extensions/quantum_initializer/ucry.py @@ -72,19 +72,22 @@ def ucry(self, angle_list, q_controls, q_target): if len(q_target) == 1: q_target = q_target[0] else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") + raise QiskitError( + "The target qubit is a QuantumRegister containing" " more than one qubits." + ) # Check if q_controls has type "list" if not isinstance(angle_list, list): raise QiskitError("The angles must be provided as a list.") num_contr = math.log2(len(angle_list)) if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not" - " a non-negative power of 2.") + raise QiskitError( + "The number of controlled rotation gates is not" " a non-negative power of 2." + ) # Check if number of control qubits does correspond to the number of rotations if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to" - " the number of control-qubits.") + raise QiskitError( + "Number of controlled rotations does not correspond to" " the number of control-qubits." + ) return self.append(UCRYGate(angle_list), [q_target] + q_controls, []) diff --git a/qiskit/extensions/quantum_initializer/ucrz.py b/qiskit/extensions/quantum_initializer/ucrz.py index 8e6a87898635..398def340757 100644 --- a/qiskit/extensions/quantum_initializer/ucrz.py +++ b/qiskit/extensions/quantum_initializer/ucrz.py @@ -73,19 +73,22 @@ def ucrz(self, angle_list, q_controls, q_target): if len(q_target) == 1: q_target = q_target[0] else: - raise QiskitError("The target qubit is a QuantumRegister containing" - " more than one qubits.") + raise QiskitError( + "The target qubit is a QuantumRegister containing" " more than one qubits." + ) # Check if q_controls has type "list" if not isinstance(angle_list, list): raise QiskitError("The angles must be provided as a list.") num_contr = math.log2(len(angle_list)) if num_contr < 0 or not num_contr.is_integer(): - raise QiskitError("The number of controlled rotation gates is not a non-negative" - " power of 2.") + raise QiskitError( + "The number of controlled rotation gates is not a non-negative" " power of 2." + ) # Check if number of control qubits does correspond to the number of rotations if num_contr != len(q_controls): - raise QiskitError("Number of controlled rotations does not correspond to the number" - " of control-qubits.") + raise QiskitError( + "Number of controlled rotations does not correspond to the number" " of control-qubits." + ) return self.append(UCRZGate(angle_list), [q_target] + q_controls, []) diff --git a/qiskit/extensions/simulator/snapshot.py b/qiskit/extensions/simulator/snapshot.py index 91edcde9e04a..a4e525d77daf 100644 --- a/qiskit/extensions/simulator/snapshot.py +++ b/qiskit/extensions/simulator/snapshot.py @@ -24,12 +24,7 @@ class Snapshot(Instruction): _directive = True - def __init__(self, - label, - snapshot_type='statevector', - num_qubits=0, - num_clbits=0, - params=None): + def __init__(self, label, snapshot_type="statevector", num_qubits=0, num_clbits=0, params=None): """Create new snapshot instruction. Args: @@ -43,12 +38,12 @@ def __init__(self, ExtensionError: if snapshot label is invalid. """ if not isinstance(label, str): - raise ExtensionError('Snapshot label must be a string.') + raise ExtensionError("Snapshot label must be a string.") self._label = label self._snapshot_type = snapshot_type if params is None: params = [] - super().__init__('snapshot', num_qubits, num_clbits, params) + super().__init__("snapshot", num_qubits, num_clbits, params) def assemble(self): """Assemble a QasmQobjInstruction""" @@ -59,8 +54,7 @@ def assemble(self): def inverse(self): """Special case. Return self.""" - return Snapshot(self.num_qubits, self.num_clbits, self.params[0], - self.params[1]) + return Snapshot(self.num_qubits, self.num_clbits, self.params[0], self.params[1]) @property def snapshot_type(self): @@ -85,17 +79,13 @@ def label(self, name): if isinstance(name, str): self._label = name else: - raise TypeError('label expects a string') + raise TypeError("label expects a string") def c_if(self, classical, val): - raise QiskitError('Snapshots are simulator directives and cannot be conditional.') + raise QiskitError("Snapshots are simulator directives and cannot be conditional.") -def snapshot(self, - label, - snapshot_type='statevector', - qubits=None, - params=None): +def snapshot(self, label, snapshot_type="statevector", qubits=None, params=None): """Take a statevector snapshot of the internal simulator representation. Works on all qubits, and prevents reordering (like barrier). @@ -123,7 +113,7 @@ def snapshot(self, for register in self.qregs: tuples.append(register) if not tuples: - raise ExtensionError('no qubits for snapshot') + raise ExtensionError("no qubits for snapshot") qubits = [] for tuple_element in tuples: if isinstance(tuple_element, QuantumRegister): @@ -132,11 +122,8 @@ def snapshot(self, else: qubits.append(tuple_element) return self.append( - Snapshot( - label, - snapshot_type=snapshot_type, - num_qubits=len(qubits), - params=params), qubits) + Snapshot(label, snapshot_type=snapshot_type, num_qubits=len(qubits), params=params), qubits + ) # Add to QuantumCircuit class diff --git a/qiskit/extensions/unitary.py b/qiskit/extensions/unitary.py index 31e163dd9d45..fa8463969093 100644 --- a/qiskit/extensions/unitary.py +++ b/qiskit/extensions/unitary.py @@ -30,7 +30,7 @@ from qiskit.quantum_info.synthesis.two_qubit_decompose import two_qubit_cnot_decompose from qiskit.extensions.exceptions import ExtensionError -_DECOMPOSER1Q = OneQubitEulerDecomposer('U3') +_DECOMPOSER1Q = OneQubitEulerDecomposer("U3") class UnitaryGate(Gate): @@ -46,11 +46,11 @@ def __init__(self, data, label=None): Raises: ExtensionError: if input data is not an N-qubit unitary operator. """ - if hasattr(data, 'to_matrix'): + if hasattr(data, "to_matrix"): # If input is Gate subclass or some other class object that has # a to_matrix method this will call that method. data = data.to_matrix() - elif hasattr(data, 'to_operator'): + elif hasattr(data, "to_operator"): # If input is a BaseOperator subclass this attempts to convert # the object to an Operator so that we can extract the underlying # numpy matrix from `Operator.data`. @@ -63,15 +63,14 @@ def __init__(self, data, label=None): # Check input is N-qubit matrix input_dim, output_dim = data.shape num_qubits = int(numpy.log2(input_dim)) - if input_dim != output_dim or 2**num_qubits != input_dim: - raise ExtensionError( - "Input matrix is not an N-qubit operator.") + if input_dim != output_dim or 2 ** num_qubits != input_dim: + raise ExtensionError("Input matrix is not an N-qubit operator.") self._qasm_name = None self._qasm_definition = None self._qasm_def_written = False # Store instruction params - super().__init__('unitary', num_qubits, [data], label=label) + super().__init__("unitary", num_qubits, [data], label=label) def __eq__(self, other): if not isinstance(other, UnitaryGate): @@ -139,16 +138,23 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): mat = self.to_matrix() cmat = _compute_control_matrix(mat, num_ctrl_qubits, ctrl_state=None) iso = isometry.Isometry(cmat, 0, 0) - cunitary = ControlledGate('c-unitary', num_qubits=self.num_qubits+num_ctrl_qubits, - params=[mat], label=label, num_ctrl_qubits=num_ctrl_qubits, - definition=iso.definition, ctrl_state=ctrl_state, - base_gate=self.copy()) + cunitary = ControlledGate( + "c-unitary", + num_qubits=self.num_qubits + num_ctrl_qubits, + params=[mat], + label=label, + num_ctrl_qubits=num_ctrl_qubits, + definition=iso.definition, + ctrl_state=ctrl_state, + base_gate=self.copy(), + ) from qiskit.quantum_info import Operator + # hack to correct global phase; should fix to prevent need for correction here - pmat = (Operator(iso.inverse()).data @ cmat) + pmat = Operator(iso.inverse()).data @ cmat diag = numpy.diag(pmat) if not numpy.allclose(diag, diag[0]): - raise ExtensionError('controlled unitary generation failed') + raise ExtensionError("controlled unitary generation failed") phase = numpy.angle(diag[0]) if phase: # need to apply to _definition since open controls creates temporary definition @@ -156,7 +162,7 @@ def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None): return cunitary def qasm(self): - """ The qasm for a custom unitary gate + """The qasm for a custom unitary gate This is achieved by adding a custom gate that corresponds to the definition of this gate. It gives the gate a random name if one hasn't been given to it. """ @@ -186,18 +192,25 @@ def qasm(self): # add regs from this gate to the overall set of params for reg in gate[1] + gate[2]: if reg not in reg_to_qasm: - reg_to_qasm[reg] = 'p' + str(current_reg) + reg_to_qasm[reg] = "p" + str(current_reg) current_reg += 1 - curr_gate = "\t%s %s;\n" % (gate[0].qasm(), - ",".join([reg_to_qasm[j] - for j in gate[1] + gate[2]])) + curr_gate = "\t%s %s;\n" % ( + gate[0].qasm(), + ",".join([reg_to_qasm[j] for j in gate[1] + gate[2]]), + ) gates_def += curr_gate # name of gate + params + {definition} - overall = "gate " + self._qasm_name + \ - " " + ",".join(reg_to_qasm.values()) + \ - " {\n" + gates_def + "}\n" + overall = ( + "gate " + + self._qasm_name + + " " + + ",".join(reg_to_qasm.values()) + + " {\n" + + gates_def + + "}\n" + ) self._qasm_def_written = True self._qasm_definition = overall @@ -209,8 +222,9 @@ def validate_parameter(self, parameter): if isinstance(parameter, numpy.ndarray): return parameter else: - raise CircuitError("invalid param type {0} in gate " - "{1}".format(type(parameter), self.name)) + raise CircuitError( + "invalid param type {0} in gate " "{1}".format(type(parameter), self.name) + ) def unitary(self, obj, qubits, label=None): diff --git a/qiskit/opflow/__init__.py b/qiskit/opflow/__init__.py index 014ef59cc9a0..f56d8e804372 100644 --- a/qiskit/opflow/__init__.py +++ b/qiskit/opflow/__init__.py @@ -144,52 +144,162 @@ # New Operators from .operator_base import OperatorBase -from .primitive_ops import (PrimitiveOp, PauliOp, MatrixOp, CircuitOp, PauliSumOp, - TaperedPauliSumOp, Z2Symmetries) -from .state_fns import (StateFn, DictStateFn, VectorStateFn, CVaRMeasurement, - CircuitStateFn, OperatorStateFn, SparseVectorStateFn) +from .primitive_ops import ( + PrimitiveOp, + PauliOp, + MatrixOp, + CircuitOp, + PauliSumOp, + TaperedPauliSumOp, + Z2Symmetries, +) +from .state_fns import ( + StateFn, + DictStateFn, + VectorStateFn, + CVaRMeasurement, + CircuitStateFn, + OperatorStateFn, + SparseVectorStateFn, +) from .list_ops import ListOp, SummedOp, ComposedOp, TensoredOp -from .converters import (ConverterBase, CircuitSampler, PauliBasisChange, - DictToCircuitSum, AbelianGrouper, TwoQubitReduction) -from .expectations import (ExpectationBase, ExpectationFactory, PauliExpectation, - MatrixExpectation, AerPauliExpectation, CVaRExpectation) -from .evolutions import (EvolutionBase, EvolutionFactory, EvolvedOp, PauliTrotterEvolution, - MatrixEvolution, TrotterizationBase, TrotterizationFactory, Trotter, - Suzuki, QDrift) +from .converters import ( + ConverterBase, + CircuitSampler, + PauliBasisChange, + DictToCircuitSum, + AbelianGrouper, + TwoQubitReduction, +) +from .expectations import ( + ExpectationBase, + ExpectationFactory, + PauliExpectation, + MatrixExpectation, + AerPauliExpectation, + CVaRExpectation, +) +from .evolutions import ( + EvolutionBase, + EvolutionFactory, + EvolvedOp, + PauliTrotterEvolution, + MatrixEvolution, + TrotterizationBase, + TrotterizationFactory, + Trotter, + Suzuki, + QDrift, +) from .utils import commutator, anti_commutator, double_commutator # Convenience immutable instances -from .operator_globals import (EVAL_SIG_DIGITS, - X, Y, Z, I, - CX, S, H, T, Swap, CZ, - Zero, One, Plus, Minus) +from .operator_globals import ( + EVAL_SIG_DIGITS, + X, + Y, + Z, + I, + CX, + S, + H, + T, + Swap, + CZ, + Zero, + One, + Plus, + Minus, +) + # Gradients -from .gradients import (DerivativeBase, GradientBase, Gradient, NaturalGradient, - HessianBase, Hessian, QFIBase, QFI, - CircuitGradient, CircuitQFI) +from .gradients import ( + DerivativeBase, + GradientBase, + Gradient, + NaturalGradient, + HessianBase, + Hessian, + QFIBase, + QFI, + CircuitGradient, + CircuitQFI, +) + # Exceptions from .exceptions import OpflowError __all__ = [ # Operators - 'OperatorBase', - 'PrimitiveOp', 'PauliOp', 'MatrixOp', 'CircuitOp', 'PauliSumOp', 'TaperedPauliSumOp', - 'StateFn', 'DictStateFn', 'VectorStateFn', 'CircuitStateFn', 'OperatorStateFn', - 'SparseVectorStateFn', 'CVaRMeasurement', - 'ListOp', 'SummedOp', 'ComposedOp', 'TensoredOp', + "OperatorBase", + "PrimitiveOp", + "PauliOp", + "MatrixOp", + "CircuitOp", + "PauliSumOp", + "TaperedPauliSumOp", + "StateFn", + "DictStateFn", + "VectorStateFn", + "CircuitStateFn", + "OperatorStateFn", + "SparseVectorStateFn", + "CVaRMeasurement", + "ListOp", + "SummedOp", + "ComposedOp", + "TensoredOp", # Converters - 'ConverterBase', 'CircuitSampler', 'AbelianGrouper', 'DictToCircuitSum', 'PauliBasisChange', - 'ExpectationBase', 'ExpectationFactory', 'PauliExpectation', 'MatrixExpectation', - 'AerPauliExpectation', 'CVaRExpectation', - 'EvolutionBase', 'EvolvedOp', 'EvolutionFactory', 'PauliTrotterEvolution', 'MatrixEvolution', - 'TrotterizationBase', 'TrotterizationFactory', 'Trotter', 'Suzuki', 'QDrift', - 'TwoQubitReduction', 'Z2Symmetries', + "ConverterBase", + "CircuitSampler", + "AbelianGrouper", + "DictToCircuitSum", + "PauliBasisChange", + "ExpectationBase", + "ExpectationFactory", + "PauliExpectation", + "MatrixExpectation", + "AerPauliExpectation", + "CVaRExpectation", + "EvolutionBase", + "EvolvedOp", + "EvolutionFactory", + "PauliTrotterEvolution", + "MatrixEvolution", + "TrotterizationBase", + "TrotterizationFactory", + "Trotter", + "Suzuki", + "QDrift", + "TwoQubitReduction", + "Z2Symmetries", # Convenience immutable instances - 'X', 'Y', 'Z', 'I', 'CX', 'S', 'H', 'T', 'Swap', 'CZ', 'Zero', 'One', 'Plus', 'Minus', + "X", + "Y", + "Z", + "I", + "CX", + "S", + "H", + "T", + "Swap", + "CZ", + "Zero", + "One", + "Plus", + "Minus", # Gradients - 'DerivativeBase', 'GradientBase', 'Gradient', 'NaturalGradient', - 'HessianBase', 'Hessian', 'QFIBase', 'QFI', - 'OpflowError', + "DerivativeBase", + "GradientBase", + "Gradient", + "NaturalGradient", + "HessianBase", + "Hessian", + "QFIBase", + "QFI", + "OpflowError", # utils - 'commutator', 'anti_commutator', 'double_commutator', + "commutator", + "anti_commutator", + "double_commutator", ] diff --git a/qiskit/opflow/converters/__init__.py b/qiskit/opflow/converters/__init__.py index 60f09df214b3..46a50d294f42 100644 --- a/qiskit/opflow/converters/__init__.py +++ b/qiskit/opflow/converters/__init__.py @@ -68,10 +68,11 @@ from .abelian_grouper import AbelianGrouper from .two_qubit_reduction import TwoQubitReduction -__all__ = ['ConverterBase', - 'CircuitSampler', - 'PauliBasisChange', - 'DictToCircuitSum', - 'AbelianGrouper', - 'TwoQubitReduction', - ] +__all__ = [ + "ConverterBase", + "CircuitSampler", + "PauliBasisChange", + "DictToCircuitSum", + "AbelianGrouper", + "TwoQubitReduction", +] diff --git a/qiskit/opflow/converters/circuit_sampler.py b/qiskit/opflow/converters/circuit_sampler.py index ef6aaaaa3671..5605e3e0b099 100644 --- a/qiskit/opflow/converters/circuit_sampler.py +++ b/qiskit/opflow/converters/circuit_sampler.py @@ -51,12 +51,14 @@ class CircuitSampler(ConverterBase): you are better off using a different CircuitSampler for each Operator to avoid cache thrashing. """ - def __init__(self, - backend: Union[Backend, BaseBackend, QuantumInstance], - statevector: Optional[bool] = None, - param_qobj: bool = False, - attach_results: bool = False, - caching: str = 'last') -> None: + def __init__( + self, + backend: Union[Backend, BaseBackend, QuantumInstance], + statevector: Optional[bool] = None, + param_qobj: bool = False, + attach_results: bool = False, + caching: str = "last", + ) -> None: """ Args: backend: The quantum backend or QuantumInstance to use to sample the circuits. @@ -74,10 +76,12 @@ def __init__(self, Raises: ValueError: Set statevector or param_qobj True when not supported by backend. """ - self._quantum_instance = backend if isinstance(backend, QuantumInstance) else\ - QuantumInstance(backend=backend) - self._statevector = statevector if statevector is not None \ - else self.quantum_instance.is_statevector + self._quantum_instance = ( + backend if isinstance(backend, QuantumInstance) else QuantumInstance(backend=backend) + ) + self._statevector = ( + statevector if statevector is not None else self.quantum_instance.is_statevector + ) self._param_qobj = param_qobj self._attach_results = attach_results @@ -95,23 +99,27 @@ def __init__(self, self._transpile_before_bind = True def _check_quantum_instance_and_modes_consistent(self) -> None: - """ Checks whether the statevector and param_qobj settings are compatible with the + """Checks whether the statevector and param_qobj settings are compatible with the backend Raises: ValueError: statevector or param_qobj are True when not supported by backend. """ if self._statevector and not is_statevector_backend(self.quantum_instance.backend): - raise ValueError('Statevector mode for circuit sampling requires statevector ' - 'backend, not {}.'.format(self.quantum_instance.backend)) + raise ValueError( + "Statevector mode for circuit sampling requires statevector " + "backend, not {}.".format(self.quantum_instance.backend) + ) if self._param_qobj and not is_aer_provider(self.quantum_instance.backend): - raise ValueError('Parameterized Qobj mode requires Aer ' - 'backend, not {}.'.format(self.quantum_instance.backend)) + raise ValueError( + "Parameterized Qobj mode requires Aer " + "backend, not {}.".format(self.quantum_instance.backend) + ) @property def quantum_instance(self) -> QuantumInstance: - """ Returns the quantum instance. + """Returns the quantum instance. Returns: The QuantumInstance used by the CircuitSampler @@ -119,9 +127,10 @@ def quantum_instance(self) -> QuantumInstance: return self._quantum_instance @quantum_instance.setter - def quantum_instance(self, quantum_instance: Union[QuantumInstance, - Backend, BaseBackend]) -> None: - """ Sets the QuantumInstance. + def quantum_instance( + self, quantum_instance: Union[QuantumInstance, Backend, BaseBackend] + ) -> None: + """Sets the QuantumInstance. Raises: ValueError: statevector or param_qobj are True when not supported by backend. @@ -132,11 +141,11 @@ def quantum_instance(self, quantum_instance: Union[QuantumInstance, self._check_quantum_instance_and_modes_consistent() # pylint: disable=arguments-differ - def convert(self, - operator: OperatorBase, - params: Optional[Dict[Parameter, - Union[float, List[float], List[List[float]]]]] = None - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[Dict[Parameter, Union[float, List[float], List[List[float]]]]] = None, + ) -> OperatorBase: r""" Converts the Operator to one in which the CircuitStateFns are replaced by DictStateFns or VectorStateFns. Extracts the CircuitStateFns out of the Operator, @@ -158,7 +167,7 @@ def convert(self, # op_id = id(operator) if op_id not in self._cached_ops.keys(): # delete cache if we only want to cache one operator - if self._caching == 'last': + if self._caching == "last": self.clear_cache() # convert to circuit and reduce @@ -170,8 +179,8 @@ def convert(self, self._extract_circuitstatefns(self._reduced_op_cache) if not self._circuit_ops_cache: raise OpflowError( - 'Circuits are empty. ' - 'Check that the operator is an instance of CircuitStateFn or its ListOp.' + "Circuits are empty. " + "Check that the operator is an instance of CircuitStateFn or its ListOp." ) self._transpiled_circ_cache = None self._transpile_before_bind = True @@ -204,15 +213,15 @@ def convert(self, # Don't pass circuits if we have in the cache, the sampling function knows to use the cache circs = list(self._circuit_ops_cache.values()) if not self._transpiled_circ_cache else None p_b = cast(List[Dict[Parameter, float]], param_bindings) - sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, - param_bindings=p_b) + sampled_statefn_dicts = self.sample_circuits(circuit_sfns=circs, param_bindings=p_b) def replace_circuits_with_dicts(operator, param_index=0): if isinstance(operator, CircuitStateFn): return sampled_statefn_dicts[id(operator)][param_index] elif isinstance(operator, ListOp): - return operator.traverse(partial(replace_circuits_with_dicts, - param_index=param_index)) + return operator.traverse( + partial(replace_circuits_with_dicts, param_index=param_index) + ) else: return operator @@ -227,8 +236,12 @@ def replace_circuits_with_dicts(operator, param_index=0): self._cached_ops[op_id] = op_cache if return_as_list: - return ListOp([replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) - for i in range(num_parameterizations)]) + return ListOp( + [ + replace_circuits_with_dicts(self._reduced_op_cache, param_index=i) + for i in range(num_parameterizations) + ] + ) else: return replace_circuits_with_dicts(self._reduced_op_cache, param_index=0) @@ -247,10 +260,11 @@ def _extract_circuitstatefns(self, operator: OperatorBase) -> None: for op in operator.oplist: self._extract_circuitstatefns(op) - def sample_circuits(self, - circuit_sfns: Optional[List[CircuitStateFn]] = None, - param_bindings: Optional[List[Dict[Parameter, float]]] = None - ) -> Dict[int, List[StateFn]]: + def sample_circuits( + self, + circuit_sfns: Optional[List[CircuitStateFn]] = None, + param_bindings: Optional[List[Dict[Parameter, float]]] = None, + ) -> Dict[int, List[StateFn]]: r""" Samples the CircuitStateFns and returns a dict associating their ``id()`` values to their replacement DictStateFn or VectorStateFn. If param_bindings is provided, @@ -270,7 +284,7 @@ def sample_circuits(self, OpflowError: if extracted circuits are empty. """ if not circuit_sfns and not self._transpiled_circ_cache: - raise OpflowError('CircuitStateFn is empty and there is no cache.') + raise OpflowError("CircuitStateFn is empty and there is no cache.") if circuit_sfns: self._transpiled_circ_templates = None @@ -282,9 +296,11 @@ def sample_circuits(self, try: self._transpiled_circ_cache = self.quantum_instance.transpile(circuits) except QiskitError: - logger.debug(r'CircuitSampler failed to transpile circuits with unbound ' - r'parameters. Attempting to transpile only when circuits are bound ' - r'now, but this can hurt performance due to repeated transpilation.') + logger.debug( + r"CircuitSampler failed to transpile circuits with unbound " + r"parameters. Attempting to transpile only when circuits are bound " + r"now, but this can hurt performance due to repeated transpilation." + ) self._transpile_before_bind = False self._transpiled_circ_cache = circuits else: @@ -295,19 +311,22 @@ def sample_circuits(self, start_time = time() ready_circs = self._prepare_parameterized_run_config(param_bindings) end_time = time() - logger.debug('Parameter conversion %.5f (ms)', (end_time - start_time) * 1000) + logger.debug("Parameter conversion %.5f (ms)", (end_time - start_time) * 1000) else: start_time = time() - ready_circs = [circ.assign_parameters(_filter_params(circ, binding)) - for circ in self._transpiled_circ_cache - for binding in param_bindings] + ready_circs = [ + circ.assign_parameters(_filter_params(circ, binding)) + for circ in self._transpiled_circ_cache + for binding in param_bindings + ] end_time = time() - logger.debug('Parameter binding %.5f (ms)', (end_time - start_time) * 1000) + logger.debug("Parameter binding %.5f (ms)", (end_time - start_time) * 1000) else: ready_circs = self._transpiled_circ_cache - results = self.quantum_instance.execute(ready_circs, - had_transpiled=self._transpile_before_bind) + results = self.quantum_instance.execute( + ready_circs, had_transpiled=self._transpile_before_bind + ) if param_bindings is not None and self._param_qobj: self._clean_parameterized_run_config() @@ -325,45 +344,53 @@ def sample_circuits(self, circ_index = (i * reps) + j circ_results = results.data(circ_index) - if 'expval_measurement' in circ_results.get('snapshots', {}).get( - 'expectation_value', {}): - snapshot_data = results.data(circ_index)['snapshots'] - avg = snapshot_data['expectation_value']['expval_measurement'][0]['value'] + if "expval_measurement" in circ_results.get("snapshots", {}).get( + "expectation_value", {} + ): + snapshot_data = results.data(circ_index)["snapshots"] + avg = snapshot_data["expectation_value"]["expval_measurement"][0]["value"] if isinstance(avg, (list, tuple)): # Aer versions before 0.4 use a list snapshot format # which must be converted to a complex value. avg = avg[0] + 1j * avg[1] # Will be replaced with just avg when eval is called later num_qubits = circuit_sfns[0].num_qubits - result_sfn = DictStateFn('0' * num_qubits, - is_measurement=op_c.is_measurement) * avg + result_sfn = ( + DictStateFn("0" * num_qubits, is_measurement=op_c.is_measurement) * avg + ) elif self._statevector: - result_sfn = StateFn(op_c.coeff * results.get_statevector(circ_index), - is_measurement=op_c.is_measurement) + result_sfn = StateFn( + op_c.coeff * results.get_statevector(circ_index), + is_measurement=op_c.is_measurement, + ) else: shots = self.quantum_instance._run_config.shots - result_sfn = StateFn({b: (v / shots) ** 0.5 * op_c.coeff - for (b, v) in results.get_counts(circ_index).items()}, - is_measurement=op_c.is_measurement) + result_sfn = StateFn( + { + b: (v / shots) ** 0.5 * op_c.coeff + for (b, v) in results.get_counts(circ_index).items() + }, + is_measurement=op_c.is_measurement, + ) if self._attach_results: result_sfn.execution_results = circ_results c_statefns.append(result_sfn) sampled_statefn_dicts[id(op_c)] = c_statefns return sampled_statefn_dicts - def _build_aer_params(self, - circuit: QuantumCircuit, - building_param_tables: Dict[Tuple[int, int], List[float]], - input_params: Dict[Parameter, float] - ) -> None: - + def _build_aer_params( + self, + circuit: QuantumCircuit, + building_param_tables: Dict[Tuple[int, int], List[float]], + input_params: Dict[Parameter, float], + ) -> None: def resolve_param(inst_param): if not isinstance(inst_param, ParameterExpression): return None param_mappings = {} for param in inst_param._parameter_symbols.keys(): if param not in input_params: - raise ValueError('unexpected parameter: {0}'.format(param)) + raise ValueError("unexpected parameter: {0}".format(param)) param_mappings[param] = input_params[param] return float(inst_param.bind(param_mappings)) @@ -381,13 +408,15 @@ def resolve_param(inst_param): param_index += 1 gate_index += 1 - def _prepare_parameterized_run_config(self, param_bindings: - List[Dict[Parameter, float]]) -> List[Any]: + def _prepare_parameterized_run_config( + self, param_bindings: List[Dict[Parameter, float]] + ) -> List[Any]: self.quantum_instance._run_config.parameterizations = [] - if self._transpiled_circ_templates is None \ - or len(self._transpiled_circ_templates) != len(self._transpiled_circ_cache): + if self._transpiled_circ_templates is None or len(self._transpiled_circ_templates) != len( + self._transpiled_circ_cache + ): # temporally resolve parameters of self._transpiled_circ_cache # They will be overridden in Aer from the next iterations @@ -404,8 +433,9 @@ def _prepare_parameterized_run_config(self, param_bindings: for gate_and_param_indices in building_param_tables: gate_index = gate_and_param_indices[0] param_index = gate_and_param_indices[1] - param_tables.append([ - [gate_index, param_index], building_param_tables[(gate_index, param_index)]]) + param_tables.append( + [[gate_index, param_index], building_param_tables[(gate_index, param_index)]] + ) self.quantum_instance._run_config.parameterizations.append(param_tables) return self._transpiled_circ_templates diff --git a/qiskit/opflow/converters/converter_base.py b/qiskit/opflow/converters/converter_base.py index 8e199442c781..043702a31d8e 100644 --- a/qiskit/opflow/converters/converter_base.py +++ b/qiskit/opflow/converters/converter_base.py @@ -27,11 +27,11 @@ class ConverterBase(ABC): converters, such as a ``MatrixExpectation`` or ``MatrixEvolution``, which convert ``PauliOps`` to ``MatrixOps`` internally, will require time or space exponential in the number of qubits unless a clever trick is known (such as the use of sparse - matrices). """ + matrices).""" @abstractmethod def convert(self, operator: OperatorBase) -> OperatorBase: - """ Accept the Operator and return the converted Operator + """Accept the Operator and return the converted Operator Args: operator: The Operator to convert. diff --git a/qiskit/opflow/converters/dict_to_circuit_sum.py b/qiskit/opflow/converters/dict_to_circuit_sum.py index b93d3050cdc6..0b34ddd734ec 100644 --- a/qiskit/opflow/converters/dict_to_circuit_sum.py +++ b/qiskit/opflow/converters/dict_to_circuit_sum.py @@ -28,10 +28,9 @@ class DictToCircuitSum(ConverterBase): or ``VectorStateFns``, rather than both. """ - def __init__(self, - traverse: bool = True, - convert_dicts: bool = True, - convert_vectors: bool = True) -> None: + def __init__( + self, traverse: bool = True, convert_dicts: bool = True, convert_vectors: bool = True + ) -> None: """ Args: traverse: Whether to recurse down into Operators with internal sub-operators for @@ -44,7 +43,7 @@ def __init__(self, self._convert_vectors = convert_vectors def convert(self, operator: OperatorBase) -> OperatorBase: - """ Convert the Operator to ``CircuitStateFns``, recursively if ``traverse`` is True. + """Convert the Operator to ``CircuitStateFns``, recursively if ``traverse`` is True. Args: operator: The Operator to convert @@ -57,7 +56,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: return CircuitStateFn.from_dict(operator.primitive) if isinstance(operator, VectorStateFn) and self._convert_vectors: return CircuitStateFn.from_vector(operator.to_matrix(massive=True)) - elif isinstance(operator, ListOp) and 'Dict' in operator.primitive_strings(): + elif isinstance(operator, ListOp) and "Dict" in operator.primitive_strings(): return operator.traverse(self.convert) else: return operator diff --git a/qiskit/opflow/converters/pauli_basis_change.py b/qiskit/opflow/converters/pauli_basis_change.py index c2bdcc74a746..6f0f256e3c25 100644 --- a/qiskit/opflow/converters/pauli_basis_change.py +++ b/qiskit/opflow/converters/pauli_basis_change.py @@ -55,10 +55,12 @@ class PauliBasisChange(ConverterBase): this method, such as the placement of the CNOT chains. """ - def __init__(self, - destination_basis: Optional[Union[Pauli, PauliOp]] = None, - traverse: bool = True, - replacement_fn: Optional[Callable] = None) -> None: + def __init__( + self, + destination_basis: Optional[Union[Pauli, PauliOp]] = None, + traverse: bool = True, + replacement_fn: Optional[Callable] = None, + ) -> None: """ Args: destination_basis: The Pauli into the basis of which the operators @@ -106,8 +108,9 @@ def destination(self, dest: Union[Pauli, PauliOp]) -> None: dest = PauliOp(dest) if not isinstance(dest, PauliOp): - raise TypeError('PauliBasisChange can only convert into Pauli bases, ' - 'not {}.'.format(type(dest))) + raise TypeError( + "PauliBasisChange can only convert into Pauli bases, " "not {}.".format(type(dest)) + ) self._destination = dest # TODO see whether we should make this performant by handling ListOps of Paulis later. @@ -163,7 +166,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: operator = OperatorStateFn( operator.primitive.to_pauli_op(), coeff=operator.coeff, - is_measurement=operator.is_measurement + is_measurement=operator.is_measurement, ) if isinstance(operator, PauliSumOp): @@ -172,7 +175,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: if isinstance(operator, (Pauli, PauliOp)): cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator) return self._replacement_fn(cob_instr_op, dest_pauli_op) - if isinstance(operator, StateFn) and 'Pauli' in operator.primitive_strings(): + if isinstance(operator, StateFn) and "Pauli" in operator.primitive_strings(): # If the StateFn/Meas only contains a Pauli, use it directly. if isinstance(operator.primitive, PauliOp): cob_instr_op, dest_pauli_op = self.get_cob_circuit(operator.primitive) @@ -185,18 +188,25 @@ def convert(self, operator: OperatorBase) -> OperatorBase: diag_ops: List[OperatorBase] = [ self.get_diagonal_pauli_op(op) for op in operator.primitive.oplist ] - dest_pauli_op = operator.primitive.__class__(diag_ops, - coeff=operator.coeff, abelian=True) + dest_pauli_op = operator.primitive.__class__( + diag_ops, coeff=operator.coeff, abelian=True + ) return self._replacement_fn(cob_instr_op, dest_pauli_op) else: - sf_list = [StateFn(op, is_measurement=operator.is_measurement) - for op in operator.primitive.oplist] - listop_of_statefns = operator.primitive.__class__(oplist=sf_list, - coeff=operator.coeff) + sf_list = [ + StateFn(op, is_measurement=operator.is_measurement) + for op in operator.primitive.oplist + ] + listop_of_statefns = operator.primitive.__class__( + oplist=sf_list, coeff=operator.coeff + ) return listop_of_statefns.traverse(self.convert) - elif isinstance(operator, ListOp) and self._traverse and \ - 'Pauli' in operator.primitive_strings(): + elif ( + isinstance(operator, ListOp) + and self._traverse + and "Pauli" in operator.primitive_strings() + ): # If ListOp is abelian we can find a single post-rotation circuit # for the whole set. For now, # assume operator can only be abelian if all elements are @@ -215,8 +225,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: @staticmethod def measurement_replacement_fn( - cob_instr_op: PrimitiveOp, - dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] + cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] ) -> OperatorBase: r""" A built-in convenience replacement function which produces measurements @@ -233,8 +242,9 @@ def measurement_replacement_fn( return ComposedOp([StateFn(dest_pauli_op, is_measurement=True), cob_instr_op]) @staticmethod - def statefn_replacement_fn(cob_instr_op: PrimitiveOp, - dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]) -> OperatorBase: + def statefn_replacement_fn( + cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] + ) -> OperatorBase: r""" A built-in convenience replacement function which produces state functions isomorphic to an ``OperatorStateFn`` state function holding the origin ``PauliOp``. @@ -250,8 +260,9 @@ def statefn_replacement_fn(cob_instr_op: PrimitiveOp, return ComposedOp([cob_instr_op.adjoint(), StateFn(dest_pauli_op)]) @staticmethod - def operator_replacement_fn(cob_instr_op: PrimitiveOp, - dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp]) -> OperatorBase: + def operator_replacement_fn( + cob_instr_op: PrimitiveOp, dest_pauli_op: Union[PauliOp, PauliSumOp, ListOp] + ) -> OperatorBase: r""" A built-in convenience replacement function which produces Operators isomorphic to the origin ``PauliOp``. @@ -285,7 +296,7 @@ def get_tpb_pauli(self, list_op: ListOp) -> Pauli: return Pauli((origin_z, origin_x)) def get_diagonal_pauli_op(self, pauli_op: PauliOp) -> PauliOp: - """ Get the diagonal ``PualiOp`` to which ``pauli_op`` could be rotated with only + """Get the diagonal ``PualiOp`` to which ``pauli_op`` could be rotated with only single-qubit operations. Args: @@ -295,8 +306,14 @@ def get_diagonal_pauli_op(self, pauli_op: PauliOp) -> PauliOp: The diagonal ``PauliOp``. """ return PauliOp( - Pauli((np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x), - [False] * pauli_op.num_qubits)), coeff=pauli_op.coeff) + Pauli( + ( + np.logical_or(pauli_op.primitive.z, pauli_op.primitive.x), + [False] * pauli_op.num_qubits, + ) + ), + coeff=pauli_op.coeff, + ) def get_diagonalizing_clifford(self, pauli: Union[Pauli, PauliOp]) -> OperatorBase: r""" @@ -328,9 +345,9 @@ def get_diagonalizing_clifford(self, pauli: Union[Pauli, PauliOp]) -> OperatorBa x_to_z_origin = tensorall([H if has_x else I for has_x in reversed(pauli.x)]) return x_to_z_origin.compose(y_to_x_origin) - def pad_paulis_to_equal_length(self, - pauli_op1: PauliOp, - pauli_op2: PauliOp) -> Tuple[PauliOp, PauliOp]: + def pad_paulis_to_equal_length( + self, pauli_op1: PauliOp, pauli_op2: PauliOp + ) -> Tuple[PauliOp, PauliOp]: r""" If ``pauli_op1`` and ``pauli_op2`` do not act over the same number of qubits, pad identities to the end of the shorter of the two so they are of equal length. Padding is @@ -351,18 +368,24 @@ def pad_paulis_to_equal_length(self, # Padding to the end of the Pauli, but remember that Paulis are in reverse endianness. if not len(pauli_1.z) == num_qubits: missing_qubits = num_qubits - len(pauli_1.z) - pauli_1 = Pauli((([False] * missing_qubits) + pauli_1.z.tolist(), - ([False] * missing_qubits) + pauli_1.x.tolist())) + pauli_1 = Pauli( + ( + ([False] * missing_qubits) + pauli_1.z.tolist(), + ([False] * missing_qubits) + pauli_1.x.tolist(), + ) + ) if not len(pauli_2.z) == num_qubits: missing_qubits = num_qubits - len(pauli_2.z) - pauli_2 = Pauli((([False] * missing_qubits) + pauli_2.z.tolist(), - ([False] * missing_qubits) + pauli_2.x.tolist())) + pauli_2 = Pauli( + ( + ([False] * missing_qubits) + pauli_2.z.tolist(), + ([False] * missing_qubits) + pauli_2.x.tolist(), + ) + ) return PauliOp(pauli_1, coeff=pauli_op1.coeff), PauliOp(pauli_2, coeff=pauli_op2.coeff) - def construct_cnot_chain(self, - diag_pauli_op1: PauliOp, - diag_pauli_op2: PauliOp) -> PrimitiveOp: + def construct_cnot_chain(self, diag_pauli_op1: PauliOp, diag_pauli_op2: PauliOp) -> PrimitiveOp: r""" Construct a ``CircuitOp`` (or ``PauliOp`` if equal to the identity) which takes the eigenvectors of ``diag_pauli_op1`` to the eigenvectors of ``diag_pauli_op2``, @@ -383,10 +406,12 @@ def construct_cnot_chain(self, # TODO be smarter about connectivity and actual distance between pauli and destination # TODO be smarter in general - pauli_1 = diag_pauli_op1.primitive if isinstance(diag_pauli_op1, PauliOp) \ - else diag_pauli_op1 - pauli_2 = diag_pauli_op2.primitive if isinstance(diag_pauli_op2, PauliOp) \ - else diag_pauli_op2 + pauli_1 = ( + diag_pauli_op1.primitive if isinstance(diag_pauli_op1, PauliOp) else diag_pauli_op1 + ) + pauli_2 = ( + diag_pauli_op2.primitive if isinstance(diag_pauli_op2, PauliOp) else diag_pauli_op2 + ) origin_sig_bits = np.logical_or(pauli_1.z, pauli_1.x) destination_sig_bits = np.logical_or(pauli_2.z, pauli_2.x) num_qubits = max(len(pauli_1.z), len(pauli_2.z)) @@ -400,11 +425,11 @@ def construct_cnot_chain(self, # I am deeply sorry for this code, but I don't know another way to do it. sig_in_origin_only_indices = np.extract( - np.logical_and(non_equal_sig_bits, origin_sig_bits), - np.arange(num_qubits)) + np.logical_and(non_equal_sig_bits, origin_sig_bits), np.arange(num_qubits) + ) sig_in_dest_only_indices = np.extract( - np.logical_and(non_equal_sig_bits, destination_sig_bits), - np.arange(num_qubits)) + np.logical_and(non_equal_sig_bits, destination_sig_bits), np.arange(num_qubits) + ) if len(sig_in_origin_only_indices) > 0 and len(sig_in_dest_only_indices) > 0: origin_anchor_bit = min(sig_in_origin_only_indices) @@ -484,7 +509,7 @@ def get_cob_circuit(self, origin: Union[Pauli, PauliOp]) -> Tuple[PrimitiveOp, P if not isinstance(origin, PauliOp): raise TypeError( - f'PauliBasisChange can only convert Pauli-based OpPrimitives, not {type(origin)}' + f"PauliBasisChange can only convert Pauli-based OpPrimitives, not {type(origin)}" ) # If no destination specified, assume nearest Pauli in {Z,I}^n basis, @@ -495,15 +520,14 @@ def get_cob_circuit(self, origin: Union[Pauli, PauliOp]) -> Tuple[PrimitiveOp, P origin, destination = self.pad_paulis_to_equal_length(origin, destination) origin_sig_bits = np.logical_or(origin.primitive.x, origin.primitive.z) - destination_sig_bits = \ - np.logical_or(destination.primitive.x, destination.primitive.z) + destination_sig_bits = np.logical_or(destination.primitive.x, destination.primitive.z) if not any(origin_sig_bits) or not any(destination_sig_bits): if not (any(origin_sig_bits) or any(destination_sig_bits)): # Both all Identity, just return Identities return I ^ origin.num_qubits, destination else: # One is Identity, one is not - raise ValueError('Cannot change to or from a fully Identity Pauli.') + raise ValueError("Cannot change to or from a fully Identity Pauli.") # Steps 1 and 2 cob_instruction = self.get_diagonalizing_clifford(origin) diff --git a/qiskit/opflow/evolutions/__init__.py b/qiskit/opflow/evolutions/__init__.py index 896f98c88d3a..0e4f9a19d347 100644 --- a/qiskit/opflow/evolutions/__init__.py +++ b/qiskit/opflow/evolutions/__init__.py @@ -86,13 +86,15 @@ # TODO evolve by density matrix (need to add iexp to operator_state_fn) # TODO linear combination evolution -__all__ = ['EvolutionBase', - 'EvolutionFactory', - 'EvolvedOp', - 'PauliTrotterEvolution', - 'MatrixEvolution', - 'TrotterizationBase', - 'TrotterizationFactory', - 'Trotter', - 'Suzuki', - 'QDrift'] +__all__ = [ + "EvolutionBase", + "EvolutionFactory", + "EvolvedOp", + "PauliTrotterEvolution", + "MatrixEvolution", + "TrotterizationBase", + "TrotterizationFactory", + "Trotter", + "Suzuki", + "QDrift", +] diff --git a/qiskit/opflow/evolutions/evolution_base.py b/qiskit/opflow/evolutions/evolution_base.py index 18993e91971f..7043beedb1c6 100644 --- a/qiskit/opflow/evolutions/evolution_base.py +++ b/qiskit/opflow/evolutions/evolution_base.py @@ -31,7 +31,7 @@ class EvolutionBase(ConverterBase, ABC): @abstractmethod def convert(self, operator: OperatorBase) -> OperatorBase: - """ Traverse the operator, replacing any ``EvolutionOps`` with their equivalent evolution + """Traverse the operator, replacing any ``EvolutionOps`` with their equivalent evolution ``CircuitOps``. Args: diff --git a/qiskit/opflow/evolutions/evolution_factory.py b/qiskit/opflow/evolutions/evolution_factory.py index da668105d9ec..6fda3d7cbffb 100644 --- a/qiskit/opflow/evolutions/evolution_factory.py +++ b/qiskit/opflow/evolutions/evolution_factory.py @@ -19,7 +19,7 @@ class EvolutionFactory: - """ A factory class for convenient automatic selection of an Evolution algorithm based on the + """A factory class for convenient automatic selection of an Evolution algorithm based on the Operator to be converted. """ @@ -41,12 +41,12 @@ def build(operator: OperatorBase = None) -> EvolutionBase: """ primitive_strings = operator.primitive_strings() - if 'Matrix' in primitive_strings: + if "Matrix" in primitive_strings: return MatrixEvolution() - elif 'Pauli' in primitive_strings or 'SparsePauliOp' in primitive_strings: + elif "Pauli" in primitive_strings or "SparsePauliOp" in primitive_strings: # TODO figure out what to do based on qubits and hamming weight. return PauliTrotterEvolution() else: - raise ValueError('Evolutions of mixed Operators not yet supported.') + raise ValueError("Evolutions of mixed Operators not yet supported.") diff --git a/qiskit/opflow/evolutions/evolved_op.py b/qiskit/opflow/evolutions/evolved_op.py index 0423eaa587e6..1f238c764288 100644 --- a/qiskit/opflow/evolutions/evolved_op.py +++ b/qiskit/opflow/evolutions/evolved_op.py @@ -39,9 +39,9 @@ class EvolvedOp(PrimitiveOp): but would have ended up copying and pasting a lot of code from PrimitiveOp.""" primitive: PrimitiveOp - def __init__(self, - primitive: OperatorBase, - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__( + self, primitive: OperatorBase, coeff: Union[complex, ParameterExpression] = 1.0 + ) -> None: """ Args: primitive: The operator being wrapped to signify evolution later. @@ -59,8 +59,9 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> Union["EvolvedOp", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over operators with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over operators with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) if isinstance(other, EvolvedOp) and self.primitive == other.primitive: return EvolvedOp(self.primitive, coeff=self.coeff + other.coeff) @@ -72,10 +73,7 @@ def add(self, other: OperatorBase) -> Union["EvolvedOp", SummedOp]: return SummedOp([self, other]) def adjoint(self) -> "EvolvedOp": - return EvolvedOp( - self.primitive.adjoint() * -1, - coeff=self.coeff.conjugate() - ) + return EvolvedOp(self.primitive.adjoint() * -1, coeff=self.coeff.conjugate()) def equals(self, other: OperatorBase) -> bool: if not isinstance(other, EvolvedOp) or not self.coeff == other.coeff: @@ -98,8 +96,9 @@ def _expand_dim(self, num_qubits: int) -> TensoredOp: def permute(self, permutation: List[int]) -> "EvolvedOp": return EvolvedOp(self.primitive.permute(permutation), coeff=self.coeff) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) if front: return other.compose(new_self) @@ -111,7 +110,7 @@ def compose(self, other: OperatorBase, def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return 'e^(-i*{})'.format(prim_str) + return "e^(-i*{})".format(prim_str) else: return "{} * e^(-i*{})".format(self.coeff, prim_str) @@ -142,23 +141,25 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: isinstance(self.primitive, ListOp) and self.primitive.__class__.__name__ == ListOp.__name__ ): - return np.array([ - op.exp_i().to_matrix(massive=massive) - * self.primitive.coeff - * self.coeff - for op in self.primitive.oplist - ], dtype=complex) - - prim_mat = -1.j * self.primitive.to_matrix() + return np.array( + [ + op.exp_i().to_matrix(massive=massive) * self.primitive.coeff * self.coeff + for op in self.primitive.oplist + ], + dtype=complex, + ) + + prim_mat = -1.0j * self.primitive.to_matrix() return scipy.linalg.expm(prim_mat) * self.coeff def to_matrix_op(self, massive: bool = False) -> Union[ListOp, MatrixOp]: - """ Returns a ``MatrixOp`` equivalent to this Operator. """ + """Returns a ``MatrixOp`` equivalent to this Operator.""" primitive = self.primitive if isinstance(primitive, ListOp) and primitive.__class__.__name__ == ListOp.__name__: return ListOp( [op.exp_i().to_matrix_op() for op in primitive.oplist], - coeff=primitive.coeff * self.coeff) + coeff=primitive.coeff * self.coeff, + ) prim_mat = EvolvedOp(primitive).to_matrix(massive=massive) return MatrixOp(prim_mat, coeff=self.coeff) diff --git a/qiskit/opflow/evolutions/matrix_evolution.py b/qiskit/opflow/evolutions/matrix_evolution.py index 9053ef753249..b26ecf773798 100644 --- a/qiskit/opflow/evolutions/matrix_evolution.py +++ b/qiskit/opflow/evolutions/matrix_evolution.py @@ -44,9 +44,11 @@ def convert(self, operator: OperatorBase) -> OperatorBase: The converted operator. """ if isinstance(operator, EvolvedOp): - if not {'Matrix'} == operator.primitive_strings(): - logger.warning('Evolved Hamiltonian is not composed of only MatrixOps, converting ' - 'to Matrix representation, which can be expensive.') + if not {"Matrix"} == operator.primitive_strings(): + logger.warning( + "Evolved Hamiltonian is not composed of only MatrixOps, converting " + "to Matrix representation, which can be expensive." + ) # Setting massive=False because this conversion is implicit. User can perform this # action on the Hamiltonian with massive=True explicitly if they so choose. # TODO explore performance to see whether we should avoid doing this repeatedly diff --git a/qiskit/opflow/evolutions/pauli_trotter_evolution.py b/qiskit/opflow/evolutions/pauli_trotter_evolution.py index 091a88c8a67a..50ea14bc4b30 100644 --- a/qiskit/opflow/evolutions/pauli_trotter_evolution.py +++ b/qiskit/opflow/evolutions/pauli_trotter_evolution.py @@ -29,6 +29,7 @@ from qiskit.opflow.primitive_ops.pauli_op import PauliOp from qiskit.opflow.primitive_ops.pauli_sum_op import PauliSumOp from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp + # TODO uncomment when we implement Abelian grouped evolution. # from qiskit.opflow.converters.abelian_grouper import AbelianGrouper @@ -76,12 +77,12 @@ def __init__( @property def trotter(self) -> TrotterizationBase: - """ TrotterizationBase used to evolve SummedOps. """ + """TrotterizationBase used to evolve SummedOps.""" return self._trotter @trotter.setter def trotter(self, trotter: TrotterizationBase) -> None: - """ Set TrotterizationBase used to evolve SummedOps. """ + """Set TrotterizationBase used to evolve SummedOps.""" self._trotter = trotter def convert(self, operator: OperatorBase) -> OperatorBase: @@ -180,5 +181,5 @@ def replacement_fn(cob_instr_op, dest_pauli_op): # TODO implement Abelian grouped evolution. def evolution_for_abelian_paulisum(self, op_sum: SummedOp) -> PrimitiveOp: - """ Evolution for abelian pauli sum """ + """Evolution for abelian pauli sum""" raise NotImplementedError diff --git a/qiskit/opflow/evolutions/trotterizations/__init__.py b/qiskit/opflow/evolutions/trotterizations/__init__.py index fa3f94e7f25d..f177f5ce36c6 100644 --- a/qiskit/opflow/evolutions/trotterizations/__init__.py +++ b/qiskit/opflow/evolutions/trotterizations/__init__.py @@ -21,8 +21,4 @@ from .suzuki import Suzuki from .qdrift import QDrift -__all__ = ['TrotterizationBase', - 'TrotterizationFactory', - 'Trotter', - 'Suzuki', - 'QDrift'] +__all__ = ["TrotterizationBase", "TrotterizationFactory", "Trotter", "Suzuki", "QDrift"] diff --git a/qiskit/opflow/evolutions/trotterizations/suzuki.py b/qiskit/opflow/evolutions/trotterizations/suzuki.py index 5c8d60577624..9ba2878d8e76 100644 --- a/qiskit/opflow/evolutions/trotterizations/suzuki.py +++ b/qiskit/opflow/evolutions/trotterizations/suzuki.py @@ -33,9 +33,8 @@ class Suzuki(TrotterizationBase): Detailed in https://arxiv.org/pdf/quant-ph/0508139.pdf. """ - def __init__(self, - reps: int = 1, - order: int = 2) -> None: + + def __init__(self, reps: int = 1, order: int = 2) -> None: """ Args: reps: The number of times to repeat the expansion circuit. @@ -47,17 +46,17 @@ def __init__(self, @property def order(self) -> int: - """ returns order """ + """returns order""" return self._order @order.setter def order(self, order: int) -> None: - """ sets order """ + """sets order""" self._order = order def convert(self, operator: OperatorBase) -> OperatorBase: if not isinstance(operator, (SummedOp, PauliSumOp)): - raise TypeError('Trotterization converters can only convert SummedOp or PauliSumOp.') + raise TypeError("Trotterization converters can only convert SummedOp or PauliSumOp.") if isinstance(operator.coeff, (float, ParameterExpression)): coeff = operator.coeff @@ -73,19 +72,19 @@ def convert(self, operator: OperatorBase) -> OperatorBase: if isinstance(operator, PauliSumOp): comp_list = self._recursive_expansion(operator, coeff, self.order, self.reps) if isinstance(operator, SummedOp): - comp_list = Suzuki._recursive_expansion( - operator.oplist, coeff, self.order, self.reps - ) + comp_list = Suzuki._recursive_expansion(operator.oplist, coeff, self.order, self.reps) single_rep = ComposedOp(cast(List[OperatorBase], comp_list)) full_evo = single_rep.power(self.reps) return full_evo.reduce() @staticmethod - def _recursive_expansion(op_list: Union[List[OperatorBase], PauliSumOp], - evo_time: Union[float, ParameterExpression], - expansion_order: int, - reps: int) -> List[PrimitiveOp]: + def _recursive_expansion( + op_list: Union[List[OperatorBase], PauliSumOp], + evo_time: Union[float, ParameterExpression], + expansion_order: int, + reps: int, + ) -> List[PrimitiveOp]: """ Compute the list of pauli terms for a single slice of the Suzuki expansion following the paper https://arxiv.org/pdf/quant-ph/0508139.pdf. @@ -104,13 +103,14 @@ def _recursive_expansion(op_list: Union[List[OperatorBase], PauliSumOp], # Base first-order Trotter case return [(op * (evo_time / reps)).exp_i() for op in op_list] # type: ignore if expansion_order == 2: - half = Suzuki._recursive_expansion(op_list, evo_time / 2, - expansion_order - 1, reps) + half = Suzuki._recursive_expansion(op_list, evo_time / 2, expansion_order - 1, reps) return list(reversed(half)) + half else: p_k = (4 - 4 ** (1 / (2 * expansion_order - 1))) ** -1 - side = 2 * Suzuki._recursive_expansion(op_list, evo_time - * p_k, expansion_order - 2, reps) - middle = Suzuki._recursive_expansion(op_list, evo_time * (1 - 4 * p_k), - expansion_order - 2, reps) + side = 2 * Suzuki._recursive_expansion( + op_list, evo_time * p_k, expansion_order - 2, reps + ) + middle = Suzuki._recursive_expansion( + op_list, evo_time * (1 - 4 * p_k), expansion_order - 2, reps + ) return side + middle + side diff --git a/qiskit/opflow/evolutions/trotterizations/trotter.py b/qiskit/opflow/evolutions/trotterizations/trotter.py index 84aba0870375..a6d8e68b8754 100644 --- a/qiskit/opflow/evolutions/trotterizations/trotter.py +++ b/qiskit/opflow/evolutions/trotterizations/trotter.py @@ -20,8 +20,8 @@ class Trotter(Suzuki): Simple Trotter expansion, composing the evolution circuits of each Operator in the sum together ``reps`` times and dividing the evolution time of each by ``reps``. """ - def __init__(self, - reps: int = 1) -> None: + + def __init__(self, reps: int = 1) -> None: r""" Args: reps: The number of times to repeat the Trotterization circuit. diff --git a/qiskit/opflow/evolutions/trotterizations/trotterization_base.py b/qiskit/opflow/evolutions/trotterizations/trotterization_base.py index d92fc7f96a3b..f76f78e10389 100644 --- a/qiskit/opflow/evolutions/trotterizations/trotterization_base.py +++ b/qiskit/opflow/evolutions/trotterizations/trotterization_base.py @@ -21,7 +21,7 @@ class TrotterizationBase(EvolutionBase): - """ A base for Trotterization methods, algorithms for approximating exponentiations of + """A base for Trotterization methods, algorithms for approximating exponentiations of operator sums by compositions of exponentiations. """ @@ -31,14 +31,14 @@ def __init__(self, reps: int = 1) -> None: @property def reps(self) -> int: - """ The number of repetitions to use in the Trotterization, improving the approximation + """The number of repetitions to use in the Trotterization, improving the approximation accuracy. """ return self._reps @reps.setter def reps(self, reps: int) -> None: - r""" Set the number of repetitions to use in the Trotterization. """ + r"""Set the number of repetitions to use in the Trotterization.""" self._reps = reps @abstractmethod diff --git a/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py b/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py index 18d16a4b79be..6bb99053248e 100644 --- a/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py +++ b/qiskit/opflow/evolutions/trotterizations/trotterization_factory.py @@ -18,13 +18,12 @@ from qiskit.opflow.evolutions.trotterizations.trotterization_base import TrotterizationBase -class TrotterizationFactory(): - """ A factory for conveniently creating TrotterizationBase instances. """ +class TrotterizationFactory: + """A factory for conveniently creating TrotterizationBase instances.""" @staticmethod - def build(mode: str = 'trotter', - reps: int = 1) -> TrotterizationBase: - """ A factory for conveniently creating TrotterizationBase instances. + def build(mode: str = "trotter", reps: int = 1) -> TrotterizationBase: + """A factory for conveniently creating TrotterizationBase instances. Args: mode: One of 'trotter', 'suzuki', 'qdrift' @@ -36,13 +35,13 @@ def build(mode: str = 'trotter', Raises: ValueError: A string not in ['trotter', 'suzuki', 'qdrift'] is given for mode. """ - if mode == 'trotter': + if mode == "trotter": return Trotter(reps=reps) - elif mode == 'suzuki': + elif mode == "suzuki": return Suzuki(reps=reps) - elif mode == 'qdrift': + elif mode == "qdrift": return QDrift(reps=reps) - raise ValueError('Trotter mode {} not supported'.format(mode)) + raise ValueError("Trotter mode {} not supported".format(mode)) diff --git a/qiskit/opflow/exceptions.py b/qiskit/opflow/exceptions.py index 99c44f23cece..bf80dd3d22c6 100644 --- a/qiskit/opflow/exceptions.py +++ b/qiskit/opflow/exceptions.py @@ -17,4 +17,5 @@ class OpflowError(QiskitError): """For Opflow specific errors.""" + pass diff --git a/qiskit/opflow/expectations/__init__.py b/qiskit/opflow/expectations/__init__.py index d8b6c1b01f1e..2e326914fdde 100644 --- a/qiskit/opflow/expectations/__init__.py +++ b/qiskit/opflow/expectations/__init__.py @@ -63,9 +63,11 @@ from .matrix_expectation import MatrixExpectation from .cvar_expectation import CVaRExpectation -__all__ = ['ExpectationBase', - 'ExpectationFactory', - 'PauliExpectation', - 'AerPauliExpectation', - 'CVaRExpectation', - 'MatrixExpectation'] +__all__ = [ + "ExpectationBase", + "ExpectationFactory", + "PauliExpectation", + "AerPauliExpectation", + "CVaRExpectation", + "MatrixExpectation", +] diff --git a/qiskit/opflow/expectations/aer_pauli_expectation.py b/qiskit/opflow/expectations/aer_pauli_expectation.py index e020efc55a42..5b3accff7acd 100644 --- a/qiskit/opflow/expectations/aer_pauli_expectation.py +++ b/qiskit/opflow/expectations/aer_pauli_expectation.py @@ -30,13 +30,13 @@ class AerPauliExpectation(ExpectationBase): - r""" An Expectation converter for using Aer's operator snapshot to + r"""An Expectation converter for using Aer's operator snapshot to take expectations of quantum state circuits over Pauli observables. """ def convert(self, operator: OperatorBase) -> OperatorBase: - """ Accept an Operator and return a new Operator with the Pauli measurements replaced by + """Accept an Operator and return a new Operator with the Pauli measurements replaced by AerSnapshot-based expectation circuits. Args: @@ -59,9 +59,10 @@ def _replace_pauli_sums(cls, operator): from qiskit.providers.aer.extensions import SnapshotExpectationValue except ImportError as ex: raise MissingOptionalLibraryError( - libname='qiskit-aer', - name='AerPauliExpectation', - pip_install='pip install qiskit-aer') from ex + libname="qiskit-aer", + name="AerPauliExpectation", + pip_install="pip install qiskit-aer", + ) from ex # The 'expval_measurement' label on the snapshot instruction is special - the # CircuitSampler will look for it to know that the circuit is a Expectation # measurement, and not simply a @@ -69,25 +70,27 @@ def _replace_pauli_sums(cls, operator): if isinstance(operator, PauliSumOp): paulis = [(meas[1], meas[0]) for meas in operator.primitive.to_list()] - snapshot_instruction = SnapshotExpectationValue('expval_measurement', paulis) + snapshot_instruction = SnapshotExpectationValue("expval_measurement", paulis) return CircuitStateFn(snapshot_instruction, coeff=operator.coeff, is_measurement=True) # Change to Pauli representation if necessary - if not {'Pauli'} == operator.primitive_strings(): - logger.warning('Measured Observable is not composed of only Paulis, converting to ' - 'Pauli representation, which can be expensive.') + if not {"Pauli"} == operator.primitive_strings(): + logger.warning( + "Measured Observable is not composed of only Paulis, converting to " + "Pauli representation, which can be expensive." + ) # Setting massive=False because this conversion is implicit. User can perform this # action on the Observable with massive=True explicitly if they so choose. operator = operator.to_pauli_op(massive=False) if isinstance(operator, SummedOp): paulis = [[meas.coeff, meas.primitive] for meas in operator.oplist] - snapshot_instruction = SnapshotExpectationValue('expval_measurement', paulis) + snapshot_instruction = SnapshotExpectationValue("expval_measurement", paulis) snapshot_op = CircuitStateFn(snapshot_instruction, is_measurement=True) return snapshot_op if isinstance(operator, PauliOp): paulis = [[operator.coeff, operator.primitive]] - snapshot_instruction = SnapshotExpectationValue('expval_measurement', paulis) + snapshot_instruction = SnapshotExpectationValue("expval_measurement", paulis) snapshot_op = CircuitStateFn(snapshot_instruction, is_measurement=True) return snapshot_op if isinstance(operator, ListOp): diff --git a/qiskit/opflow/expectations/cvar_expectation.py b/qiskit/opflow/expectations/cvar_expectation.py index b3973dd9e063..cc0d6285744d 100644 --- a/qiskit/opflow/expectations/cvar_expectation.py +++ b/qiskit/opflow/expectations/cvar_expectation.py @@ -66,7 +66,7 @@ def __init__(self, alpha: float, expectation: Optional[ExpectationBase] = None) """ self.alpha = alpha if isinstance(expectation, AerPauliExpectation): - raise NotImplementedError('AerPauliExpecation currently not supported.') + raise NotImplementedError("AerPauliExpecation currently not supported.") if expectation is None: expectation = PauliExpectation() self.expectation = expectation @@ -104,6 +104,7 @@ def compute_variance(self, exp_op: OperatorBase) -> Union[list, float]: Raises: ValueError: If the exp_op does not correspond to an expectation value. """ + def cvar_variance(operator): if isinstance(operator, ComposedOp): sfdict = operator.oplist[1] @@ -113,7 +114,7 @@ def cvar_variance(operator): elif isinstance(operator, ListOp): return operator.combo_fn([cvar_variance(op) for op in operator.oplist]) - raise ValueError("Input operator does not correspond to a value " - "expectation value.") + raise ValueError("Input operator does not correspond to a value " "expectation value.") + cvar_op = self.convert(exp_op) return cvar_variance(cvar_op) diff --git a/qiskit/opflow/expectations/expectation_base.py b/qiskit/opflow/expectations/expectation_base.py index a61aaf12e2b4..5de7de7dbd3d 100644 --- a/qiskit/opflow/expectations/expectation_base.py +++ b/qiskit/opflow/expectations/expectation_base.py @@ -39,7 +39,7 @@ class ExpectationBase(ConverterBase): @abstractmethod def convert(self, operator: OperatorBase) -> OperatorBase: - """ Accept an Operator and return a new Operator with the measurements replaced by + """Accept an Operator and return a new Operator with the measurements replaced by alternate methods to compute the expectation value. Args: @@ -52,7 +52,7 @@ def convert(self, operator: OperatorBase) -> OperatorBase: @abstractmethod def compute_variance(self, exp_op: OperatorBase) -> Union[list, complex, np.ndarray]: - """ Compute the variance of the expectation estimator. + """Compute the variance of the expectation estimator. Args: exp_op: The full expectation value Operator after sampling. diff --git a/qiskit/opflow/expectations/expectation_factory.py b/qiskit/opflow/expectations/expectation_factory.py index 3734d411fbf5..40f521cc45aa 100644 --- a/qiskit/opflow/expectations/expectation_factory.py +++ b/qiskit/opflow/expectations/expectation_factory.py @@ -29,14 +29,16 @@ class ExpectationFactory: - """ A factory class for convenient automatic selection of an Expectation based on the + """A factory class for convenient automatic selection of an Expectation based on the Operator to be converted and backend used to sample the expectation value. """ @staticmethod - def build(operator: OperatorBase, - backend: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None, - include_custom: bool = True) -> ExpectationBase: + def build( + operator: OperatorBase, + backend: Optional[Union[Backend, BaseBackend, QuantumInstance]] = None, + include_custom: bool = True, + ) -> ExpectationBase: """ A factory method for convenient automatic selection of an Expectation based on the Operator to be converted and backend used to sample the expectation value. @@ -62,29 +64,31 @@ def build(operator: OperatorBase, # pylint: disable=cyclic-import primitives = operator.primitive_strings() - if primitives in ({'Pauli'}, {'SparsePauliOp'}): + if primitives in ({"Pauli"}, {"SparsePauliOp"}): if backend_to_check is None: # If user has Aer but didn't specify a backend, use the Aer fast expectation if has_aer(): from qiskit import Aer - backend_to_check = Aer.get_backend('qasm_simulator') + + backend_to_check = Aer.get_backend("qasm_simulator") # If user doesn't have Aer, use statevector_simulator # for < 16 qubits, and qasm with warning for more. else: if operator.num_qubits <= 16: - backend_to_check = BasicAer.get_backend('statevector_simulator') + backend_to_check = BasicAer.get_backend("statevector_simulator") else: logger.warning( - '%d qubits is a very large expectation value. ' - 'Consider installing Aer to use ' - 'Aer\'s fast expectation, which will perform better here. We\'ll use ' - 'the BasicAer qasm backend for this expectation to avoid having to ' - 'construct the %dx%d operator matrix.', + "%d qubits is a very large expectation value. " + "Consider installing Aer to use " + "Aer's fast expectation, which will perform better here. We'll use " + "the BasicAer qasm backend for this expectation to avoid having to " + "construct the %dx%d operator matrix.", operator.num_qubits, 2 ** operator.num_qubits, - 2 ** operator.num_qubits) - backend_to_check = BasicAer.get_backend('qasm_simulator') + 2 ** operator.num_qubits, + ) + backend_to_check = BasicAer.get_backend("qasm_simulator") # If the user specified Aer qasm backend and is using a # Pauli operator, use the Aer fast expectation if we are including such @@ -98,17 +102,19 @@ def build(operator: OperatorBase, elif is_statevector_backend(backend_to_check): if operator.num_qubits >= 16: logger.warning( - 'Note: Using a statevector_simulator with %d qubits can be very expensive. ' - 'Consider using the Aer qasm_simulator instead to take advantage of Aer\'s ' - 'built-in fast Pauli Expectation', operator.num_qubits) + "Note: Using a statevector_simulator with %d qubits can be very expensive. " + "Consider using the Aer qasm_simulator instead to take advantage of Aer's " + "built-in fast Pauli Expectation", + operator.num_qubits, + ) return MatrixExpectation() # All other backends, including IBMQ, BasicAer QASM, go here. else: return PauliExpectation() - elif primitives == {'Matrix'}: + elif primitives == {"Matrix"}: return MatrixExpectation() else: - raise ValueError('Expectations of Mixed Operators not yet supported.') + raise ValueError("Expectations of Mixed Operators not yet supported.") diff --git a/qiskit/opflow/expectations/matrix_expectation.py b/qiskit/opflow/expectations/matrix_expectation.py index ebda53229f78..19384b73f141 100644 --- a/qiskit/opflow/expectations/matrix_expectation.py +++ b/qiskit/opflow/expectations/matrix_expectation.py @@ -21,11 +21,11 @@ class MatrixExpectation(ExpectationBase): - """ An Expectation converter which converts Operator measurements to be matrix-based so they - can be evaluated by matrix multiplication. """ + """An Expectation converter which converts Operator measurements to be matrix-based so they + can be evaluated by matrix multiplication.""" def convert(self, operator: OperatorBase) -> OperatorBase: - """ Accept an Operator and return a new Operator with the Pauli measurements replaced by + """Accept an Operator and return a new Operator with the Pauli measurements replaced by Matrix based measurements. Args: diff --git a/qiskit/opflow/expectations/pauli_expectation.py b/qiskit/opflow/expectations/pauli_expectation.py index 05b24f8a635a..545c0044c8eb 100644 --- a/qiskit/opflow/expectations/pauli_expectation.py +++ b/qiskit/opflow/expectations/pauli_expectation.py @@ -51,7 +51,7 @@ def __init__(self, group_paulis: bool = True) -> None: self._grouper = AbelianGrouper() if group_paulis else None def convert(self, operator: OperatorBase) -> OperatorBase: - """ Accepts an Operator and returns a new Operator with the Pauli measurements replaced by + """Accepts an Operator and returns a new Operator with the Pauli measurements replaced by diagonal Pauli post-rotation based measurements so they can be evaluated by sampling and averaging. @@ -68,8 +68,10 @@ def convert(self, operator: OperatorBase) -> OperatorBase: and not isinstance(operator.primitive, PauliSumOp) and {"Pauli"} != operator.primitive_strings() ): - logger.warning('Measured Observable is not composed of only Paulis, converting to ' - 'Pauli representation, which can be expensive.') + logger.warning( + "Measured Observable is not composed of only Paulis, converting to " + "Pauli representation, which can be expensive." + ) # Setting massive=False because this conversion is implicit. User can perform this # action on the Observable with massive=True explicitly if they so choose. pauli_obsv = operator.primitive.to_pauli_op(massive=False) @@ -90,14 +92,17 @@ def convert(self, operator: OperatorBase) -> OperatorBase: return operator def compute_variance(self, exp_op: OperatorBase) -> Union[list, float, np.ndarray]: - def sum_variance(operator): if isinstance(operator, ComposedOp): sfdict = operator.oplist[1] measurement = operator.oplist[0] average = measurement.eval(sfdict) - variance = sum([(v * (measurement.eval(b) - average))**2 - for (b, v) in sfdict.primitive.items()]) + variance = sum( + [ + (v * (measurement.eval(b) - average)) ** 2 + for (b, v) in sfdict.primitive.items() + ] + ) return operator.coeff * variance elif isinstance(operator, ListOp): diff --git a/qiskit/opflow/gradients/__init__.py b/qiskit/opflow/gradients/__init__.py index 3595def7e91e..da3c1cf79488 100644 --- a/qiskit/opflow/gradients/__init__.py +++ b/qiskit/opflow/gradients/__init__.py @@ -173,13 +173,15 @@ from .qfi_base import QFIBase from .qfi import QFI -__all__ = ['DerivativeBase', - 'CircuitGradient', - 'GradientBase', - 'Gradient', - 'NaturalGradient', - 'HessianBase', - 'Hessian', - 'QFIBase', - 'QFI', - 'CircuitQFI'] +__all__ = [ + "DerivativeBase", + "CircuitGradient", + "GradientBase", + "Gradient", + "NaturalGradient", + "HessianBase", + "Hessian", + "QFIBase", + "QFI", + "CircuitQFI", +] diff --git a/qiskit/opflow/gradients/circuit_gradients/__init__.py b/qiskit/opflow/gradients/circuit_gradients/__init__.py index 7864bb9f8746..eae08898049a 100644 --- a/qiskit/opflow/gradients/circuit_gradients/__init__.py +++ b/qiskit/opflow/gradients/circuit_gradients/__init__.py @@ -16,7 +16,4 @@ from .lin_comb import LinComb from .param_shift import ParamShift -__all__ = ['CircuitGradient', - 'LinComb', - 'ParamShift' - ] +__all__ = ["CircuitGradient", "LinComb", "ParamShift"] diff --git a/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py b/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py index 7e7e7a998bf4..ba0044d42d40 100644 --- a/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py +++ b/qiskit/opflow/gradients/circuit_gradients/circuit_gradient.py @@ -36,14 +36,19 @@ class CircuitGradient(ConverterBase): # pylint: disable=arguments-differ @abstractmethod - def convert(self, - operator: OperatorBase, - params: Optional[Union[ParameterExpression, ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]]]] - = None, - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[ + Union[ + ParameterExpression, + ParameterVector, + List[ParameterExpression], + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ] + ] = None, + ) -> OperatorBase: r""" Args: operator: The operator we are taking the gradient of diff --git a/qiskit/opflow/gradients/circuit_gradients/lin_comb.py b/qiskit/opflow/gradients/circuit_gradients/lin_comb.py index 78efe3daa502..7b6852238bba 100644 --- a/qiskit/opflow/gradients/circuit_gradients/lin_comb.py +++ b/qiskit/opflow/gradients/circuit_gradients/lin_comb.py @@ -21,16 +21,32 @@ import scipy import numpy as np from qiskit.circuit import Gate, Instruction -from qiskit.circuit import (QuantumCircuit, QuantumRegister, ParameterVector, - ParameterExpression, Parameter) +from qiskit.circuit import ( + QuantumCircuit, + QuantumRegister, + ParameterVector, + ParameterExpression, + Parameter, +) from qiskit.circuit.parametertable import ParameterTable from qiskit.circuit.controlledgate import ControlledGate from qiskit.circuit.library import SGate, SdgGate, XGate -from qiskit.circuit.library.standard_gates import (CXGate, CYGate, CZGate, - IGate, RXGate, RXXGate, - RYGate, RYYGate, RZGate, - RZXGate, RZZGate, PhaseGate, - UGate, ZGate) +from qiskit.circuit.library.standard_gates import ( + CXGate, + CYGate, + CZGate, + IGate, + RXGate, + RXXGate, + RYGate, + RYYGate, + RZGate, + RZXGate, + RZZGate, + PhaseGate, + UGate, + ZGate, +) from qiskit.quantum_info import partial_trace from ...operator_base import OperatorBase @@ -57,13 +73,17 @@ class LinComb(CircuitGradient): """ # pylint: disable=signature-differs - def convert(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]]] - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Union[ + ParameterExpression, + ParameterVector, + List[ParameterExpression], + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ], + ) -> OperatorBase: """Convert ``operator`` into an operator that represents the gradient w.r.t. ``params``. Args: @@ -83,13 +103,17 @@ def convert(self, return self._prepare_operator(operator, params) # pylint: disable=too-many-return-statements - def _prepare_operator(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]]] - ) -> OperatorBase: + def _prepare_operator( + self, + operator: OperatorBase, + params: Union[ + ParameterExpression, + ParameterVector, + List[ParameterExpression], + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ], + ) -> OperatorBase: """Traverse ``operator`` to get back the adapted operator representing the gradient. Args: @@ -132,48 +156,66 @@ def _prepare_operator(self, if len(operator.oplist) == 2: state_op = operator[1] if not isinstance(state_op, StateFn): - raise TypeError('The StateFn representing the quantum state could not be' - 'extracted.') - if isinstance(params, (ParameterExpression, ParameterVector)) or \ - (isinstance(params, list) and all(isinstance(param, ParameterExpression) - for param in params)): - - return self._gradient_states(state_op, meas_op=(2 * ~StateFn(Z) ^ - operator[0]), - target_params=params) - elif isinstance(params, tuple) or \ - (isinstance(params, list) and all(isinstance(param, tuple) - for param in params)): - return self._hessian_states(state_op, - meas_op=(4 * ~StateFn(Z ^ I) ^ operator[0]), - target_params=params) # type: ignore + raise TypeError( + "The StateFn representing the quantum state could not be" "extracted." + ) + if isinstance(params, (ParameterExpression, ParameterVector)) or ( + isinstance(params, list) + and all(isinstance(param, ParameterExpression) for param in params) + ): + + return self._gradient_states( + state_op, meas_op=(2 * ~StateFn(Z) ^ operator[0]), target_params=params + ) + elif isinstance(params, tuple) or ( + isinstance(params, list) + and all(isinstance(param, tuple) for param in params) + ): + return self._hessian_states( + state_op, + meas_op=(4 * ~StateFn(Z ^ I) ^ operator[0]), + target_params=params, + ) # type: ignore else: - raise OpflowError('The linear combination gradient does only support the ' - 'computation of 1st gradients and 2nd order gradients.') + raise OpflowError( + "The linear combination gradient does only support the " + "computation of 1st gradients and 2nd order gradients." + ) else: state_op = deepcopy(operator) state_op.oplist.pop(0) if not isinstance(state_op, StateFn): - raise TypeError('The StateFn representing the quantum state could not be' - 'extracted.') - - if isinstance(params, (ParameterExpression, ParameterVector)) or \ - (isinstance(params, list) and all(isinstance(param, ParameterExpression) - for param in params)): - return state_op.traverse(partial(self._gradient_states, - meas_op=(2 * ~StateFn(Z) ^ operator[0]), - target_params=params)) - elif isinstance(params, tuple) or \ - (isinstance(params, list) and all(isinstance(param, tuple) - for param in params)): + raise TypeError( + "The StateFn representing the quantum state could not be" "extracted." + ) + + if isinstance(params, (ParameterExpression, ParameterVector)) or ( + isinstance(params, list) + and all(isinstance(param, ParameterExpression) for param in params) + ): + return state_op.traverse( + partial( + self._gradient_states, + meas_op=(2 * ~StateFn(Z) ^ operator[0]), + target_params=params, + ) + ) + elif isinstance(params, tuple) or ( + isinstance(params, list) + and all(isinstance(param, tuple) for param in params) + ): return state_op.traverse( - partial(self._hessian_states, - meas_op=(4 * ~StateFn(Z ^ I) ^ operator[0]), - target_params=params)) + partial( + self._hessian_states, + meas_op=(4 * ~StateFn(Z ^ I) ^ operator[0]), + target_params=params, + ) + ) raise OpflowError( - 'The linear combination gradient does only support the computation ' - 'of 1st gradients and 2nd order gradients.') + "The linear combination gradient does only support the computation " + "of 1st gradients and 2nd order gradients." + ) else: return operator.traverse(partial(self._prepare_operator, params=params)) elif isinstance(operator, ListOp): @@ -182,18 +224,20 @@ def _prepare_operator(self, if operator.is_measurement: return operator.traverse(partial(self._prepare_operator, params=params)) else: - if isinstance(params, (ParameterExpression, ParameterVector)) or \ - (isinstance(params, list) and all(isinstance(param, ParameterExpression) - for param in params)): + if isinstance(params, (ParameterExpression, ParameterVector)) or ( + isinstance(params, list) + and all(isinstance(param, ParameterExpression) for param in params) + ): return self._gradient_states(operator, target_params=params) - elif isinstance(params, tuple) or \ - (isinstance(params, list) and all(isinstance(param, tuple) - for param in params)): + elif isinstance(params, tuple) or ( + isinstance(params, list) and all(isinstance(param, tuple) for param in params) + ): return self._hessian_states(operator, target_params=params) # type: ignore else: raise OpflowError( - 'The linear combination gradient does only support the computation ' - 'of 1st gradients and 2nd order gradients.') + "The linear combination gradient does only support the computation " + "of 1st gradients and 2nd order gradients." + ) elif isinstance(operator, PrimitiveOp): return operator return operator @@ -225,11 +269,13 @@ def get_result(item): lin_comb_op = 2 * Z ^ (I ^ state_op.num_qubits) lin_comb_op = lin_comb_op.to_matrix() outer = np.outer(item, item.conj()) - return list(np.diag(partial_trace(lin_comb_op.dot(outer), - [state_op.num_qubits]).data)) + return list( + np.diag(partial_trace(lin_comb_op.dot(outer), [state_op.num_qubits]).data) + ) else: raise TypeError( - 'The state result should be either a DictStateFn or a VectorStateFn.') + "The state result should be either a DictStateFn or a VectorStateFn." + ) if not isinstance(x, Iterable): return get_result(x) @@ -252,8 +298,11 @@ def get_result(item): # Generate the operator which computes the linear combination lin_comb_op = 4 * (I ^ (state_op.num_qubits + 1)) ^ Z lin_comb_op = lin_comb_op.to_matrix() - return list(np.diag( - partial_trace(lin_comb_op.dot(np.outer(item, np.conj(item))), [0, 1]).data)) + return list( + np.diag( + partial_trace(lin_comb_op.dot(np.outer(item, np.conj(item))), [0, 1]).data + ) + ) elif isinstance(item, dict): prob_dict = {} for key, val in item.values(): @@ -267,8 +316,8 @@ def get_result(item): return prob_dict else: raise TypeError( - 'The state result should be either a ' - 'DictStateFn or a VectorStateFn.') + "The state result should be either a " "DictStateFn or a VectorStateFn." + ) if not isinstance(x, Iterable): return get_result(x) @@ -351,8 +400,9 @@ def _gate_gradient_dict(gate: Gate) -> List[Tuple[List[complex], List[Instructio # TODO support arbitrary control states if gate.ctrl_state != 2 ** gate.num_ctrl_qubits - 1: raise OpflowError( - 'Function only support controlled gates with control state `1` on all control ' - 'qubits.') + "Function only support controlled gates with control state `1` on all control " + "qubits." + ) base_coeffs_gates = LinComb._gate_gradient_dict(gate.base_gate) coeffs_gates = [] @@ -360,9 +410,10 @@ def _gate_gradient_dict(gate: Gate) -> List[Tuple[List[complex], List[Instructio # of gates. # The following line generates the decomposition gates. - proj_gates_controlled = [[(-1) ** p.count(ZGate()), p] for p in - product([IGate(), ZGate()], - repeat=gate.num_ctrl_qubits)] + proj_gates_controlled = [ + [(-1) ** p.count(ZGate()), p] + for p in product([IGate(), ZGate()], repeat=gate.num_ctrl_qubits) + ] for base_coeffs, base_gates in base_coeffs_gates: # loop over parameters coeffs = [] gates = [] @@ -374,19 +425,34 @@ def _gate_gradient_dict(gate: Gate) -> List[Tuple[List[complex], List[Instructio if isinstance(proj_gate, ZGate): controlled_circ.cz(0, i + 1) if not isinstance(base_gate, IGate): - controlled_circ.append(base_gate, [0, range(gate.num_ctrl_qubits + 1, - gate.num_ctrl_qubits + - gate.num_qubits)]) + controlled_circ.append( + base_gate, + [ + 0, + range( + gate.num_ctrl_qubits + 1, + gate.num_ctrl_qubits + gate.num_qubits, + ), + ], + ) gates.append(controlled_circ.to_instruction()) c_g = (coeffs, gates) coeffs_gates.append(c_g) return coeffs_gates - raise TypeError('Unrecognized parameterized gate, {}'.format(gate)) + raise TypeError("Unrecognized parameterized gate, {}".format(gate)) @staticmethod - def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superpos, - open_ctrl=False, trim_after_grad_gate=False): + def apply_grad_gate( + circuit, + gate, + param_index, + grad_gate, + grad_coeff, + qr_superpos, + open_ctrl=False, + trim_after_grad_gate=False, + ): """Util function to apply a gradient gate for the linear combination of unitaries method. Replaces the ``gate`` instance in ``circuit`` with ``grad_gate`` using ``qr_superpos`` as @@ -414,9 +480,9 @@ def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superp # copy the input circuit taking the gates by reference out = QuantumCircuit(*circuit.qregs) out._data = circuit._data.copy() - out._parameter_table = ParameterTable({ - param: values.copy() for param, values in circuit._parameter_table.items() - }) + out._parameter_table = ParameterTable( + {param: values.copy() for param, values in circuit._parameter_table.items()} + ) # get the data index and qubits of the target gate TODO use built-in gate_idx, gate_qubits = None, None @@ -425,7 +491,7 @@ def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superp gate_idx, gate_qubits = i, qarg break if gate_idx is None: - raise RuntimeError('The specified gate could not be found in the circuit data.') + raise RuntimeError("The specified gate could not be found in the circuit data.") # initialize replacement instructions replacement = [] @@ -499,17 +565,18 @@ def apply_grad_gate(circuit, gate, param_index, grad_gate, grad_coeff, qr_superp out._parameter_table = table else: - out._data[gate_idx:gate_idx + 1] = replacement + out._data[gate_idx : gate_idx + 1] = replacement return out - def _gradient_states(self, - state_op: StateFn, - meas_op: Union[OperatorBase, bool] = True, - target_params: Optional[Union[Parameter, List[Parameter]]] = None, - open_ctrl: bool = False, - trim_after_grad_gate: bool = False - ) -> ListOp: + def _gradient_states( + self, + state_op: StateFn, + meas_op: Union[OperatorBase, bool] = True, + target_params: Optional[Union[Parameter, List[Parameter]]] = None, + open_ctrl: bool = False, + trim_after_grad_gate: bool = False, + ) -> ListOp: """Generate the gradient states. Args: @@ -551,8 +618,14 @@ def _gradient_states(self, # construct the states for grad_coeff, grad_gate in zip(grad_coeffs, grad_gates): grad_circuit = self.apply_grad_gate( - state_qc, gate, idx, grad_gate, grad_coeff, qr_superpos, - open_ctrl, trim_after_grad_gate + state_qc, + gate, + idx, + grad_gate, + grad_coeff, + qr_superpos, + open_ctrl, + trim_after_grad_gate, ) # apply final hadamard on superposition qubit grad_circuit.h(qr_superpos) @@ -567,9 +640,9 @@ def _gradient_states(self, if isinstance(meas_op, OperatorBase): state = meas_op @ state elif meas_op is True: - state = ListOp([state], - combo_fn=partial(self._grad_combo_fn, - state_op=state_op)) + state = ListOp( + [state], combo_fn=partial(self._grad_combo_fn, state_op=state_op) + ) if param_expression != param: # parameter is not identity, apply chain rule param_grad = param_expression.gradient(param) @@ -581,14 +654,17 @@ def _gradient_states(self, return ListOp(oplist) if len(oplist) > 1 else oplist[0] - def _hessian_states(self, - state_op: StateFn, - meas_op: Optional[OperatorBase] = None, - target_params: Optional[Union[Tuple[ParameterExpression, - ParameterExpression], - List[Tuple[ParameterExpression, - ParameterExpression]]]] = None - ) -> OperatorBase: + def _hessian_states( + self, + state_op: StateFn, + meas_op: Optional[OperatorBase] = None, + target_params: Optional[ + Union[ + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ] + ] = None, + ) -> OperatorBase: """Generate the operator states whose evaluation returns the Hessian (items). Args: @@ -608,12 +684,14 @@ def _hessian_states(self, target_params = [target_params] if not all(isinstance(params, tuple) for params in target_params): - raise TypeError('Please define in the parameters for which the Hessian is evaluated ' - 'either as parameter tuple or a list of parameter tuples') + raise TypeError( + "Please define in the parameters for which the Hessian is evaluated " + "either as parameter tuple or a list of parameter tuples" + ) # create circuit with two additional qubits - qr_add0 = QuantumRegister(1, 's0') - qr_add1 = QuantumRegister(1, 's1') + qr_add0 = QuantumRegister(1, "s0") + qr_add1 = QuantumRegister(1, "s1") state_qc = QuantumCircuit(*state_op.primitive.qregs, qr_add0, qr_add1) # add hadamards @@ -662,13 +740,15 @@ def _hessian_states(self, else: # special operator for probability gradients # uses combo_fn on list op with a single operator - state = ListOp([state], combo_fn=partial(self._hess_combo_fn, - state_op=state_op)) + state = ListOp( + [state], + combo_fn=partial(self._hess_combo_fn, state_op=state_op), + ) # Chain Rule Parameter Expression param_grad = 1 for gate, idx, param in zip( - [gate_a, gate_b], [idx_a, idx_b], [param_a, param_b] + [gate_a, gate_b], [idx_a, idx_b], [param_a, param_b] ): param_expression = gate.params[idx] if param_expression != param: # need to apply chain rule @@ -693,7 +773,7 @@ def _z_exp(spmatrix): for index, amplitude in dok.items(): binary = bin(index[1])[2:].zfill(num_qubits) - sign = -1 if binary[0] == '1' else 1 + sign = -1 if binary[0] == "1" else 1 new_index = int(binary[1:], 2) exp[(0, new_index)] = exp[(0, new_index)] + 2 * sign * np.abs(amplitude) ** 2 diff --git a/qiskit/opflow/gradients/circuit_gradients/param_shift.py b/qiskit/opflow/gradients/circuit_gradients/param_shift.py index cf387556cb3b..8baa8e9ca5ab 100644 --- a/qiskit/opflow/gradients/circuit_gradients/param_shift.py +++ b/qiskit/opflow/gradients/circuit_gradients/param_shift.py @@ -43,9 +43,7 @@ class ParamShift(CircuitGradient): method. """ - def __init__(self, - analytic: bool = True, - epsilon: float = 1e-6): + def __init__(self, analytic: bool = True, epsilon: float = 1e-6): r""" Args: analytic: If True use the parameter shift rule to compute analytic gradients, @@ -81,12 +79,17 @@ def epsilon(self) -> float: return self._epsilon # pylint: disable=signature-differs - def convert(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression], - Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]]] - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Union[ + ParameterExpression, + ParameterVector, + List[ParameterExpression], + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ], + ) -> OperatorBase: """ Args: operator: The operator corresponding to our quantum state we are taking the @@ -114,20 +117,27 @@ def convert(self, return self._parameter_shift(operator, params) elif all(isinstance(param, tuple) for param in params): return ListOp( - [self._parameter_shift(self._parameter_shift(operator, pair[0]), pair[1]) - for pair in params]) + [ + self._parameter_shift(self._parameter_shift(operator, pair[0]), pair[1]) + for pair in params + ] + ) else: - raise OpflowError('The linear combination gradient does only support ' - 'the computation ' - 'of 1st gradients and 2nd order gradients.') + raise OpflowError( + "The linear combination gradient does only support " + "the computation " + "of 1st gradients and 2nd order gradients." + ) else: - raise OpflowError('The linear combination gradient does only support the computation ' - 'of 1st gradients and 2nd order gradients.') + raise OpflowError( + "The linear combination gradient does only support the computation " + "of 1st gradients and 2nd order gradients." + ) # pylint: disable=too-many-return-statements - def _parameter_shift(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List]) -> OperatorBase: + def _parameter_shift( + self, operator: OperatorBase, params: Union[ParameterExpression, ParameterVector, List] + ) -> OperatorBase: r""" Args: operator: The operator containing circuits we are taking the derivative of. @@ -142,12 +152,14 @@ def _parameter_shift(self, """ if isinstance(params, (ParameterVector, list)): param_grads = [self._parameter_shift(operator, param) for param in params] - absent_params = [params[i] for i, grad_ops in enumerate(param_grads) if - grad_ops is None] + absent_params = [ + params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None + ] if len(absent_params) > 0: raise ValueError( "The following parameters do not appear in the provided operator: ", - absent_params) + absent_params, + ) return ListOp(absent_params) # By this point, it's only one parameter @@ -162,9 +174,9 @@ def _parameter_shift(self, if len(trimmed_oplist) == 0: return None # Rebuild the operator with the trimmed down oplist - properties = {'coeff': return_op._coeff, 'abelian': return_op._abelian} + properties = {"coeff": return_op._coeff, "abelian": return_op._abelian} if return_op.__class__ == ListOp: - properties['combo_fn'] = return_op.combo_fn + properties["combo_fn"] = return_op.combo_fn return return_op.__class__(oplist=trimmed_oplist, **properties) else: @@ -172,8 +184,9 @@ def _parameter_shift(self, if len(circs) > 1: raise TypeError( - 'Please define an operator with a single circuit representing ' - 'the quantum state.') + "Please define an operator with a single circuit representing " + "the quantum state." + ) if len(circs) == 0: return operator circ = circs[0] @@ -207,14 +220,14 @@ def _parameter_shift(self, # once by -pi/2. if self.analytic: shift_constant = 0.5 - pshift_gate.params[param_index] = (p_param + (np.pi / (4 * shift_constant))) - mshift_gate.params[param_index] = (m_param - (np.pi / (4 * shift_constant))) + pshift_gate.params[param_index] = p_param + (np.pi / (4 * shift_constant)) + mshift_gate.params[param_index] = m_param - (np.pi / (4 * shift_constant)) # For finite difference gradients the circuit parameters are shifted once by # +epsilon and once by -epsilon. else: - shift_constant = 1. / (2 * self._epsilon) - pshift_gate.params[param_index] = (p_param + self._epsilon) - mshift_gate.params[param_index] = (m_param - self._epsilon) + shift_constant = 1.0 / (2 * self._epsilon) + pshift_gate.params[param_index] = p_param + self._epsilon + mshift_gate.params[param_index] = m_param - self._epsilon # The results of the shifted operators are now evaluated according the parameter # shift / finite difference formula. if isinstance(operator, ComposedOp): @@ -224,13 +237,14 @@ def _parameter_shift(self, elif isinstance(operator, StateFn): shifted_op = ListOp( [pshift_op, mshift_op], - combo_fn=partial(self._prob_combo_fn, shift_constant=shift_constant)) + combo_fn=partial(self._prob_combo_fn, shift_constant=shift_constant), + ) else: - raise TypeError('Probability gradients are not supported for the given ' - 'operator type') + raise TypeError( + "Probability gradients are not supported for the given " "operator type" + ) - if isinstance(p_param, ParameterExpression) and not isinstance(p_param, - Parameter): + if isinstance(p_param, ParameterExpression) and not isinstance(p_param, Parameter): expr_grad = _coeff_derivative(p_param, param) shifted_op *= expr_grad if not summed_shifted_op: @@ -246,9 +260,15 @@ def _parameter_shift(self, return SummedOp(shifted_ops).reduce() @staticmethod - def _prob_combo_fn(x: Union[DictStateFn, VectorStateFn, SparseVectorStateFn, - List[Union[DictStateFn, VectorStateFn, SparseVectorStateFn]]], - shift_constant: float) -> Union[Dict, np.ndarray]: + def _prob_combo_fn( + x: Union[ + DictStateFn, + VectorStateFn, + SparseVectorStateFn, + List[Union[DictStateFn, VectorStateFn, SparseVectorStateFn]], + ], + shift_constant: float, + ) -> Union[Dict, np.ndarray]: """Implement the combo_fn used to evaluate probability gradients Args: @@ -287,16 +307,18 @@ def get_primitives(item): prob_dict: Dict[str, float] = {} for i, item in enumerate(items): for key, prob_counts in item.items(): - prob_dict[key] = prob_dict.get(key, 0) + \ - shift_constant * ((-1) ** i) * prob_counts + prob_dict[key] = ( + prob_dict.get(key, 0) + shift_constant * ((-1) ** i) * prob_counts + ) return prob_dict elif isinstance(items[0], scipy.sparse.spmatrix): # If x was given as StateFn the state amplitudes need to be multiplied in order to # evaluate the sampling probabilities which are then subtracted according to the # parameter shift rule. if is_statefn: - return shift_constant * np.subtract(items[0].multiply(np.conj(items[0])), - items[1].multiply(np.conj(items[1]))) + return shift_constant * np.subtract( + items[0].multiply(np.conj(items[0])), items[1].multiply(np.conj(items[1])) + ) # If x was not given as a StateFn the state amplitudes were already converted into # sampling probabilities which are then only subtracted according to the # parameter shift rule. @@ -307,15 +329,18 @@ def get_primitives(item): # evaluate the sampling probabilities which are then subtracted according to the # parameter shift rule. if is_statefn: - return shift_constant * np.subtract(np.multiply(items[0], np.conj(items[0])), - np.multiply(items[1], np.conj(items[1]))) + return shift_constant * np.subtract( + np.multiply(items[0], np.conj(items[0])), + np.multiply(items[1], np.conj(items[1])), + ) # If x was not given as a StateFn the state amplitudes were already converted into # sampling probabilities which are then only subtracted according to the # parameter shift rule. else: return shift_constant * np.subtract(items[0], items[1]) raise TypeError( - 'Probability gradients can only be evaluated from VectorStateFs or DictStateFns.') + "Probability gradients can only be evaluated from VectorStateFs or DictStateFns." + ) @staticmethod def _unroll_to_supported_operations(circuit: QuantumCircuit) -> QuantumCircuit: @@ -329,15 +354,14 @@ def _unroll_to_supported_operations(circuit: QuantumCircuit) -> QuantumCircuit: Quantum circuit which is unrolled into supported operations """ - supported = {'x', 'y', 'z', 'h', 'rx', 'ry', 'rz', 'p', 'u', 'cx', 'cy', 'cz'} + supported = {"x", "y", "z", "h", "rx", "ry", "rz", "p", "u", "cx", "cy", "cz"} unique_ops = set(circuit.count_ops().keys()) if not unique_ops.issubset(supported): circuit = transpile(circuit, basis_gates=list(supported), optimization_level=0) return circuit @staticmethod - def _replace_operator_circuit(operator: OperatorBase, - circuit: QuantumCircuit) -> OperatorBase: + def _replace_operator_circuit(operator: OperatorBase, circuit: QuantumCircuit) -> OperatorBase: """Replace a circuit element in an operator with a single element given as circuit Args: @@ -354,8 +378,7 @@ def _replace_operator_circuit(operator: OperatorBase, elif isinstance(operator, CircuitOp): return CircuitOp(circuit, coeff=operator.coeff) elif isinstance(operator, (ComposedOp, ListOp)): - return operator.traverse( - partial(ParamShift._replace_operator_circuit, circuit=circuit)) + return operator.traverse(partial(ParamShift._replace_operator_circuit, circuit=circuit)) else: return operator @@ -404,6 +427,6 @@ def unroll_operator(cls, operator: OperatorBase) -> Union[OperatorBase, List[Ope """ if isinstance(operator, ListOp): return [cls.unroll_operator(op) for op in operator] - if hasattr(operator, 'primitive') and isinstance(operator.primitive, ListOp): + if hasattr(operator, "primitive") and isinstance(operator.primitive, ListOp): return [operator.__class__(op) for op in operator.primitive] return operator diff --git a/qiskit/opflow/gradients/circuit_qfis/__init__.py b/qiskit/opflow/gradients/circuit_qfis/__init__.py index b6853761a95b..d32126acd523 100644 --- a/qiskit/opflow/gradients/circuit_qfis/__init__.py +++ b/qiskit/opflow/gradients/circuit_qfis/__init__.py @@ -17,7 +17,4 @@ from .overlap_diag import OverlapDiag from .overlap_block_diag import OverlapBlockDiag -__all__ = ['CircuitQFI', - 'LinCombFull', - 'OverlapDiag', - 'OverlapBlockDiag'] +__all__ = ["CircuitQFI", "LinCombFull", "OverlapDiag", "OverlapBlockDiag"] diff --git a/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py b/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py index 0a368a77c762..693f713263ae 100644 --- a/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py +++ b/qiskit/opflow/gradients/circuit_qfis/circuit_qfi.py @@ -37,10 +37,11 @@ class CircuitQFI(ConverterBase): # pylint: disable=arguments-differ @abstractmethod - def convert(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> OperatorBase: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` diff --git a/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py b/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py index 069f82e78786..79ad9bf9a668 100644 --- a/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py +++ b/qiskit/opflow/gradients/circuit_qfis/lin_comb_full.py @@ -35,10 +35,11 @@ class LinCombFull(CircuitQFI): See also :class:`~qiskit.opflow.QFI`. """ - def convert(self, - operator: CircuitStateFn, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ) -> ListOp: + def convert( + self, + operator: CircuitStateFn, + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` @@ -59,8 +60,10 @@ def convert(self, # Check if the given operator corresponds to a quantum state given as a circuit. if not isinstance(operator, CircuitStateFn): - raise TypeError('LinCombFull is only compatible with states that are given as ' - f'CircuitStateFn, not {type(operator)}') + raise TypeError( + "LinCombFull is only compatible with states that are given as " + f"CircuitStateFn, not {type(operator)}" + ) # If a single parameter is given wrap it into a list. if isinstance(params, ParameterExpression): @@ -71,8 +74,11 @@ def convert(self, # First, the operators are computed which can compensate for a potential phase-mismatch # between target and trained state, i.e.〈ψ|∂lψ〉 gradient_states = LinComb()._gradient_states( - operator, meas_op=phase_fix_observable, target_params=params, open_ctrl=False, - trim_after_grad_gate=True + operator, + meas_op=phase_fix_observable, + target_params=params, + open_ctrl=False, + trim_after_grad_gate=True, ) # if type(gradient_states) in [ListOp, SummedOp]: # pylint: disable=unidiomatic-typecheck if type(gradient_states) == ListOp: @@ -83,7 +89,7 @@ def convert(self, # Get 4 * Re[〈∂kψ|∂lψ] qfi_operators = [] # Add a working qubit - qr_work = QuantumRegister(1, 'work_qubit') + qr_work = QuantumRegister(1, "work_qubit") state_qc = QuantumCircuit(*operator.primitive.qregs, qr_work) state_qc.h(qr_work) state_qc.compose(operator.primitive, inplace=True) @@ -123,14 +129,26 @@ def convert(self, grad_coeff_ij = np.conj(grad_coeff_i) * grad_coeff_j qfi_circuit = LinComb.apply_grad_gate( - state_qc, gate_i, idx_i, grad_gate_i, grad_coeff_ij, qr_work, - open_ctrl=True, trim_after_grad_gate=(location_j < location_i) + state_qc, + gate_i, + idx_i, + grad_gate_i, + grad_coeff_ij, + qr_work, + open_ctrl=True, + trim_after_grad_gate=(location_j < location_i), ) # create a copy of the original circuit with the same registers qfi_circuit = LinComb.apply_grad_gate( - qfi_circuit, gate_j, idx_j, grad_gate_j, 1, qr_work, - open_ctrl=False, trim_after_grad_gate=(location_j >= location_i) + qfi_circuit, + gate_j, + idx_j, + grad_gate_j, + 1, + qr_work, + open_ctrl=False, + trim_after_grad_gate=(location_j >= location_i), ) qfi_circuit.h(qr_work) @@ -142,7 +160,7 @@ def convert(self, param_grad = 1 for gate, idx, param in zip( - [gate_i, gate_j], [idx_i, idx_j], [param_i, param_j] + [gate_i, gate_j], [idx_i, idx_j], [param_i, param_j] ): param_expression = gate.params[idx] param_grad *= param_expression.gradient(param) @@ -156,8 +174,9 @@ def convert(self, def phase_fix_combo_fn(x): return 4 * (-0.5) * (x[0] * np.conjugate(x[1]) + x[1] * np.conjugate(x[0])) - phase_fix = ListOp([phase_fix_states[i], phase_fix_states[j]], - combo_fn=phase_fix_combo_fn) + phase_fix = ListOp( + [phase_fix_states[i], phase_fix_states[j]], combo_fn=phase_fix_combo_fn + ) # Add the phase fix quantities to the entries of the QFI # Get 4 * Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] qfi_ops += [SummedOp(qfi_op) + phase_fix] diff --git a/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py b/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py index 7869fc6dc9db..5fe85d39eb21 100644 --- a/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py +++ b/qiskit/opflow/gradients/circuit_qfis/overlap_block_diag.py @@ -38,10 +38,11 @@ class OverlapBlockDiag(CircuitQFI): See also :class:`~qiskit.opflow.QFI`. """ - def convert(self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ) -> ListOp: + def convert( + self, + operator: Union[CircuitOp, CircuitStateFn], + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` @@ -56,15 +57,14 @@ def convert(self, NotImplementedError: If ``operator`` is neither ``CircuitOp`` nor ``CircuitStateFn``. """ if not isinstance(operator, (CircuitOp, CircuitStateFn)): - raise NotImplementedError('operator must be a CircuitOp or CircuitStateFn') + raise NotImplementedError("operator must be a CircuitOp or CircuitStateFn") return self._block_diag_approx(operator=operator, params=params) - def _block_diag_approx(self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, - ParameterVector, - List[ParameterExpression]] - ) -> ListOp: + def _block_diag_approx( + self, + operator: Union[CircuitOp, CircuitStateFn], + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` @@ -131,13 +131,16 @@ def _block_diag_approx(self, def get_parameter_expression(circuit, param): if len(circuit._parameter_table[param]) > 1: - raise NotImplementedError("OverlapDiag does not yet support multiple " - "gates parameterized by a single parameter. For such " - "circuits use LinCombFull") + raise NotImplementedError( + "OverlapDiag does not yet support multiple " + "gates parameterized by a single parameter. For such " + "circuits use LinCombFull" + ) gate = circuit._parameter_table[param][0][0] if len(gate.params) > 1: - raise OpflowError("OverlapDiag cannot yet support gates with more than one " - "parameter.") + raise OpflowError( + "OverlapDiag cannot yet support gates with more than one " "parameter." + ) param_value = gate.params[0] return param_value @@ -151,7 +154,8 @@ def get_parameter_expression(circuit, param): if i == j: block[i][i] = ListOp([single_terms[i]], combo_fn=lambda x: 1 - x[0] ** 2) if isinstance(param_expr_i, ParameterExpression) and not isinstance( - param_expr_i, Parameter): + param_expr_i, Parameter + ): expr_grad_i = _coeff_derivative(param_expr_i, p_i) block[i][i] *= expr_grad_i * expr_grad_i continue @@ -173,11 +177,10 @@ def get_parameter_expression(circuit, param): expr_grad_j = _coeff_derivative(param_expr_j, p_j) block[i][j] *= expr_grad_j - wrapped_block = ListOp([ListOp([ - block[i][j] - for j in range(i, len(params))]) for i in range(len(params))], - combo_fn=triu_to_dense) + wrapped_block = ListOp( + [ListOp([block[i][j] for j in range(i, len(params))]) for i in range(len(params))], + combo_fn=triu_to_dense, + ) blocks.append(wrapped_block) - return ListOp(oplist=blocks, - combo_fn=lambda x: np.real(block_diag(*x))[:, perm][perm, :]) + return ListOp(oplist=blocks, combo_fn=lambda x: np.real(block_diag(*x))[:, perm][perm, :]) diff --git a/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py b/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py index 09ef98336f5e..5fae9bf97054 100644 --- a/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py +++ b/qiskit/opflow/gradients/circuit_qfis/overlap_diag.py @@ -36,10 +36,11 @@ class OverlapDiag(CircuitQFI): See also :class:`~qiskit.opflow.QFI`. """ - def convert(self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ) -> ListOp: + def convert( + self, + operator: Union[CircuitOp, CircuitStateFn], + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state :math:`|\psi(\omega)\rangle` @@ -56,16 +57,17 @@ def convert(self, """ if not isinstance(operator, CircuitStateFn): - raise NotImplementedError('operator must be a CircuitStateFn') + raise NotImplementedError("operator must be a CircuitStateFn") return self._diagonal_approx(operator=operator, params=params) # TODO, for some reason diagonal_approx doesn't use the same get_parameter_expression method. # This should be fixed. - def _diagonal_approx(self, - operator: Union[CircuitOp, CircuitStateFn], - params: Union[ParameterExpression, ParameterVector, List] - ) -> ListOp: + def _diagonal_approx( + self, + operator: Union[CircuitOp, CircuitStateFn], + params: Union[ParameterExpression, ParameterVector, List], + ) -> ListOp: """ Args: operator: The operator corresponding to the quantum state |ψ(ω)〉for which we compute @@ -84,7 +86,7 @@ def _diagonal_approx(self, """ if not isinstance(operator, (CircuitOp, CircuitStateFn)): - raise NotImplementedError('operator must be a CircuitOp or CircuitStateFn') + raise NotImplementedError("operator must be a CircuitOp or CircuitStateFn") # If a single parameter is given wrap it into a list. if isinstance(params, ParameterExpression): @@ -112,15 +114,18 @@ def _diagonal_approx(self, diag = [] for param in params: if len(circuit._parameter_table[param]) > 1: - raise NotImplementedError("OverlapDiag does not yet support multiple " - "gates parameterized by a single parameter. For such " - "circuits use LinCombFull") + raise NotImplementedError( + "OverlapDiag does not yet support multiple " + "gates parameterized by a single parameter. For such " + "circuits use LinCombFull" + ) gate = circuit._parameter_table[param][0][0] if len(gate.params) != 1: - raise TypeError("OverlapDiag cannot yet support gates with more than one " - "parameter.") + raise TypeError( + "OverlapDiag cannot yet support gates with more than one " "parameter." + ) param_value = gate.params[0] generator = generators[param] @@ -142,10 +147,11 @@ def _diagonal_approx(self, def _partition_circuit(circuit): dag = circuit_to_dag(circuit) - dag_layers = ([i['graph'] for i in dag.serial_layers()]) + dag_layers = [i["graph"] for i in dag.serial_layers()] num_qubits = circuit.num_qubits layers = list( - zip(dag_layers, [{x: False for x in range(0, num_qubits)} for layer in dag_layers])) + zip(dag_layers, [{x: False for x in range(0, num_qubits)} for layer in dag_layers]) + ) # initialize the ledger # The ledger tracks which qubits in each layer are available to have @@ -221,14 +227,16 @@ def _get_generators(params, circuit): bit_indices = {bit: index for index, bit in enumerate(circuit.qubits)} for layer in layers: - instr = layer['graph'].op_nodes()[0].op + instr = layer["graph"].op_nodes()[0].op # if no gate is parameterized, skip if not any(isinstance(param, ParameterExpression) for param in instr.params): continue if len(instr.params) != 1: - raise NotImplementedError('The QFI diagonal approximation currently only supports ' - 'gates with a single free parameter.') + raise NotImplementedError( + "The QFI diagonal approximation currently only supports " + "gates with a single free parameter." + ) param_value = instr.params[0] for param in params: @@ -241,11 +249,11 @@ def _get_generators(params, circuit): elif isinstance(instr, RXGate): generator = X else: - raise NotImplementedError(f'Generator for gate {instr.name} not implemented.') + raise NotImplementedError(f"Generator for gate {instr.name} not implemented.") # get all qubit indices in this layer where the param parameterizes # an operation. - indices = [[bit_indices[q] for q in qreg] for qreg in layer['partition']] + indices = [[bit_indices[q] for q in qreg] for qreg in layer["partition"]] indices = [item for sublist in indices for item in sublist] if len(indices) > 1: diff --git a/qiskit/opflow/gradients/derivative_base.py b/qiskit/opflow/gradients/derivative_base.py index 266b32823e4d..c09950720d7b 100644 --- a/qiskit/opflow/gradients/derivative_base.py +++ b/qiskit/opflow/gradients/derivative_base.py @@ -49,11 +49,13 @@ class DerivativeBase(ConverterBase): # pylint: disable=arguments-differ @abstractmethod - def convert(self, - operator: OperatorBase, - params: Optional[Union[ParameterVector, ParameterExpression, - List[ParameterExpression]]] = None - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[ + Union[ParameterVector, ParameterExpression, List[ParameterExpression]] + ] = None, + ) -> OperatorBase: r""" Args: operator: The operator we are taking the gradient, Hessian or QFI of @@ -67,18 +69,21 @@ def convert(self, """ raise NotImplementedError - def gradient_wrapper(self, - operator: OperatorBase, - bind_params: Union[ParameterExpression, ParameterVector, - List[ParameterExpression]], - grad_params: Optional[Union[ParameterExpression, ParameterVector, - List[ParameterExpression], - Tuple[ParameterExpression, - ParameterExpression], - List[Tuple[ParameterExpression, - ParameterExpression]]]] = None, - backend: Optional[Union[BaseBackend, QuantumInstance]] = None) \ - -> Callable[[Iterable], np.ndarray]: + def gradient_wrapper( + self, + operator: OperatorBase, + bind_params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + grad_params: Optional[ + Union[ + ParameterExpression, + ParameterVector, + List[ParameterExpression], + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + ] + ] = None, + backend: Optional[Union[BaseBackend, QuantumInstance]] = None, + ) -> Callable[[Iterable], np.ndarray]: """Get a callable function which provides the respective gradient, Hessian or QFI for given parameter values. This callable can be used as gradient function for optimizers. @@ -95,6 +100,7 @@ def gradient_wrapper(self, takes an iterable as argument which holds the parameter values. """ from ..converters import CircuitSampler + if not grad_params: grad_params = bind_params @@ -113,8 +119,9 @@ def gradient_fn(p_values): return gradient_fn @staticmethod - def parameter_expression_grad(param_expr: ParameterExpression, - param: ParameterExpression) -> Union[ParameterExpression, float]: + def parameter_expression_grad( + param_expr: ParameterExpression, param: ParameterExpression + ) -> Union[ParameterExpression, float]: """Get the derivative of a parameter expression w.r.t. the given parameter. Args: @@ -124,10 +131,14 @@ def parameter_expression_grad(param_expr: ParameterExpression, Returns: ParameterExpression representing the gradient of param_expr w.r.t. param """ - warnings.warn('The DerivativeBase.parameter_expression_grad method is deprecated as of ' - 'Qiskit Terra 0.18.0 and will be removed no earlier than 3 months after ' - 'the release date. Use the ParameterExpression.gradient method instead for ' - 'a direct replacement.', DeprecationWarning, stacklevel=2) + warnings.warn( + "The DerivativeBase.parameter_expression_grad method is deprecated as of " + "Qiskit Terra 0.18.0 and will be removed no earlier than 3 months after " + "the release date. Use the ParameterExpression.gradient method instead for " + "a direct replacement.", + DeprecationWarning, + stacklevel=2, + ) return _coeff_derivative(param_expr, param) @classmethod @@ -178,10 +189,10 @@ def _factor_coeffs_out_of_composed_op(cls, operator: OperatorBase) -> OperatorBa take_norm_of_coeffs = False for k, op in enumerate(operator.oplist): if take_norm_of_coeffs: - total_coeff *= (op.coeff * np.conj(op.coeff)) # type: ignore + total_coeff *= op.coeff * np.conj(op.coeff) # type: ignore else: total_coeff *= op.coeff # type: ignore - if hasattr(op, 'primitive'): + if hasattr(op, "primitive"): prim = op.primitive # type: ignore if isinstance(op, StateFn) and isinstance(prim, TensoredOp): # Check if any of the coefficients in the TensoredOp is a @@ -190,19 +201,21 @@ def _factor_coeffs_out_of_composed_op(cls, operator: OperatorBase) -> OperatorBa # If a coefficient is a ParameterExpression make sure that the # coefficients are pulled together correctly if isinstance(prim_op.coeff, ParameterExpression): - prim_tensored = StateFn(prim.reduce(), - is_measurement=op.is_measurement, - coeff=op.coeff) + prim_tensored = StateFn( + prim.reduce(), is_measurement=op.is_measurement, coeff=op.coeff + ) operator.oplist[k] = prim_tensored return operator.traverse(cls._factor_coeffs_out_of_composed_op) elif isinstance(prim, ListOp): - raise ValueError("This operator was not properly decomposed. " - "By this point, all operator measurements should " - "contain single operators, otherwise the coefficient " - "gradients will not be handled properly.") - if hasattr(prim, 'coeff'): + raise ValueError( + "This operator was not properly decomposed. " + "By this point, all operator measurements should " + "contain single operators, otherwise the coefficient " + "gradients will not be handled properly." + ) + if hasattr(prim, "coeff"): if take_norm_of_coeffs: - total_coeff *= (prim._coeff * np.conj(prim._coeff)) + total_coeff *= prim._coeff * np.conj(prim._coeff) else: total_coeff *= prim._coeff if isinstance(op, OperatorStateFn) and op.is_measurement: diff --git a/qiskit/opflow/gradients/gradient.py b/qiskit/opflow/gradients/gradient.py index e66b37446e56..32e841bfb5c1 100644 --- a/qiskit/opflow/gradients/gradient.py +++ b/qiskit/opflow/gradients/gradient.py @@ -33,6 +33,7 @@ try: from jax import grad, jit + _HAS_JAX = True except ImportError: _HAS_JAX = False @@ -42,11 +43,13 @@ class Gradient(GradientBase): """Convert an operator expression to the first-order gradient.""" # pylint: disable=signature-differs - def convert(self, - operator: OperatorBase, - params: Optional[ - Union[ParameterVector, ParameterExpression, List[ParameterExpression]]] = None - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[ + Union[ParameterVector, ParameterExpression, List[ParameterExpression]] + ] = None, + ) -> OperatorBase: r""" Args: operator: The operator we are taking the gradient of. @@ -66,12 +69,13 @@ def convert(self, params = sorted(operator.parameters, key=functools.cmp_to_key(_compare_parameters)) if isinstance(params, (ParameterVector, list)): param_grads = [self.convert(operator, param) for param in params] - absent_params = [params[i] - for i, grad_ops in enumerate(param_grads) if grad_ops is None] + absent_params = [ + params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None + ] if len(absent_params) > 0: raise ValueError( "The following parameters do not appear in the provided operator: ", - absent_params + absent_params, ) return ListOp(param_grads) @@ -82,10 +86,11 @@ def convert(self, return self.get_gradient(cleaned_op, param) # pylint: disable=too-many-return-statements - def get_gradient(self, - operator: OperatorBase, - params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]] - ) -> OperatorBase: + def get_gradient( + self, + operator: OperatorBase, + params: Union[ParameterExpression, ParameterVector, List[ParameterExpression]], + ) -> OperatorBase: """Get the gradient for the given operator w.r.t. the given parameters Args: @@ -116,12 +121,13 @@ def is_coeff_c(coeff, c): # If get_gradient returns None, then the corresponding parameter was probably not # present in the operator. This needs to be looked at more carefully as other things can # probably trigger a return of None. - absent_params = [params[i] - for i, grad_ops in enumerate(param_grads) if grad_ops is None] + absent_params = [ + params[i] for i, grad_ops in enumerate(param_grads) if grad_ops is None + ] if len(absent_params) > 0: raise ValueError( - 'The following parameters do not appear in the provided operator: ', - absent_params + "The following parameters do not appear in the provided operator: ", + absent_params, ) return ListOp(param_grads) @@ -155,23 +161,28 @@ def is_coeff_c(coeff, c): # Gradient of an expectation value if not is_coeff_c(operator._coeff, 1.0): - raise OpflowError('Operator pre-processing failed. Coefficients were not properly ' - 'collected inside the ComposedOp.') + raise OpflowError( + "Operator pre-processing failed. Coefficients were not properly " + "collected inside the ComposedOp." + ) # Do some checks to make sure operator is sensible # TODO add compatibility with sum of circuit state fns if not isinstance(operator[-1], CircuitStateFn): raise TypeError( - 'The gradient framework is compatible with states that are given as ' - 'CircuitStateFn') + "The gradient framework is compatible with states that are given as " + "CircuitStateFn" + ) return self.grad_method.convert(operator, param) elif isinstance(operator, CircuitStateFn): # Gradient of an a state's sampling probabilities if not is_coeff_c(operator._coeff, 1.0): - raise OpflowError('Operator pre-processing failed. Coefficients were not properly ' - 'collected inside the ComposedOp.') + raise OpflowError( + "Operator pre-processing failed. Coefficients were not properly " + "collected inside the ComposedOp." + ) return self.grad_method.convert(operator, param) # Handle the chain rule @@ -198,11 +209,12 @@ def is_coeff_c(coeff, c): grad_combo_fn = jit(grad(operator._combo_fn, holomorphic=True)) else: raise MissingOptionalLibraryError( - libname='jax', - name='get_gradient', - msg='This automatic differentiation function is based on JAX. ' - 'Please install jax and use `import jax.numpy as jnp` instead ' - 'of `import numpy as np` when defining a combo_fn.') + libname="jax", + name="get_gradient", + msg="This automatic differentiation function is based on JAX. " + "Please install jax and use `import jax.numpy as jnp` instead " + "of `import numpy as np` when defining a combo_fn.", + ) def chain_rule_combo_fn(x): result = np.dot(x[1], x[0]) @@ -210,5 +222,7 @@ def chain_rule_combo_fn(x): result = list(result) return result - return ListOp([ListOp(operator.oplist, combo_fn=grad_combo_fn), ListOp(grad_ops)], - combo_fn=chain_rule_combo_fn) + return ListOp( + [ListOp(operator.oplist, combo_fn=grad_combo_fn), ListOp(grad_ops)], + combo_fn=chain_rule_combo_fn, + ) diff --git a/qiskit/opflow/gradients/gradient_base.py b/qiskit/opflow/gradients/gradient_base.py index 815be9016f97..9a76ec3fd91e 100644 --- a/qiskit/opflow/gradients/gradient_base.py +++ b/qiskit/opflow/gradients/gradient_base.py @@ -24,9 +24,7 @@ class GradientBase(DerivativeBase): # pylint: disable=abstract-method Convert an operator expression to the first-order gradient. """ - def __init__(self, - grad_method: Union[str, CircuitGradient] = 'param_shift', - **kwargs): + def __init__(self, grad_method: Union[str, CircuitGradient] = "param_shift", **kwargs): r""" Args: grad_method: The method used to compute the state/probability gradient. Can be either @@ -40,22 +38,27 @@ def __init__(self, if isinstance(grad_method, CircuitGradient): self._grad_method = grad_method - elif grad_method == 'param_shift': + elif grad_method == "param_shift": from .circuit_gradients.param_shift import ParamShift + self._grad_method = ParamShift(analytic=True) - elif grad_method == 'fin_diff': + elif grad_method == "fin_diff": from .circuit_gradients.param_shift import ParamShift - epsilon = kwargs.get('epsilon', 1e-6) + + epsilon = kwargs.get("epsilon", 1e-6) self._grad_method = ParamShift(analytic=False, epsilon=epsilon) - elif grad_method == 'lin_comb': + elif grad_method == "lin_comb": from .circuit_gradients.lin_comb import LinComb + self._grad_method = LinComb() else: - raise ValueError("Unrecognized input provided for `grad_method`. Please provide" - " a CircuitGradient object or one of the pre-defined string" - " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. ") + raise ValueError( + "Unrecognized input provided for `grad_method`. Please provide" + " a CircuitGradient object or one of the pre-defined string" + " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. " + ) @property def grad_method(self) -> CircuitGradient: diff --git a/qiskit/opflow/gradients/hessian.py b/qiskit/opflow/gradients/hessian.py index c72bfcd3aa21..52b2863119f3 100644 --- a/qiskit/opflow/gradients/hessian.py +++ b/qiskit/opflow/gradients/hessian.py @@ -36,6 +36,7 @@ try: from jax import grad, jit + _HAS_JAX = True except ImportError: _HAS_JAX = False @@ -45,12 +46,18 @@ class Hessian(HessianBase): """Compute the Hessian of an expected value.""" # pylint: disable=signature-differs - def convert(self, - operator: OperatorBase, - params: Optional[Union[Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - List[ParameterExpression], ParameterVector]] = None - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[ + Union[ + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + List[ParameterExpression], + ParameterVector, + ] + ] = None, + ) -> OperatorBase: """ Args: operator: The operator for which we compute the Hessian @@ -70,12 +77,18 @@ def convert(self, return self.get_hessian(cleaned_op, params) # pylint: disable=too-many-return-statements - def get_hessian(self, - operator: OperatorBase, - params: Optional[Union[Tuple[ParameterExpression, ParameterExpression], - List[Tuple[ParameterExpression, ParameterExpression]], - List[ParameterExpression], ParameterVector]] = None - ) -> OperatorBase: + def get_hessian( + self, + operator: OperatorBase, + params: Optional[ + Union[ + Tuple[ParameterExpression, ParameterExpression], + List[Tuple[ParameterExpression, ParameterExpression]], + List[ParameterExpression], + ParameterVector, + ] + ] = None, + ) -> OperatorBase: """Get the Hessian for the given operator w.r.t. the given parameters Args: @@ -106,11 +119,18 @@ def get_hessian(self, if isinstance(params, (ParameterVector, list)): # Case: a list of parameters were given, compute the Hessian for all param pairs if all(isinstance(param, ParameterExpression) for param in params): - return ListOp([ListOp([ - self.get_hessian(operator, (p_i, p_j)) - for i, p_i in enumerate(params[j:], j)]) - for j, p_j in enumerate(params)], - combo_fn=triu_to_dense) + return ListOp( + [ + ListOp( + [ + self.get_hessian(operator, (p_i, p_j)) + for i, p_i in enumerate(params[j:], j) + ] + ) + for j, p_j in enumerate(params) + ], + combo_fn=triu_to_dense, + ) # Case: a list was given containing tuples of parameter pairs. elif all(isinstance(param, tuple) for param in params): # Compute the Hessian entries corresponding to these pairs of parameters. @@ -172,16 +192,19 @@ def is_coeff_c(coeff, c): # and moved out front. if isinstance(operator, ComposedOp): - if not is_coeff_c(operator.coeff, 1.): - raise OpflowError('Operator pre-processing failed. Coefficients were not properly ' - 'collected inside the ComposedOp.') + if not is_coeff_c(operator.coeff, 1.0): + raise OpflowError( + "Operator pre-processing failed. Coefficients were not properly " + "collected inside the ComposedOp." + ) # Do some checks to make sure operator is sensible # TODO enable compatibility with sum of CircuitStateFn operators if not isinstance(operator[-1], CircuitStateFn): raise TypeError( - 'The gradient framework is compatible with states that are given as ' - 'CircuitStateFn') + "The gradient framework is compatible with states that are given as " + "CircuitStateFn" + ) return self.hess_method.convert(operator, params) @@ -207,45 +230,62 @@ def is_coeff_c(coeff, c): # These operators correspond to (d g_i/d θ0)•(d g_i/d θ1) for op in operator.oplist # and params = (θ0,θ1) - d1d0_ops = ListOp([ListOp([Gradient(grad_method=self._hess_method).convert(op, param) - for param in params], combo_fn=np.prod) for - op in operator.oplist]) + d1d0_ops = ListOp( + [ + ListOp( + [ + Gradient(grad_method=self._hess_method).convert(op, param) + for param in params + ], + combo_fn=np.prod, + ) + for op in operator.oplist + ] + ) if operator.grad_combo_fn: first_partial_combo_fn = operator.grad_combo_fn if _HAS_JAX: - second_partial_combo_fn = jit(grad(lambda x: first_partial_combo_fn(x)[0], - holomorphic=True)) + second_partial_combo_fn = jit( + grad(lambda x: first_partial_combo_fn(x)[0], holomorphic=True) + ) else: raise MissingOptionalLibraryError( - libname='jax', - name='get_hessian', - msg='This automatic differentiation function is based on JAX. Please ' - 'install jax and use `import jax.numpy as jnp` instead of ' - '`import numpy as np` when defining a combo_fn.') + libname="jax", + name="get_hessian", + msg="This automatic differentiation function is based on JAX. Please " + "install jax and use `import jax.numpy as jnp` instead of " + "`import numpy as np` when defining a combo_fn.", + ) else: if _HAS_JAX: first_partial_combo_fn = jit(grad(operator.combo_fn, holomorphic=True)) - second_partial_combo_fn = jit(grad(lambda x: first_partial_combo_fn(x)[0], - holomorphic=True)) + second_partial_combo_fn = jit( + grad(lambda x: first_partial_combo_fn(x)[0], holomorphic=True) + ) else: raise MissingOptionalLibraryError( - libname='jax', - name='get_hessian', - msg='This automatic differentiation function is based on JAX. ' - 'Please install jax and use `import jax.numpy as jnp` instead ' - 'of `import numpy as np` when defining a combo_fn.') + libname="jax", + name="get_hessian", + msg="This automatic differentiation function is based on JAX. " + "Please install jax and use `import jax.numpy as jnp` instead " + "of `import numpy as np` when defining a combo_fn.", + ) # For a general combo_fn F(g_0, g_1, ..., g_k) # dF/d θ0,θ1 = sum_i: (∂F/∂g_i)•(d g_i/ d θ0,θ1) + (∂F/∂^2 g_i)•(d g_i/d θ0)•(d g_i/d # θ1) # term1 = (∂F/∂g_i)•(d g_i/ d θ0,θ1) - term1 = ListOp([ListOp(operator.oplist, combo_fn=first_partial_combo_fn), - ListOp(dd_ops)], combo_fn=lambda x: np.dot(x[1], x[0])) + term1 = ListOp( + [ListOp(operator.oplist, combo_fn=first_partial_combo_fn), ListOp(dd_ops)], + combo_fn=lambda x: np.dot(x[1], x[0]), + ) # term2 = (∂F/∂^2 g_i)•(d g_i/d θ0)•(d g_i/d θ1) - term2 = ListOp([ListOp(operator.oplist, combo_fn=second_partial_combo_fn), d1d0_ops], - combo_fn=lambda x: np.dot(x[1], x[0])) + term2 = ListOp( + [ListOp(operator.oplist, combo_fn=second_partial_combo_fn), d1d0_ops], + combo_fn=lambda x: np.dot(x[1], x[0]), + ) return SummedOp([term1, term2]) @@ -254,9 +294,13 @@ def is_coeff_c(coeff, c): return self.hess_method.convert(operator, params) else: - raise TypeError('The computation of Hessians is only supported for Operators which ' - 'represent expectation values or quantum states.') + raise TypeError( + "The computation of Hessians is only supported for Operators which " + "represent expectation values or quantum states." + ) else: - raise TypeError('The computation of Hessians is only supported for Operators which ' - 'represent expectation values.') + raise TypeError( + "The computation of Hessians is only supported for Operators which " + "represent expectation values." + ) diff --git a/qiskit/opflow/gradients/hessian_base.py b/qiskit/opflow/gradients/hessian_base.py index ed58e1160fb0..0ce618dd1daa 100644 --- a/qiskit/opflow/gradients/hessian_base.py +++ b/qiskit/opflow/gradients/hessian_base.py @@ -21,9 +21,7 @@ class HessianBase(DerivativeBase): # pylint: disable=abstract-method """Base class for the Hessian of an expected value.""" - def __init__(self, - hess_method: Union[str, CircuitGradient] = 'param_shift', - **kwargs): + def __init__(self, hess_method: Union[str, CircuitGradient] = "param_shift", **kwargs): r""" Args: hess_method: The method used to compute the state/probability gradient. Can be either @@ -37,23 +35,28 @@ def __init__(self, if isinstance(hess_method, CircuitGradient): self._hess_method = hess_method - elif hess_method == 'param_shift': + elif hess_method == "param_shift": from .circuit_gradients import ParamShift + self._hess_method = ParamShift() - elif hess_method == 'fin_diff': + elif hess_method == "fin_diff": from .circuit_gradients import ParamShift - epsilon = kwargs.get('epsilon', 1e-6) + + epsilon = kwargs.get("epsilon", 1e-6) self._hess_method = ParamShift(analytic=False, epsilon=epsilon) - elif hess_method == 'lin_comb': + elif hess_method == "lin_comb": from .circuit_gradients import LinComb + self._hess_method = LinComb() else: - raise ValueError("Unrecognized input provided for `hess_method`. Please provide" - " a CircuitGradient object or one of the pre-defined string" - " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. ") + raise ValueError( + "Unrecognized input provided for `hess_method`. Please provide" + " a CircuitGradient object or one of the pre-defined string" + " arguments: {'param_shift', 'fin_diff', 'lin_comb'}. " + ) @property def hess_method(self) -> CircuitGradient: diff --git a/qiskit/opflow/gradients/natural_gradient.py b/qiskit/opflow/gradients/natural_gradient.py index dd053a0eca50..f7b226eeba72 100644 --- a/qiskit/opflow/gradients/natural_gradient.py +++ b/qiskit/opflow/gradients/natural_gradient.py @@ -46,11 +46,13 @@ class NaturalGradient(GradientBase): where R(x) represents the penalization term. """ - def __init__(self, - grad_method: Union[str, CircuitGradient] = 'lin_comb', - qfi_method: Union[str, CircuitQFI] = 'lin_comb_full', - regularization: Optional[str] = None, - **kwargs): + def __init__( + self, + grad_method: Union[str, CircuitGradient] = "lin_comb", + qfi_method: Union[str, CircuitQFI] = "lin_comb_full", + regularization: Optional[str] = None, + **kwargs, + ): r""" Args: grad_method: The method used to compute the state gradient. Can be either @@ -69,14 +71,16 @@ def __init__(self, self._qfi_method = QFI(qfi_method) self._regularization = regularization - self._epsilon = kwargs.get('epsilon', 1e-6) + self._epsilon = kwargs.get("epsilon", 1e-6) # pylint: disable=signature-differs - def convert(self, - operator: OperatorBase, - params: Optional[ - Union[ParameterVector, ParameterExpression, List[ParameterExpression]]] = None - ) -> OperatorBase: + def convert( + self, + operator: OperatorBase, + params: Optional[ + Union[ParameterVector, ParameterExpression, List[ParameterExpression]] + ] = None, + ) -> OperatorBase: r""" Args: operator: The operator we are taking the gradient of. @@ -94,14 +98,18 @@ def convert(self, """ if not isinstance(operator, ComposedOp): if not (isinstance(operator, ListOp) and len(operator.oplist) == 1): - raise TypeError('Please provide the operator either as ComposedOp or as ListOp of ' - 'a CircuitStateFn potentially with a combo function.') + raise TypeError( + "Please provide the operator either as ComposedOp or as ListOp of " + "a CircuitStateFn potentially with a combo function." + ) if not isinstance(operator[-1], CircuitStateFn): - raise TypeError('Please make sure that the operator for which you want to compute ' - 'Quantum Fisher Information represents an expectation value or a ' - 'loss function and that the quantum state is given as ' - 'CircuitStateFn.') + raise TypeError( + "Please make sure that the operator for which you want to compute " + "Quantum Fisher Information represents an expectation value or a " + "loss function and that the quantum state is given as " + "CircuitStateFn." + ) if len(operator.parameters) == 0: raise ValueError("The operator we are taking the gradient of is not parameterized!") if params is None: @@ -121,7 +129,8 @@ def combo_fn(x): # If a regularization method is chosen then use a regularized solver to # construct the natural gradient. nat_grad = NaturalGradient._regularized_sle_solver( - a, c, regularization=self.regularization) + a, c, regularization=self.regularization + ) else: try: # Try to solve the system of linear equations Ax = C. @@ -129,6 +138,7 @@ def combo_fn(x): except np.linalg.LinAlgError: # singular matrix nat_grad = np.linalg.lstsq(a, c)[0] return np.real(nat_grad) + # Define the ListOp which combines the gradient and the QFI according to the combination # function defined above. return ListOp([grad, metric], combo_fn=combo_fn) @@ -152,12 +162,14 @@ def regularization(self) -> Optional[str]: return self._regularization @staticmethod - def _reg_term_search(a: np.ndarray, - c: np.ndarray, - reg_method: Callable[[np.ndarray, np.ndarray, float], float], - lambda1: float = 1e-3, - lambda4: float = 1., - tol: float = 1e-8) -> Tuple[float, np.ndarray]: + def _reg_term_search( + a: np.ndarray, + c: np.ndarray, + reg_method: Callable[[np.ndarray, np.ndarray, float], float], + lambda1: float = 1e-3, + lambda4: float = 1.0, + tol: float = 1e-8, + ) -> Tuple[float, np.ndarray]: """ This method implements a search for a regularization parameter lambda by finding for the corner of the L-curve @@ -203,14 +215,15 @@ def _get_curvature(x_lambda: List) -> Union[int, float]: p_temp = 1 c_k = 0 for i in range(3): - p_temp *= (eps[np.mod(i + 1, 3)] - eps[i]) ** 2 + (eta[np.mod(i + 1, 3)] - eta[i]) \ - ** 2 + p_temp *= (eps[np.mod(i + 1, 3)] - eps[i]) ** 2 + ( + eta[np.mod(i + 1, 3)] - eta[i] + ) ** 2 c_k += eps[i] * eta[np.mod(i + 1, 3)] - eps[np.mod(i + 1, 3)] * eta[i] c_k = 2 * c_k / max(1e-4, np.sqrt(p_temp)) return c_k def get_lambda2_lambda3(lambda1, lambda4): - gold_sec = (1 + np.sqrt(5)) / 2. + gold_sec = (1 + np.sqrt(5)) / 2.0 lambda2 = 10 ** ((np.log10(lambda4) + np.log10(lambda1) * gold_sec) / (1 + gold_sec)) lambda3 = 10 ** (np.log10(lambda1) + np.log10(lambda4) - np.log10(lambda2)) return lambda2, lambda3 @@ -258,19 +271,21 @@ def get_lambda2_lambda3(lambda1, lambda4): return lambda_mc, x_mc @staticmethod - def _ridge(a: np.ndarray, - c: np.ndarray, - lambda_: float = 1., - lambda1: float = 1e-4, - lambda4: float = 1e-1, - tol_search: float = 1e-8, - fit_intercept: bool = True, - normalize: bool = False, - copy_a: bool = True, - max_iter: int = 1000, - tol: float = 0.0001, - solver: str = 'auto', - random_state: Optional[int] = None) -> Tuple[float, np.ndarray]: + def _ridge( + a: np.ndarray, + c: np.ndarray, + lambda_: float = 1.0, + lambda1: float = 1e-4, + lambda4: float = 1e-1, + tol_search: float = 1e-8, + fit_intercept: bool = True, + normalize: bool = False, + copy_a: bool = True, + max_iter: int = 1000, + tol: float = 0.0001, + solver: str = "auto", + random_state: Optional[int] = None, + ) -> Tuple[float, np.ndarray]: """ Ridge Regression with automatic search for a good regularization term lambda x_lambda = arg min{||Ax-C||^2 + lambda*||x||_2^2} (3) @@ -302,40 +317,49 @@ def _ridge(a: np.ndarray, from sklearn.linear_model import Ridge except ImportError as ex: raise MissingOptionalLibraryError( - libname='scikit-learn', - name='_ridge', - pip_install='pip install scikit-learn') from ex - - reg = Ridge(alpha=lambda_, fit_intercept=fit_intercept, normalize=normalize, copy_X=copy_a, - max_iter=max_iter, - tol=tol, solver=solver, random_state=random_state) + libname="scikit-learn", name="_ridge", pip_install="pip install scikit-learn" + ) from ex + + reg = Ridge( + alpha=lambda_, + fit_intercept=fit_intercept, + normalize=normalize, + copy_X=copy_a, + max_iter=max_iter, + tol=tol, + solver=solver, + random_state=random_state, + ) def reg_method(a, c, alpha): reg.set_params(alpha=alpha) reg.fit(a, c) return reg.coef_ - lambda_mc, x_mc = NaturalGradient._reg_term_search(a, c, reg_method, lambda1=lambda1, - lambda4=lambda4, tol=tol_search) + lambda_mc, x_mc = NaturalGradient._reg_term_search( + a, c, reg_method, lambda1=lambda1, lambda4=lambda4, tol=tol_search + ) return lambda_mc, np.transpose(x_mc) @staticmethod - def _lasso(a: np.ndarray, - c: np.ndarray, - lambda_: float = 1., - lambda1: float = 1e-4, - lambda4: float = 1e-1, - tol_search: float = 1e-8, - fit_intercept: bool = True, - normalize: bool = False, - precompute: Union[bool, Iterable] = False, - copy_a: bool = True, - max_iter: int = 1000, - tol: float = 0.0001, - warm_start: bool = False, - positive: bool = False, - random_state: Optional[int] = None, - selection: str = 'random') -> Tuple[float, np.ndarray]: + def _lasso( + a: np.ndarray, + c: np.ndarray, + lambda_: float = 1.0, + lambda1: float = 1e-4, + lambda4: float = 1e-1, + tol_search: float = 1e-8, + fit_intercept: bool = True, + normalize: bool = False, + precompute: Union[bool, Iterable] = False, + copy_a: bool = True, + max_iter: int = 1000, + tol: float = 0.0001, + warm_start: bool = False, + positive: bool = False, + random_state: Optional[int] = None, + selection: str = "random", + ) -> Tuple[float, np.ndarray]: """ Lasso Regression with automatic search for a good regularization term lambda x_lambda = arg min{||Ax-C||^2/(2*n_samples) + lambda*||x||_1} (4) @@ -372,35 +396,45 @@ def _lasso(a: np.ndarray, from sklearn.linear_model import Lasso except ImportError as ex: raise MissingOptionalLibraryError( - libname='scikit-learn', - name='_lasso', - pip_install='pip install scikit-learn') from ex - - reg = Lasso(alpha=lambda_, fit_intercept=fit_intercept, normalize=normalize, - precompute=precompute, - copy_X=copy_a, max_iter=max_iter, tol=tol, warm_start=warm_start, - positive=positive, - random_state=random_state, selection=selection) + libname="scikit-learn", name="_lasso", pip_install="pip install scikit-learn" + ) from ex + + reg = Lasso( + alpha=lambda_, + fit_intercept=fit_intercept, + normalize=normalize, + precompute=precompute, + copy_X=copy_a, + max_iter=max_iter, + tol=tol, + warm_start=warm_start, + positive=positive, + random_state=random_state, + selection=selection, + ) def reg_method(a, c, alpha): reg.set_params(alpha=alpha) reg.fit(a, c) return reg.coef_ - lambda_mc, x_mc = NaturalGradient._reg_term_search(a, c, reg_method, lambda1=lambda1, - lambda4=lambda4, tol=tol_search) + lambda_mc, x_mc = NaturalGradient._reg_term_search( + a, c, reg_method, lambda1=lambda1, lambda4=lambda4, tol=tol_search + ) return lambda_mc, x_mc @staticmethod - def _regularized_sle_solver(a: np.ndarray, - c: np.ndarray, - regularization: str = 'perturb_diag', - lambda1: float = 1e-3, - lambda4: float = 1., - alpha: float = 0., - tol_norm_x: Tuple[float, float] = (1e-8, 5.), - tol_cond_a: float = 1000.) -> np.ndarray: + def _regularized_sle_solver( + a: np.ndarray, + c: np.ndarray, + regularization: str = "perturb_diag", + lambda1: float = 1e-3, + lambda4: float = 1.0, + alpha: float = 0.0, + tol_norm_x: Tuple[float, float] = (1e-8, 5.0), + tol_cond_a: float = 1000.0, + ) -> np.ndarray: """ Solve a linear system of equations with a regularization method and automatic lambda fitting Args: @@ -418,17 +452,17 @@ def _regularized_sle_solver(a: np.ndarray, solution to the regularized system of linear equations """ - if regularization == 'ridge': + if regularization == "ridge": _, x = NaturalGradient._ridge(a, c, lambda1=lambda1) - elif regularization == 'lasso': + elif regularization == "lasso": _, x = NaturalGradient._lasso(a, c, lambda1=lambda1) - elif regularization == 'perturb_diag_elements': + elif regularization == "perturb_diag_elements": alpha = 1e-7 while np.linalg.cond(a + alpha * np.diag(a)) > tol_cond_a: alpha *= 10 # include perturbation in A to avoid singularity x, _, _, _ = np.linalg.lstsq(a + alpha * np.diag(a), c, rcond=None) - elif regularization == 'perturb_diag': + elif regularization == "perturb_diag": alpha = 1e-7 while np.linalg.cond(a + alpha * np.eye(len(c))) > tol_cond_a: alpha *= 10 @@ -439,13 +473,13 @@ def _regularized_sle_solver(a: np.ndarray, x, _, _, _ = np.linalg.lstsq(a, c, rcond=None) if np.linalg.norm(x) > tol_norm_x[1] or np.linalg.norm(x) < tol_norm_x[0]: - if regularization == 'ridge': - lambda1 = lambda1 / 10. + if regularization == "ridge": + lambda1 = lambda1 / 10.0 _, x = NaturalGradient._ridge(a, c, lambda1=lambda1, lambda4=lambda4) - elif regularization == 'lasso': - lambda1 = lambda1 / 10. + elif regularization == "lasso": + lambda1 = lambda1 / 10.0 _, x = NaturalGradient._lasso(a, c, lambda1=lambda1) - elif regularization == 'perturb_diag_elements': + elif regularization == "perturb_diag_elements": while np.linalg.cond(a + alpha * np.diag(a)) > tol_cond_a: if alpha == 0: alpha = 1e-7 diff --git a/qiskit/opflow/gradients/qfi.py b/qiskit/opflow/gradients/qfi.py index 8828b3ee1651..b840bcf7511b 100644 --- a/qiskit/opflow/gradients/qfi.py +++ b/qiskit/opflow/gradients/qfi.py @@ -16,7 +16,7 @@ import functools from qiskit.circuit.quantumcircuit import _compare_parameters -from qiskit.circuit import (ParameterExpression, ParameterVector) +from qiskit.circuit import ParameterExpression, ParameterVector from ..list_ops.list_op import ListOp from ..expectations.pauli_expectation import PauliExpectation from ..state_fns.circuit_state_fn import CircuitStateFn @@ -36,11 +36,13 @@ class QFI(QFIBase): """ # pylint: disable=signature-differs - def convert(self, - operator: CircuitStateFn, - params: Optional[ - Union[ParameterExpression, ParameterVector, List[ParameterExpression]]] = None - ) -> ListOp: + def convert( + self, + operator: CircuitStateFn, + params: Optional[ + Union[ParameterExpression, ParameterVector, List[ParameterExpression]] + ] = None, + ) -> ListOp: r""" Args: operator: The operator corresponding to the quantum state \|ψ(ω)〉for which we compute diff --git a/qiskit/opflow/gradients/qfi_base.py b/qiskit/opflow/gradients/qfi_base.py index f50638d13fe6..e370aac20069 100644 --- a/qiskit/opflow/gradients/qfi_base.py +++ b/qiskit/opflow/gradients/qfi_base.py @@ -29,8 +29,7 @@ class QFIBase(DerivativeBase): # pylint: disable=abstract-method [QFI]kl= Re[〈∂kψ|∂lψ〉−〈∂kψ|ψ〉〈ψ|∂lψ〉] * 4. """ - def __init__(self, - qfi_method: Union[str, CircuitQFI] = 'lin_comb_full'): + def __init__(self, qfi_method: Union[str, CircuitQFI] = "lin_comb_full"): r""" Args: qfi_method: The method used to compute the state/probability gradient. Can be either @@ -44,20 +43,25 @@ def __init__(self, if isinstance(qfi_method, CircuitQFI): self._qfi_method = qfi_method - elif qfi_method == 'lin_comb_full': + elif qfi_method == "lin_comb_full": from .circuit_qfis import LinCombFull + self._qfi_method = LinCombFull() - elif qfi_method == 'overlap_block_diag': + elif qfi_method == "overlap_block_diag": from .circuit_qfis import OverlapBlockDiag + self._qfi_method = OverlapBlockDiag() - elif qfi_method == 'overlap_diag': + elif qfi_method == "overlap_diag": from .circuit_qfis import OverlapDiag + self._qfi_method = OverlapDiag() else: - raise ValueError("Unrecognized input provided for `qfi_method`. Please provide" - " a CircuitQFI object or one of the pre-defined string" - " arguments: {'lin_comb_full', 'overlap_diag', " - "'overlap_block_diag'}. ") + raise ValueError( + "Unrecognized input provided for `qfi_method`. Please provide" + " a CircuitQFI object or one of the pre-defined string" + " arguments: {'lin_comb_full', 'overlap_diag', " + "'overlap_block_diag'}. " + ) @property def qfi_method(self) -> CircuitQFI: diff --git a/qiskit/opflow/list_ops/__init__.py b/qiskit/opflow/list_ops/__init__.py index 095ba4613fa7..9c27fab646b7 100644 --- a/qiskit/opflow/list_ops/__init__.py +++ b/qiskit/opflow/list_ops/__init__.py @@ -86,7 +86,4 @@ from .composed_op import ComposedOp from .tensored_op import TensoredOp -__all__ = ['ListOp', - 'SummedOp', - 'TensoredOp', - 'ComposedOp'] +__all__ = ["ListOp", "SummedOp", "TensoredOp", "ComposedOp"] diff --git a/qiskit/opflow/list_ops/composed_op.py b/qiskit/opflow/list_ops/composed_op.py index a73e722249da..c127a813c52c 100644 --- a/qiskit/opflow/list_ops/composed_op.py +++ b/qiskit/opflow/list_ops/composed_op.py @@ -26,26 +26,25 @@ class ComposedOp(ListOp): - """ A class for lazily representing compositions of Operators. Often Operators cannot be + """A class for lazily representing compositions of Operators. Often Operators cannot be efficiently composed with one another, but may be manipulated further so that they can be composed later. This class holds logic to indicate that the Operators in ``oplist`` are meant to be composed, and therefore if they reach a point in which they can be, such as after - conversion to QuantumCircuits or matrices, they can be reduced by composition. """ - - def __init__(self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False) -> None: + conversion to QuantumCircuits or matrices, they can be reduced by composition.""" + + def __init__( + self, + oplist: List[OperatorBase], + coeff: Union[complex, ParameterExpression] = 1.0, + abelian: bool = False, + ) -> None: """ Args: oplist: The Operators being composed. coeff: A coefficient multiplying the operator abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. """ - super().__init__(oplist, - combo_fn=partial(reduce, np.dot), - coeff=coeff, - abelian=abelian) + super().__init__(oplist, combo_fn=partial(reduce, np.dot), coeff=coeff, abelian=abelian) @property def num_qubits(self) -> int: @@ -72,17 +71,21 @@ def to_circuit(self) -> QuantumCircuit: # pylint: disable=cyclic-import from ..state_fns.circuit_state_fn import CircuitStateFn from ..primitive_ops.primitive_op import PrimitiveOp + circuit_op = self.to_circuit_op() if isinstance(circuit_op, (PrimitiveOp, CircuitStateFn)): return circuit_op.to_circuit() - raise OpflowError('Conversion to_circuit supported only for operators, where a single ' - 'underlying circuit can be produced.') + raise OpflowError( + "Conversion to_circuit supported only for operators, where a single " + "underlying circuit can be produced." + ) def adjoint(self) -> "ComposedOp": return ComposedOp([op.adjoint() for op in reversed(self.oplist)], coeff=self.coeff) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(ComposedOp, new_self) @@ -134,7 +137,7 @@ def tree_recursive_eval(r, l_arg): # Try collapsing list or trees of compositions into a single . def non_distributive_reduce(self) -> OperatorBase: - """ Reduce without attempting to expand all distributive compositions. + """Reduce without attempting to expand all distributive compositions. Returns: The reduced Operator. @@ -155,7 +158,8 @@ def distribute_compose(l_arg, r): if isinstance(l_arg, ListOp) and l_arg.distributive: # Either ListOp or SummedOp, returns correct type return l_arg.__class__( - [distribute_compose(l_op * l_arg.coeff, r) for l_op in l_arg.oplist]) + [distribute_compose(l_op * l_arg.coeff, r) for l_op in l_arg.oplist] + ) if isinstance(r, ListOp) and r.distributive: return r.__class__([distribute_compose(l_arg, r_op * r.coeff) for r_op in r.oplist]) else: diff --git a/qiskit/opflow/list_ops/list_op.py b/qiskit/opflow/list_ops/list_op.py index 393ad97b6ca4..9dec9a2cb196 100644 --- a/qiskit/opflow/list_ops/list_op.py +++ b/qiskit/opflow/list_ops/list_op.py @@ -53,12 +53,14 @@ class ListOp(OperatorBase): multiple dimensional lists. """ - def __init__(self, - oplist: Sequence[OperatorBase], - combo_fn: Callable = lambda x: x, - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False, - grad_combo_fn: Optional[Callable] = None) -> None: + def __init__( + self, + oplist: Sequence[OperatorBase], + combo_fn: Callable = lambda x: x, + coeff: Union[complex, ParameterExpression] = 1.0, + abelian: bool = False, + grad_combo_fn: Optional[Callable] = None, + ) -> None: """ Args: oplist: The list of ``OperatorBases`` defining this Operator's underlying function. @@ -83,23 +85,25 @@ def _check_input_types(self, oplist): return list(oplist) else: badval = next(x for x in oplist if not isinstance(x, OperatorBase)) - raise TypeError(f'ListOp expecting objects of type OperatorBase, got {badval}') + raise TypeError(f"ListOp expecting objects of type OperatorBase, got {badval}") - def _state(self, - coeff: Optional[Union[complex, ParameterExpression]] = None, - combo_fn: Optional[Callable] = None, - abelian: Optional[bool] = None, - grad_combo_fn: Optional[Callable] = None) -> Dict: + def _state( + self, + coeff: Optional[Union[complex, ParameterExpression]] = None, + combo_fn: Optional[Callable] = None, + abelian: Optional[bool] = None, + grad_combo_fn: Optional[Callable] = None, + ) -> Dict: return { - 'coeff': coeff if coeff is not None else self.coeff, - 'combo_fn': combo_fn if combo_fn is not None else self.combo_fn, - 'abelian': abelian if abelian is not None else self.abelian, - 'grad_combo_fn': grad_combo_fn if grad_combo_fn is not None else self.grad_combo_fn + "coeff": coeff if coeff is not None else self.coeff, + "combo_fn": combo_fn if combo_fn is not None else self.combo_fn, + "abelian": abelian if abelian is not None else self.abelian, + "grad_combo_fn": grad_combo_fn if grad_combo_fn is not None else self.grad_combo_fn, } @property def oplist(self) -> List[OperatorBase]: - """ The list of ``OperatorBases`` defining the underlying function of this + """The list of ``OperatorBases`` defining the underlying function of this Operator. Returns: @@ -109,7 +113,7 @@ def oplist(self) -> List[OperatorBase]: @property def combo_fn(self) -> Callable: - """ The function defining how to combine ``oplist`` (or Numbers, or NumPy arrays) to + """The function defining how to combine ``oplist`` (or Numbers, or NumPy arrays) to produce the Operator's underlying function. For example, SummedOp's combination function is to add all of the Operators in ``oplist``. @@ -120,12 +124,12 @@ def combo_fn(self) -> Callable: @property def grad_combo_fn(self) -> Optional[Callable]: - """ The gradient of ``combo_fn``. """ + """The gradient of ``combo_fn``.""" return self._grad_combo_fn @property def abelian(self) -> bool: - """ Whether the Operators in ``oplist`` are known to commute with one another. + """Whether the Operators in ``oplist`` are known to commute with one another. Returns: A bool indicating whether the ``oplist`` is Abelian. @@ -134,7 +138,7 @@ def abelian(self) -> bool: @property def distributive(self) -> bool: - """ Indicates whether the ListOp or subclass is distributive under composition. + """Indicates whether the ListOp or subclass is distributive under composition. ListOp and SummedOp are, meaning that (opv @ op) = (opv[0] @ op + opv[1] @ op) (using plus for SummedOp, list for ListOp, etc.), while ComposedOp and TensoredOp do not behave this way. @@ -146,7 +150,7 @@ def distributive(self) -> bool: @property def coeff(self) -> Union[complex, ParameterExpression]: - """ The scalar coefficient multiplying the Operator. + """The scalar coefficient multiplying the Operator. Returns: The coefficient. @@ -160,7 +164,7 @@ def primitive_strings(self) -> Set[str]: def num_qubits(self) -> int: num_qubits0 = self.oplist[0].num_qubits if not all(num_qubits0 == op.num_qubits for op in self.oplist): - raise ValueError('Operators in ListOp have differing numbers of qubits.') + raise ValueError("Operators in ListOp have differing numbers of qubits.") return num_qubits0 def add(self, other: OperatorBase) -> "ListOp": @@ -170,45 +174,49 @@ def add(self, other: OperatorBase) -> "ListOp": # Avoid circular dependency # pylint: disable=cyclic-import from .summed_op import SummedOp + return SummedOp([self, other]) def adjoint(self) -> "ListOp": # TODO do this lazily? Basically rebuilds the entire tree, and ops and adjoints almost # always come in pairs, so an AdjointOp holding a reference could save copying. if self.__class__ == ListOp: - return ListOp([op.adjoint() for op in self.oplist], - **self._state(coeff=self.coeff.conjugate())) # coeff is conjugated - return self.__class__([op.adjoint() for op in self.oplist], - coeff=self.coeff.conjugate(), abelian=self.abelian) - - def traverse(self, - convert_fn: Callable, - coeff: Optional[Union[complex, ParameterExpression]] = None) -> "ListOp": + return ListOp( + [op.adjoint() for op in self.oplist], **self._state(coeff=self.coeff.conjugate()) + ) # coeff is conjugated + return self.__class__( + [op.adjoint() for op in self.oplist], coeff=self.coeff.conjugate(), abelian=self.abelian + ) + + def traverse( + self, convert_fn: Callable, coeff: Optional[Union[complex, ParameterExpression]] = None + ) -> "ListOp": """Apply the convert_fn to each node in the oplist. - Args: - convert_fn: The function to apply to the internal OperatorBase. - coeff: A coefficient to multiply by after applying convert_fn. - If it is None, self.coeff is used instead. + Args: + convert_fn: The function to apply to the internal OperatorBase. + coeff: A coefficient to multiply by after applying convert_fn. + If it is None, self.coeff is used instead. - Returns: - The converted ListOp. + Returns: + The converted ListOp. """ if coeff is None: coeff = self.coeff if self.__class__ == ListOp: - return ListOp([convert_fn(op) for op in self.oplist], - **self._state(coeff=coeff)) - return self.__class__([convert_fn(op) for op in self.oplist], - coeff=coeff, abelian=self.abelian) + return ListOp([convert_fn(op) for op in self.oplist], **self._state(coeff=coeff)) + return self.__class__( + [convert_fn(op) for op in self.oplist], coeff=coeff, abelian=self.abelian + ) def equals(self, other: OperatorBase) -> bool: if not isinstance(other, type(self)) or not len(self.oplist) == len(other.oplist): return False # Note, ordering matters here (i.e. different list orders will return False) return self.coeff == other.coeff and all( - op1 == op2 for op1, op2 in zip(self.oplist, other.oplist)) + op1 == op2 for op1, op2 in zip(self.oplist, other.oplist) + ) # We need to do this because otherwise Numpy takes over scalar multiplication and wrecks it if # isinstance(scalar, np.number) - this started happening when we added __get_item__(). @@ -216,8 +224,10 @@ def equals(self, other: OperatorBase) -> bool: def mul(self, scalar: Union[complex, ParameterExpression]) -> "ListOp": if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError('Operators can only be scalar multiplied by float or complex, not ' - '{} of type {}.'.format(scalar, type(scalar))) + raise ValueError( + "Operators can only be scalar multiplied by float or complex, not " + "{} of type {}.".format(scalar, type(scalar)) + ) if self.__class__ == ListOp: return ListOp(self.oplist, **self._state(coeff=scalar * self.coeff)) return self.__class__(self.oplist, coeff=scalar * self.coeff, abelian=self.abelian) @@ -226,6 +236,7 @@ def tensor(self, other: OperatorBase) -> OperatorBase: # Avoid circular dependency # pylint: disable=cyclic-import from .tensored_op import TensoredOp + return TensoredOp([self, other]) def tensorpower(self, other: int) -> Union[OperatorBase, int]: @@ -233,19 +244,21 @@ def tensorpower(self, other: int) -> Union[OperatorBase, int]: if other == 0: return 1 if not isinstance(other, int) or other <= 0: - raise TypeError('Tensorpower can only take positive int arguments') + raise TypeError("Tensorpower can only take positive int arguments") # Avoid circular dependency # pylint: disable=cyclic-import from .tensored_op import TensoredOp + return TensoredOp([self] * other) - def _expand_dim(self, num_qubits: int) -> 'ListOp': - oplist = [op._expand_dim(num_qubits + self.num_qubits - op.num_qubits) - for op in self.oplist] + def _expand_dim(self, num_qubits: int) -> "ListOp": + oplist = [ + op._expand_dim(num_qubits + self.num_qubits - op.num_qubits) for op in self.oplist + ] return ListOp(oplist, **self._state()) - def permute(self, permutation: List[int]) -> 'OperatorBase': + def permute(self, permutation: List[int]) -> "OperatorBase": """Permute the qubits of the operator. Args: @@ -265,15 +278,18 @@ def permute(self, permutation: List[int]) -> 'OperatorBase': if self.num_qubits != len(permutation): raise OpflowError("New index must be defined for each qubit of the operator.") except ValueError: - raise OpflowError("Permute is only possible if all operators in the ListOp have the " - "same number of qubits.") from ValueError + raise OpflowError( + "Permute is only possible if all operators in the ListOp have the " + "same number of qubits." + ) from ValueError if self.num_qubits < circuit_size: # pad the operator with identities new_self = self._expand_dim(circuit_size - self.num_qubits) qc = QuantumCircuit(circuit_size) # extend the indices to match the size of the circuit - permutation \ - = list(filter(lambda x: x not in permutation, range(circuit_size))) + permutation + permutation = ( + list(filter(lambda x: x not in permutation, range(circuit_size))) + permutation + ) # decompose permutation into sequence of transpositions transpositions = arithmetic.transpositions(permutation) @@ -285,8 +301,9 @@ def permute(self, permutation: List[int]) -> 'OperatorBase': return CircuitOp(qc.reverse_ops()) @ new_self @ CircuitOp(qc) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(ListOp, new_self) @@ -296,27 +313,31 @@ def compose(self, other: OperatorBase, # Avoid circular dependency # pylint: disable=cyclic-import from .composed_op import ComposedOp + return ComposedOp([new_self, other]) def power(self, exponent: int) -> OperatorBase: if not isinstance(exponent, int) or exponent <= 0: - raise TypeError('power can only take positive int arguments') + raise TypeError("power can only take positive int arguments") # Avoid circular dependency # pylint: disable=cyclic-import from .composed_op import ComposedOp + return ComposedOp([self] * exponent) def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) # Combination function must be able to handle classical values. # Note: this can end up, when we have list operators containing other list operators, as a # ragged array and numpy 1.19 raises a deprecation warning unless this is explicitly # done as object type now - was implicit before. mat = self.combo_fn( - np.asarray([op.to_matrix(massive=massive) * self.coeff for op in self.oplist], - dtype=object)) + np.asarray( + [op.to_matrix(massive=massive) * self.coeff for op in self.oplist], dtype=object + ) + ) # Note: As ComposedOp has a combo function of inner product we can end up here not with # a matrix (array) but a scalar. In which case we make a single element array of it. if isinstance(mat, Number): @@ -324,15 +345,14 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: return np.asarray(mat, dtype=complex) def to_spmatrix(self) -> Union[spmatrix, List[spmatrix]]: - """ Returns SciPy sparse matrix representation of the Operator. + """Returns SciPy sparse matrix representation of the Operator. Returns: CSR sparse matrix representation of the Operator, or List thereof. """ # Combination function must be able to handle classical values - return self.combo_fn( - [op.to_spmatrix() for op in self.oplist]) * self.coeff + return self.combo_fn([op.to_spmatrix() for op in self.oplist]) * self.coeff def eval( self, @@ -378,21 +398,26 @@ def eval( # The below code only works for distributive ListOps, e.g. ListOp and SummedOp if not self.distributive: - raise NotImplementedError("ListOp's eval function is only defined for distributive " - "ListOps.") + raise NotImplementedError( + "ListOp's eval function is only defined for distributive " "ListOps." + ) evals = [op.eval(front) for op in self.oplist] # Handle application of combo_fn for DictStateFn resp VectorStateFn operators if self._combo_fn != ListOp([])._combo_fn: - if all(isinstance(op, DictStateFn) for op in evals) or \ - all(isinstance(op, VectorStateFn) for op in evals) or \ - all(isinstance(op, SparseVectorStateFn) for op in evals): + if ( + all(isinstance(op, DictStateFn) for op in evals) + or all(isinstance(op, VectorStateFn) for op in evals) + or all(isinstance(op, SparseVectorStateFn) for op in evals) + ): if not all( op.is_measurement == evals[0].is_measurement for op in evals # type: ignore ): - raise NotImplementedError("Combo_fn not yet supported for mixed measurement " - "and non-measurement StateFns") + raise NotImplementedError( + "Combo_fn not yet supported for mixed measurement " + "and non-measurement StateFns" + ) result = self.combo_fn(evals) if isinstance(result, list): multiplied = self.coeff * np.array(result) @@ -402,7 +427,7 @@ def eval( if all(isinstance(op, OperatorBase) for op in evals): return self.__class__(evals) # type: ignore elif any(isinstance(op, OperatorBase) for op in evals): - raise TypeError('Cannot handle mixed scalar and Operator eval results.') + raise TypeError("Cannot handle mixed scalar and Operator eval results.") else: result = self.combo_fn(evals) if isinstance(result, list): @@ -411,14 +436,16 @@ def eval( return self.coeff * result def exp_i(self) -> OperatorBase: - """ Return an ``OperatorBase`` equivalent to an exponentiation of self * -i, e^(-i*op).""" + """Return an ``OperatorBase`` equivalent to an exponentiation of self * -i, e^(-i*op).""" # pylint: disable=unidiomatic-typecheck if type(self) == ListOp: - return ListOp([op.exp_i() for op in self.oplist], # type: ignore - **self._state(abelian=False)) + return ListOp( + [op.exp_i() for op in self.oplist], **self._state(abelian=False) # type: ignore + ) # pylint: disable=cyclic-import from ..evolutions.evolved_op import EvolvedOp + return EvolvedOp(self) def log_i(self, massive: bool = False) -> OperatorBase: @@ -428,27 +455,28 @@ def log_i(self, massive: bool = False) -> OperatorBase: to all ops in oplist. """ if self.__class__.__name__ == ListOp.__name__: - return ListOp([op.log_i(massive=massive) for op in self.oplist], # type: ignore - **self._state(abelian=False)) + return ListOp( + [op.log_i(massive=massive) for op in self.oplist], # type: ignore + **self._state(abelian=False), + ) return self.to_matrix_op(massive=massive).log_i(massive=massive) def __str__(self) -> str: - content_string = ',\n'.join([str(op) for op in self.oplist]) + content_string = ",\n".join([str(op) for op in self.oplist]) main_string = "{}([\n{}\n])".format( - self.__class__.__name__, - self._indent(content_string, indentation=self.INDENTATION)) + self.__class__.__name__, self._indent(content_string, indentation=self.INDENTATION) + ) if self.abelian: - main_string = 'Abelian' + main_string + main_string = "Abelian" + main_string if self.coeff != 1.0: - main_string = '{} * '.format(self.coeff) + main_string + main_string = "{} * ".format(self.coeff) + main_string return main_string def __repr__(self) -> str: - return "{}({}, coeff={}, abelian={})".format(self.__class__.__name__, - repr(self.oplist), - self.coeff, - self.abelian) + return "{}({}, coeff={}, abelian={})".format( + self.__class__.__name__, repr(self.oplist), self.coeff, self.abelian + ) @property def parameters(self): @@ -477,8 +505,8 @@ def reduce(self) -> OperatorBase: return self.__class__(reduced_ops, coeff=self.coeff, abelian=self.abelian) def to_matrix_op(self, massive: bool = False) -> "ListOp": - """ Returns an equivalent Operator composed of only NumPy-based primitives, such as - ``MatrixOp`` and ``VectorStateFn``. """ + """Returns an equivalent Operator composed of only NumPy-based primitives, such as + ``MatrixOp`` and ``VectorStateFn``.""" if self.__class__ == ListOp: return cast( ListOp, @@ -496,33 +524,54 @@ def to_matrix_op(self, massive: bool = False) -> "ListOp": ) def to_circuit_op(self) -> OperatorBase: - """ Returns an equivalent Operator composed of only QuantumCircuit-based primitives, - such as ``CircuitOp`` and ``CircuitStateFn``. """ + """Returns an equivalent Operator composed of only QuantumCircuit-based primitives, + such as ``CircuitOp`` and ``CircuitStateFn``.""" # pylint: disable=cyclic-import from ..state_fns.operator_state_fn import OperatorStateFn + if self.__class__ == ListOp: - return ListOp([op.to_circuit_op() - if not isinstance(op, OperatorStateFn) else op - for op in self.oplist], **self._state()).reduce() - return self.__class__([op.to_circuit_op() - if not isinstance(op, OperatorStateFn) else op - for op in self.oplist], - coeff=self.coeff, abelian=self.abelian).reduce() + return ListOp( + [ + op.to_circuit_op() if not isinstance(op, OperatorStateFn) else op + for op in self.oplist + ], + **self._state(), + ).reduce() + return self.__class__( + [ + op.to_circuit_op() if not isinstance(op, OperatorStateFn) else op + for op in self.oplist + ], + coeff=self.coeff, + abelian=self.abelian, + ).reduce() def to_pauli_op(self, massive: bool = False) -> "ListOp": - """ Returns an equivalent Operator composed of only Pauli-based primitives, - such as ``PauliOp``. """ + """Returns an equivalent Operator composed of only Pauli-based primitives, + such as ``PauliOp``.""" # pylint: disable=cyclic-import from ..state_fns.state_fn import StateFn + if self.__class__ == ListOp: - return ListOp([op.to_pauli_op(massive=massive) # type: ignore - if not isinstance(op, StateFn) else op - for op in self.oplist], **self._state()).reduce() - return self.__class__([op.to_pauli_op(massive=massive) # type: ignore - if not isinstance(op, StateFn) else op - for op in self.oplist], - coeff=self.coeff, abelian=self.abelian - ).reduce() + return ListOp( + [ + op.to_pauli_op(massive=massive) # type: ignore + if not isinstance(op, StateFn) + else op + for op in self.oplist + ], + **self._state(), + ).reduce() + return self.__class__( + [ + op.to_pauli_op(massive=massive) # type: ignore + if not isinstance(op, StateFn) + else op + for op in self.oplist + ], + coeff=self.coeff, + abelian=self.abelian, + ).reduce() def _is_empty(self): return len(self.oplist) == 0 @@ -530,7 +579,7 @@ def _is_empty(self): # Array operations: def __getitem__(self, offset: Union[int, slice]) -> OperatorBase: - """ Allows array-indexing style access to the Operators in ``oplist``. + """Allows array-indexing style access to the Operators in ``oplist``. Args: offset: The index of ``oplist`` desired. @@ -545,12 +594,10 @@ def __getitem__(self, offset: Union[int, slice]) -> OperatorBase: if self.__class__ == ListOp: return ListOp(oplist=self._oplist[offset], **self._state()) - return self.__class__(oplist=self._oplist[offset], - coeff=self._coeff, - abelian=self._abelian) + return self.__class__(oplist=self._oplist[offset], coeff=self._coeff, abelian=self._abelian) def __iter__(self) -> Iterator: - """ Returns an iterator over the operators in ``oplist``. + """Returns an iterator over the operators in ``oplist``. Returns: An iterator over the operators in ``oplist`` @@ -558,7 +605,7 @@ def __iter__(self) -> Iterator: return iter(self.oplist) def __len__(self) -> int: - """ Length of ``oplist``. + """Length of ``oplist``. Returns: An int equal to the length of ``oplist``. diff --git a/qiskit/opflow/list_ops/summed_op.py b/qiskit/opflow/list_ops/summed_op.py index 11863f4ab34a..8760ff595943 100644 --- a/qiskit/opflow/list_ops/summed_op.py +++ b/qiskit/opflow/list_ops/summed_op.py @@ -24,26 +24,25 @@ class SummedOp(ListOp): - """ A class for lazily representing sums of Operators. Often Operators cannot be + """A class for lazily representing sums of Operators. Often Operators cannot be efficiently added to one another, but may be manipulated further so that they can be later. This class holds logic to indicate that the Operators in ``oplist`` are meant to be added together, and therefore if they reach a point in which they can be, such as after - evaluation or conversion to matrices, they can be reduced by addition. """ - - def __init__(self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False) -> None: + evaluation or conversion to matrices, they can be reduced by addition.""" + + def __init__( + self, + oplist: List[OperatorBase], + coeff: Union[complex, ParameterExpression] = 1.0, + abelian: bool = False, + ) -> None: """ Args: oplist: The Operators being summed. coeff: A coefficient multiplying the operator abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. """ - super().__init__(oplist, - combo_fn=lambda x: np.sum(x, axis=0), - coeff=coeff, - abelian=abelian) + super().__init__(oplist, combo_fn=lambda x: np.sum(x, axis=0), coeff=coeff, abelian=abelian) @property def num_qubits(self) -> int: @@ -68,16 +67,18 @@ def add(self, other: OperatorBase) -> "SummedOp": Returns: A ``SummedOp`` equivalent to the sum of self and other. """ - self_new_ops = self.oplist if self.coeff == 1 \ - else [op.mul(self.coeff) for op in self.oplist] + self_new_ops = ( + self.oplist if self.coeff == 1 else [op.mul(self.coeff) for op in self.oplist] + ) if isinstance(other, SummedOp): - other_new_ops = other.oplist if other.coeff == 1 \ - else [op.mul(other.coeff) for op in other.oplist] + other_new_ops = ( + other.oplist if other.coeff == 1 else [op.mul(other.coeff) for op in other.oplist] + ) else: other_new_ops = [other] return SummedOp(self_new_ops + other_new_ops) - def collapse_summands(self) -> 'SummedOp': + def collapse_summands(self) -> "SummedOp": """Return Operator by simplifying duplicate operators. E.g., ``SummedOp([2 * X ^ Y, X ^ Y]).collapse_summands() -> SummedOp([3 * X ^ Y])``. @@ -87,6 +88,7 @@ def collapse_summands(self) -> 'SummedOp': """ # pylint: disable=cyclic-import from ..primitive_ops.primitive_op import PrimitiveOp + oplist = [] # type: List[OperatorBase] coeffs = [] # type: List[Union[int, float, complex, ParameterExpression]] for op in self.oplist: @@ -130,6 +132,7 @@ def reduce(self) -> OperatorBase: # pylint: disable=cyclic-import from ..primitive_ops.pauli_sum_op import PauliSumOp + if isinstance(reduced_ops, PauliSumOp): reduced_ops = reduced_ops.reduce() @@ -156,15 +159,18 @@ def to_circuit(self) -> QuantumCircuit: """ # pylint: disable=cyclic-import from ..primitive_ops.matrix_op import MatrixOp + matrix_op = self.to_matrix_op() if isinstance(matrix_op, MatrixOp): return matrix_op.to_circuit() - raise OpflowError("The SummedOp can not be converted to circuit, because to_matrix_op did " - "not return a MatrixOp.") + raise OpflowError( + "The SummedOp can not be converted to circuit, because to_matrix_op did " + "not return a MatrixOp." + ) def to_matrix_op(self, massive: bool = False) -> "SummedOp": - """ Returns an equivalent Operator composed of only NumPy-based primitives, such as - ``MatrixOp`` and ``VectorStateFn``. """ + """Returns an equivalent Operator composed of only NumPy-based primitives, such as + ``MatrixOp`` and ``VectorStateFn``.""" accum = self.oplist[0].to_matrix_op(massive=massive) for i in range(1, len(self.oplist)): accum += self.oplist[i].to_matrix_op(massive=massive) @@ -174,6 +180,7 @@ def to_matrix_op(self, massive: bool = False) -> "SummedOp": def to_pauli_op(self, massive: bool = False) -> "SummedOp": # pylint: disable=cyclic-import from ..state_fns.state_fn import StateFn + pauli_sum = SummedOp( [ op.to_pauli_op(massive=massive) # type: ignore diff --git a/qiskit/opflow/list_ops/tensored_op.py b/qiskit/opflow/list_ops/tensored_op.py index ce6bb57e1f76..5eb368b4e4fd 100644 --- a/qiskit/opflow/list_ops/tensored_op.py +++ b/qiskit/opflow/list_ops/tensored_op.py @@ -25,25 +25,25 @@ class TensoredOp(ListOp): - """ A class for lazily representing tensor products of Operators. Often Operators cannot be + """A class for lazily representing tensor products of Operators. Often Operators cannot be efficiently tensored to one another, but may be manipulated further so that they can be later. This class holds logic to indicate that the Operators in ``oplist`` are meant to be tensored together, and therefore if they reach a point in which they can be, such as after - conversion to QuantumCircuits, they can be reduced by tensor product. """ - def __init__(self, - oplist: List[OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0, - abelian: bool = False) -> None: + conversion to QuantumCircuits, they can be reduced by tensor product.""" + + def __init__( + self, + oplist: List[OperatorBase], + coeff: Union[complex, ParameterExpression] = 1.0, + abelian: bool = False, + ) -> None: """ Args: oplist: The Operators being tensored. coeff: A coefficient multiplying the operator abelian: Indicates whether the Operators in ``oplist`` are known to mutually commute. """ - super().__init__(oplist, - combo_fn=partial(reduce, np.kron), - coeff=coeff, - abelian=abelian) + super().__init__(oplist, combo_fn=partial(reduce, np.kron), coeff=coeff, abelian=abelian) @property def num_qubits(self) -> int: @@ -53,7 +53,7 @@ def num_qubits(self) -> int: def distributive(self) -> bool: return False - def _expand_dim(self, num_qubits: int) -> 'TensoredOp': + def _expand_dim(self, num_qubits: int) -> "TensoredOp": """Appends I ^ num_qubits to ``oplist``. Choice of PauliOp as identity is arbitrary and can be substituted for other PrimitiveOp identity. @@ -62,6 +62,7 @@ def _expand_dim(self, num_qubits: int) -> 'TensoredOp': """ # pylint: disable=cyclic-import from ..operator_globals import I + return TensoredOp(self.oplist + [I ^ num_qubits], coeff=self.coeff) def tensor(self, other: OperatorBase) -> OperatorBase: @@ -103,7 +104,10 @@ def to_circuit(self) -> QuantumCircuit: # pylint: disable=cyclic-import from ..state_fns.circuit_state_fn import CircuitStateFn from ..primitive_ops.primitive_op import PrimitiveOp + if isinstance(circuit_op, (PrimitiveOp, CircuitStateFn)): return circuit_op.to_circuit() - raise OpflowError('Conversion to_circuit supported only for operators, where a single ' - 'underlying circuit can be produced.') + raise OpflowError( + "Conversion to_circuit supported only for operators, where a single " + "underlying circuit can be produced." + ) diff --git a/qiskit/opflow/operator_base.py b/qiskit/opflow/operator_base.py index cb3de7cdc95a..9922e09832d6 100644 --- a/qiskit/opflow/operator_base.py +++ b/qiskit/opflow/operator_base.py @@ -27,7 +27,7 @@ class OperatorBase(ABC): - """ A base class for all Operators: PrimitiveOps, StateFns, ListOps, etc. Operators are + """A base class for all Operators: PrimitiveOps, StateFns, ListOps, etc. Operators are defined as functions which take one complex binary function to another. These complex binary functions are represented by StateFns, which are themselves a special class of Operators taking only the ``Zero`` StateFn to the complex binary function they represent. @@ -36,9 +36,10 @@ class OperatorBase(ABC): building blocks for algorithms. """ + # Indentation used in string representation of list operators # Can be changed to use another indentation than two whitespaces - INDENTATION = ' ' + INDENTATION = " " _count = itertools.count() @@ -53,7 +54,7 @@ def instance_id(self) -> int: @property @abstractmethod def num_qubits(self) -> int: - r""" The number of qubits over which the Operator is defined. If + r"""The number of qubits over which the Operator is defined. If ``op.num_qubits == 5``, then ``op.eval('1' * 5)`` will be valid, but ``op.eval('11')`` will not. @@ -64,7 +65,7 @@ def num_qubits(self) -> int: @abstractmethod def primitive_strings(self) -> Set[str]: - r""" Return a set of strings describing the primitives contained in the Operator. For + r"""Return a set of strings describing the primitives contained in the Operator. For example, ``{'QuantumCircuit', 'Pauli'}``. For hierarchical Operators, such as ``ListOps``, this can help illuminate the primitives represented in the various recursive levels, and therefore which conversions can be applied. @@ -114,7 +115,7 @@ def eval( @abstractmethod def reduce(self): - r""" Try collapsing the Operator structure, usually after some type of conversion, + r"""Try collapsing the Operator structure, usually after some type of conversion, e.g. trying to add Operators in a SummedOp or delete needless IGates in a CircuitOp. If no reduction is available, just returns self. @@ -125,7 +126,7 @@ def reduce(self): @abstractmethod def to_matrix(self, massive: bool = False) -> np.ndarray: - r""" Return NumPy representation of the Operator. Represents the evaluation of + r"""Return NumPy representation of the Operator. Represents the evaluation of the Operator's underlying function on every combination of basis binary strings. Warn if more than 16 qubits to force having to set ``massive=True`` if such a large vector is desired. @@ -137,16 +138,16 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: @abstractmethod def to_matrix_op(self, massive: bool = False) -> "OperatorBase": - """ Returns a ``MatrixOp`` equivalent to this Operator. """ + """Returns a ``MatrixOp`` equivalent to this Operator.""" raise NotImplementedError @abstractmethod def to_circuit_op(self) -> "OperatorBase": - """ Returns a ``CircuitOp`` equivalent to this Operator. """ + """Returns a ``CircuitOp`` equivalent to this Operator.""" raise NotImplementedError def to_spmatrix(self) -> spmatrix: - r""" Return SciPy sparse matrix representation of the Operator. Represents the evaluation of + r"""Return SciPy sparse matrix representation of the Operator. Represents the evaluation of the Operator's underlying function on every combination of basis binary strings. Returns: @@ -156,16 +157,16 @@ def to_spmatrix(self) -> spmatrix: @staticmethod def _indent(lines: str, indentation: str = INDENTATION) -> str: - """ Indented representation to allow pretty representation of nested operators. """ + """Indented representation to allow pretty representation of nested operators.""" indented_str = indentation + lines.replace("\n", "\n{}".format(indentation)) if indented_str.endswith("\n{}".format(indentation)): - indented_str = indented_str[:-len(indentation)] + indented_str = indented_str[: -len(indentation)] return indented_str # Addition / Subtraction - def __add__(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Overload ``+`` operation for Operator addition. + def __add__(self, other: "OperatorBase") -> "OperatorBase": + r"""Overload ``+`` operation for Operator addition. Args: other: An ``OperatorBase`` with the same number of qubits as self, and in the same @@ -182,8 +183,8 @@ def __add__(self, other: 'OperatorBase') -> 'OperatorBase': return self.add(other) - def __radd__(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Overload right ``+`` operation for Operator addition. + def __radd__(self, other: "OperatorBase") -> "OperatorBase": + r"""Overload right ``+`` operation for Operator addition. Args: other: An ``OperatorBase`` with the same number of qubits as self, and in the same @@ -201,8 +202,8 @@ def __radd__(self, other: 'OperatorBase') -> 'OperatorBase': return self.add(other) @abstractmethod - def add(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Return Operator addition of self and other, overloaded by ``+``. + def add(self, other: "OperatorBase") -> "OperatorBase": + r"""Return Operator addition of self and other, overloaded by ``+``. Args: other: An ``OperatorBase`` with the same number of qubits as self, and in the same @@ -214,8 +215,8 @@ def add(self, other: 'OperatorBase') -> 'OperatorBase': """ raise NotImplementedError - def __sub__(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Overload ``-`` operation for Operator subtraction. + def __sub__(self, other: "OperatorBase") -> "OperatorBase": + r"""Overload ``-`` operation for Operator subtraction. Args: other: An ``OperatorBase`` with the same number of qubits as self, and in the same @@ -227,8 +228,8 @@ def __sub__(self, other: 'OperatorBase') -> 'OperatorBase': """ return self.add(-other) - def __rsub__(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Overload right ``-`` operation for Operator subtraction. + def __rsub__(self, other: "OperatorBase") -> "OperatorBase": + r"""Overload right ``-`` operation for Operator subtraction. Args: other: An ``OperatorBase`` with the same number of qubits as self, and in the same @@ -242,16 +243,16 @@ def __rsub__(self, other: 'OperatorBase') -> 'OperatorBase': # Negation - def __neg__(self) -> 'OperatorBase': - r""" Overload unary ``-`` to return Operator negation. + def __neg__(self) -> "OperatorBase": + r"""Overload unary ``-`` to return Operator negation. Returns: An ``OperatorBase`` equivalent to the negation of self. """ return self.neg() - def neg(self) -> 'OperatorBase': - r""" Return the Operator's negation, effectively just multiplying by -1.0, + def neg(self) -> "OperatorBase": + r"""Return the Operator's negation, effectively just multiplying by -1.0, overloaded by ``-``. Returns: @@ -261,8 +262,8 @@ def neg(self) -> 'OperatorBase': # Adjoint - def __invert__(self) -> 'OperatorBase': - r""" Overload unary ``~`` to return Operator adjoint. + def __invert__(self) -> "OperatorBase": + r"""Overload unary ``~`` to return Operator adjoint. Returns: An ``OperatorBase`` equivalent to the adjoint of self. @@ -270,8 +271,8 @@ def __invert__(self) -> 'OperatorBase': return self.adjoint() @abstractmethod - def adjoint(self) -> 'OperatorBase': - r""" Return a new Operator equal to the Operator's adjoint (conjugate transpose), + def adjoint(self) -> "OperatorBase": + r"""Return a new Operator equal to the Operator's adjoint (conjugate transpose), overloaded by ``~``. For StateFns, this also turns the StateFn into a measurement. Returns: @@ -282,7 +283,7 @@ def adjoint(self) -> 'OperatorBase': # Equality def __eq__(self, other: object) -> bool: - r""" Overload ``==`` operation to evaluate equality between Operators. + r"""Overload ``==`` operation to evaluate equality between Operators. Args: other: The ``OperatorBase`` to compare to self. @@ -295,7 +296,7 @@ def __eq__(self, other: object) -> bool: return self.equals(cast(OperatorBase, other)) @abstractmethod - def equals(self, other: 'OperatorBase') -> bool: + def equals(self, other: "OperatorBase") -> bool: r""" Evaluate Equality between Operators, overloaded by ``==``. Only returns True if self and other are of the same representation (e.g. a DictStateFn and CircuitStateFn will never be @@ -315,7 +316,7 @@ def equals(self, other: 'OperatorBase') -> bool: # Scalar Multiplication @abstractmethod - def mul(self, scalar: Union[complex, ParameterExpression]) -> 'OperatorBase': + def mul(self, scalar: Union[complex, ParameterExpression]) -> "OperatorBase": r""" Returns the scalar multiplication of the Operator, overloaded by ``*``, including support for Terra's ``Parameters``, which can be bound to values later (via @@ -330,8 +331,8 @@ def mul(self, scalar: Union[complex, ParameterExpression]) -> 'OperatorBase': """ raise NotImplementedError - def __mul__(self, other: complex) -> 'OperatorBase': - r""" Overload ``*`` for Operator scalar multiplication. + def __mul__(self, other: complex) -> "OperatorBase": + r"""Overload ``*`` for Operator scalar multiplication. Args: other: The real or complex scalar by which to multiply the Operator, @@ -342,8 +343,8 @@ def __mul__(self, other: complex) -> 'OperatorBase': """ return self.mul(other) - def __rmul__(self, other: complex) -> 'OperatorBase': - r""" Overload right ``*`` for Operator scalar multiplication. + def __rmul__(self, other: complex) -> "OperatorBase": + r"""Overload right ``*`` for Operator scalar multiplication. Args: other: The real or complex scalar by which to multiply the Operator, @@ -354,8 +355,8 @@ def __rmul__(self, other: complex) -> 'OperatorBase': """ return self.mul(other) - def __truediv__(self, other: complex) -> 'OperatorBase': - r""" Overload ``/`` for scalar Operator division. + def __truediv__(self, other: complex) -> "OperatorBase": + r"""Overload ``/`` for scalar Operator division. Args: other: The real or complex scalar by which to divide the Operator, @@ -366,8 +367,8 @@ def __truediv__(self, other: complex) -> 'OperatorBase': """ return self.mul(1 / other) - def __xor__(self, other: Union['OperatorBase', int]) -> 'OperatorBase': - r""" Overload ``^`` for tensor product or tensorpower if other is an int. + def __xor__(self, other: Union["OperatorBase", int]) -> "OperatorBase": + r"""Overload ``^`` for tensor product or tensorpower if other is an int. Args: other: The ``OperatorBase`` to tensor product with self, or the int number of times @@ -382,8 +383,8 @@ def __xor__(self, other: Union['OperatorBase', int]) -> 'OperatorBase': else: return self.tensor(other) - def __rxor__(self, other: Union['OperatorBase', int]) -> 'OperatorBase': - r""" Overload right ``^`` for tensor product, a hack to make (I^0)^Z work as intended. + def __rxor__(self, other: Union["OperatorBase", int]) -> "OperatorBase": + r"""Overload right ``^`` for tensor product, a hack to make (I^0)^Z work as intended. Args: other: The ``OperatorBase`` for self to tensor product with, or 1, which indicates to @@ -398,8 +399,8 @@ def __rxor__(self, other: Union['OperatorBase', int]) -> 'OperatorBase': return cast(OperatorBase, other).tensor(self) @abstractmethod - def tensor(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Return tensor product between self and other, overloaded by ``^``. + def tensor(self, other: "OperatorBase") -> "OperatorBase": + r"""Return tensor product between self and other, overloaded by ``^``. Note: You must be conscious of Qiskit's big-endian bit printing convention. Meaning, X.tensor(Y) produces an X on qubit 0 and an Y on qubit 1, or X⨂Y, but would produce a QuantumCircuit which looks like @@ -419,8 +420,8 @@ def tensor(self, other: 'OperatorBase') -> 'OperatorBase': raise NotImplementedError @abstractmethod - def tensorpower(self, other: int) -> Union['OperatorBase', int]: - r""" Return tensor product with self multiple times, overloaded by ``^``. + def tensorpower(self, other: int) -> Union["OperatorBase", int]: + r"""Return tensor product with self multiple times, overloaded by ``^``. Args: other: The int number of times to tensor product self with itself via ``tensorpower``. @@ -433,20 +434,20 @@ def tensorpower(self, other: int) -> Union['OperatorBase', int]: @property @abstractmethod def parameters(self): - r""" Return a set of Parameter objects contained in the Operator. - """ + r"""Return a set of Parameter objects contained in the Operator.""" raise NotImplementedError # Utility functions for parameter binding @abstractmethod - def assign_parameters(self, - param_dict: Dict[ParameterExpression, - Union[complex, - ParameterExpression, - List[Union[complex, ParameterExpression]]]] - ) -> 'OperatorBase': - """ Binds scalar values to any Terra ``Parameters`` in the coefficients or primitives of + def assign_parameters( + self, + param_dict: Dict[ + ParameterExpression, + Union[complex, ParameterExpression, List[Union[complex, ParameterExpression]]], + ], + ) -> "OperatorBase": + """Binds scalar values to any Terra ``Parameters`` in the coefficients or primitives of the Operator, or substitutes one ``Parameter`` for another. This method differs from Terra's ``assign_parameters`` in that it also supports lists of values to assign for a give ``Parameter``, in which case self will be copied for each parameterization in the @@ -466,7 +467,7 @@ def assign_parameters(self, raise NotImplementedError @abstractmethod - def _expand_dim(self, num_qubits: int) -> 'OperatorBase': + def _expand_dim(self, num_qubits: int) -> "OperatorBase": """Expands the operator with identity operator of dimension 2**num_qubits. Returns: @@ -476,7 +477,7 @@ def _expand_dim(self, num_qubits: int) -> 'OperatorBase': raise NotImplementedError @abstractmethod - def permute(self, permutation: List[int]) -> 'OperatorBase': + def permute(self, permutation: List[int]) -> "OperatorBase": """Permutes the qubits of the operator. Args: @@ -491,12 +492,13 @@ def permute(self, permutation: List[int]) -> 'OperatorBase': """ raise NotImplementedError - def bind_parameters(self, - param_dict: Dict[ParameterExpression, - Union[complex, - ParameterExpression, - List[Union[complex, ParameterExpression]]]] - ) -> 'OperatorBase': + def bind_parameters( + self, + param_dict: Dict[ + ParameterExpression, + Union[complex, ParameterExpression, List[Union[complex, ParameterExpression]]], + ], + ) -> "OperatorBase": r""" Same as assign_parameters, but maintained for consistency with QuantumCircuit in Terra (which has both assign_parameters and bind_parameters). @@ -505,12 +507,11 @@ def bind_parameters(self, # Mostly copied from terra, but with list unrolling added: @staticmethod - def _unroll_param_dict(value_dict: Dict[Union[ParameterExpression, ParameterVector], - Union[complex, List[complex]]] - ) -> Union[Dict[ParameterExpression, complex], - List[Dict[ParameterExpression, complex]]]: - """ Unrolls the ParameterVectors in a param_dict into separate Parameters, and unrolls - parameterization value lists into separate param_dicts without list nesting. """ + def _unroll_param_dict( + value_dict: Dict[Union[ParameterExpression, ParameterVector], Union[complex, List[complex]]] + ) -> Union[Dict[ParameterExpression, complex], List[Dict[ParameterExpression, complex]]]: + """Unrolls the ParameterVectors in a param_dict into separate Parameters, and unrolls + parameterization value lists into separate param_dicts without list nesting.""" unrolled_value_dict = {} for (param, value) in value_dict.items(): if isinstance(param, ParameterExpression): @@ -518,8 +519,9 @@ def _unroll_param_dict(value_dict: Dict[Union[ParameterExpression, ParameterVect if isinstance(param, ParameterVector) and isinstance(value, (list, np.ndarray)): if not len(param) == len(value): raise ValueError( - 'ParameterVector {} has length {}, which differs from value list {} of ' - 'len {}'.format(param, len(param), value, len(value))) + "ParameterVector {} has length {}, which differs from value list {} of " + "len {}".format(param, len(param), value, len(value)) + ) unrolled_value_dict.update(zip(param, value)) if isinstance(list(unrolled_value_dict.values())[0], list): # check that all are same length @@ -527,31 +529,33 @@ def _unroll_param_dict(value_dict: Dict[Union[ParameterExpression, ParameterVect try: for i in range(len(list(unrolled_value_dict.values())[0])): # type: ignore unrolled_value_dict_list.append( - OperatorBase._get_param_dict_for_index(unrolled_value_dict, # type: ignore - i)) + OperatorBase._get_param_dict_for_index( + unrolled_value_dict, i # type: ignore + ) + ) return unrolled_value_dict_list except IndexError as ex: - raise OpflowError('Parameter binding lists must all be the same length.') from ex + raise OpflowError("Parameter binding lists must all be the same length.") from ex return unrolled_value_dict # type: ignore @staticmethod - def _get_param_dict_for_index(unrolled_dict: Dict[ParameterExpression, List[complex]], - i: int): - """ Gets a single non-list-nested param_dict for a given list index from a nested one. """ + def _get_param_dict_for_index(unrolled_dict: Dict[ParameterExpression, List[complex]], i: int): + """Gets a single non-list-nested param_dict for a given list index from a nested one.""" return {k: v[i] for (k, v) in unrolled_dict.items()} - def _expand_shorter_operator_and_permute(self, other: 'OperatorBase', - permutation: Optional[List[int]] = None) \ - -> Tuple['OperatorBase', 'OperatorBase']: + def _expand_shorter_operator_and_permute( + self, other: "OperatorBase", permutation: Optional[List[int]] = None + ) -> Tuple["OperatorBase", "OperatorBase"]: if permutation is not None: other = other.permute(permutation) new_self = self if not self.num_qubits == other.num_qubits: # pylint: disable=cyclic-import from .operator_globals import Zero + if other == Zero: # Zero is special - we'll expand it to the correct qubit number. - other = Zero.__class__('0' * self.num_qubits) + other = Zero.__class__("0" * self.num_qubits) elif other.num_qubits < self.num_qubits: other = other._expand_dim(self.num_qubits - other.num_qubits) elif other.num_qubits > self.num_qubits: @@ -564,8 +568,8 @@ def copy(self) -> "OperatorBase": # Composition - def __matmul__(self, other: 'OperatorBase') -> 'OperatorBase': - r""" Overload ``@`` for Operator composition. + def __matmul__(self, other: "OperatorBase") -> "OperatorBase": + r"""Overload ``@`` for Operator composition. Args: other: The ``OperatorBase`` with which to compose self. @@ -576,9 +580,10 @@ def __matmul__(self, other: 'OperatorBase') -> 'OperatorBase': return self.compose(other) @abstractmethod - def compose(self, other: 'OperatorBase', - permutation: Optional[List[int]] = None, front: bool = False) -> 'OperatorBase': - r""" Return Operator Composition between self and other (linear algebra-style: + def compose( + self, other: "OperatorBase", permutation: Optional[List[int]] = None, front: bool = False + ) -> "OperatorBase": + r"""Return Operator Composition between self and other (linear algebra-style: A@B(x) = A(B(x))), overloaded by ``@``. Note: You must be conscious of Quantum Circuit vs. Linear Algebra ordering @@ -600,8 +605,8 @@ def compose(self, other: 'OperatorBase', raise NotImplementedError @abstractmethod - def power(self, exponent: int) -> 'OperatorBase': - r""" Return Operator composed with self multiple times, overloaded by ``**``. + def power(self, exponent: int) -> "OperatorBase": + r"""Return Operator composed with self multiple times, overloaded by ``**``. Args: exponent: The int number of times to compose self with itself. @@ -611,8 +616,8 @@ def power(self, exponent: int) -> 'OperatorBase': """ raise NotImplementedError - def __pow__(self, exponent: int) -> 'OperatorBase': - r""" Overload ``**`` for composition power. + def __pow__(self, exponent: int) -> "OperatorBase": + r"""Overload ``**`` for composition power. Args: exponent: The int number of times to compose self with itself. @@ -623,10 +628,7 @@ def __pow__(self, exponent: int) -> 'OperatorBase': return self.power(exponent) @staticmethod - def _check_massive(method: str, - matrix: bool, - num_qubits: int, - massive: bool) -> None: + def _check_massive(method: str, matrix: bool, num_qubits: int, massive: bool) -> None: """ Checks if matrix or vector generated will be too large. @@ -642,16 +644,17 @@ def _check_massive(method: str, if num_qubits > 16 and not massive and not algorithm_globals.massive: dim = 2 ** num_qubits if matrix: - obj_type = 'matrix' - dimensions = f'{dim}x{dim}' + obj_type = "matrix" + dimensions = f"{dim}x{dim}" else: - obj_type = 'vector' - dimensions = f'{dim}' + obj_type = "vector" + dimensions = f"{dim}" raise ValueError( f"'{method}' will return an exponentially large {obj_type}, " f"in this case '{dimensions}' elements. " "Set algorithm_globals.massive=True or the method argument massive=True " - "if you want to proceed.") + "if you want to proceed." + ) # Printing diff --git a/qiskit/opflow/operator_globals.py b/qiskit/opflow/operator_globals.py index a0b5b0763efa..394935b57954 100644 --- a/qiskit/opflow/operator_globals.py +++ b/qiskit/opflow/operator_globals.py @@ -35,7 +35,7 @@ def make_immutable(obj): - """ Delete the __setattr__ property to make the object mostly immutable. """ + """Delete the __setattr__ property to make the object mostly immutable.""" # TODO figure out how to get correct error message # def throw_immutability_exception(self, *args): @@ -46,10 +46,10 @@ def make_immutable(obj): # 1-Qubit Paulis -X = make_immutable(PauliOp(Pauli('X'))) -Y = make_immutable(PauliOp(Pauli('Y'))) -Z = make_immutable(PauliOp(Pauli('Z'))) -I = make_immutable(PauliOp(Pauli('I'))) +X = make_immutable(PauliOp(Pauli("X"))) +Y = make_immutable(PauliOp(Pauli("Y"))) +Z = make_immutable(PauliOp(Pauli("Z"))) +I = make_immutable(PauliOp(Pauli("I"))) # Clifford+T, and some other common non-parameterized gates CX = make_immutable(CircuitOp(CXGate())) @@ -60,7 +60,7 @@ def make_immutable(obj): CZ = make_immutable(CircuitOp(CZGate())) # 1-Qubit Paulis -Zero = make_immutable(DictStateFn('0')) -One = make_immutable(DictStateFn('1')) +Zero = make_immutable(DictStateFn("0")) +One = make_immutable(DictStateFn("1")) Plus = make_immutable(H.compose(Zero)) Minus = make_immutable(H.compose(X).compose(Zero)) diff --git a/qiskit/opflow/primitive_ops/__init__.py b/qiskit/opflow/primitive_ops/__init__.py index 1c644ee01068..4da80dad2439 100644 --- a/qiskit/opflow/primitive_ops/__init__.py +++ b/qiskit/opflow/primitive_ops/__init__.py @@ -62,11 +62,12 @@ from .pauli_sum_op import PauliSumOp from .tapered_pauli_sum_op import TaperedPauliSumOp, Z2Symmetries -__all__ = ['PrimitiveOp', - 'PauliOp', - 'MatrixOp', - 'CircuitOp', - 'PauliSumOp', - 'TaperedPauliSumOp', - 'Z2Symmetries' - ] +__all__ = [ + "PrimitiveOp", + "PauliOp", + "MatrixOp", + "CircuitOp", + "PauliSumOp", + "TaperedPauliSumOp", + "Z2Symmetries", +] diff --git a/qiskit/opflow/primitive_ops/circuit_op.py b/qiskit/opflow/primitive_ops/circuit_op.py index 4285a9e0d9d6..e3b5af2839e2 100644 --- a/qiskit/opflow/primitive_ops/circuit_op.py +++ b/qiskit/opflow/primitive_ops/circuit_op.py @@ -27,14 +27,15 @@ class CircuitOp(PrimitiveOp): - """ Class for Operators backed by Terra's ``QuantumCircuit`` module. - """ + """Class for Operators backed by Terra's ``QuantumCircuit`` module.""" primitive: QuantumCircuit - def __init__(self, - primitive: Union[Instruction, QuantumCircuit], - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__( + self, + primitive: Union[Instruction, QuantumCircuit], + coeff: Union[complex, ParameterExpression] = 1.0, + ) -> None: """ Args: primitive: The QuantumCircuit which defines the @@ -50,17 +51,19 @@ def __init__(self, primitive = qc if not isinstance(primitive, QuantumCircuit): - raise TypeError('CircuitOp can only be instantiated with ' - 'QuantumCircuit, not {}'.format(type(primitive))) + raise TypeError( + "CircuitOp can only be instantiated with " + "QuantumCircuit, not {}".format(type(primitive)) + ) if len(primitive.clbits) != 0: - raise TypeError('CircuitOp does not support QuantumCircuits with ClassicalRegisters.') + raise TypeError("CircuitOp does not support QuantumCircuits with ClassicalRegisters.") super().__init__(primitive, coeff) self._coeff = coeff def primitive_strings(self) -> Set[str]: - return {'QuantumCircuit'} + return {"QuantumCircuit"} @property def num_qubits(self) -> int: @@ -69,8 +72,9 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over operators with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over operators with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) if isinstance(other, CircuitOp) and self.primitive == other.primitive: return CircuitOp(self.primitive, coeff=self.coeff + other.coeff) @@ -78,6 +82,7 @@ def add(self, other: OperatorBase) -> OperatorBase: # Covers all else. # pylint: disable=cyclic-import from ..list_ops.summed_op import SummedOp + return SummedOp([self, other]) def adjoint(self) -> "CircuitOp": @@ -93,23 +98,25 @@ def tensor(self, other: OperatorBase) -> Union["CircuitOp", TensoredOp]: # pylint: disable=cyclic-import from .pauli_op import PauliOp from .matrix_op import MatrixOp + if isinstance(other, (PauliOp, CircuitOp, MatrixOp)): other = other.to_circuit_op() if isinstance(other, CircuitOp): new_qc = QuantumCircuit(self.num_qubits + other.num_qubits) # NOTE!!! REVERSING QISKIT ENDIANNESS HERE - new_qc.append(other.to_instruction(), - qargs=new_qc.qubits[0:other.primitive.num_qubits]) - new_qc.append(self.to_instruction(), - qargs=new_qc.qubits[other.primitive.num_qubits:]) + new_qc.append( + other.to_instruction(), qargs=new_qc.qubits[0 : other.primitive.num_qubits] + ) + new_qc.append(self.to_instruction(), qargs=new_qc.qubits[other.primitive.num_qubits :]) new_qc = new_qc.decompose() return CircuitOp(new_qc, coeff=self.coeff * other.coeff) return TensoredOp([self, other]) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(CircuitOp, new_self) @@ -131,22 +138,22 @@ def compose(self, other: OperatorBase, if isinstance(other, (CircuitOp, CircuitStateFn)): new_qc = other.primitive.compose(new_self.primitive) if isinstance(other, CircuitStateFn): - return CircuitStateFn(new_qc, - is_measurement=other.is_measurement, - coeff=new_self.coeff * other.coeff) + return CircuitStateFn( + new_qc, is_measurement=other.is_measurement, coeff=new_self.coeff * other.coeff + ) else: return CircuitOp(new_qc, coeff=new_self.coeff * other.coeff) return super(CircuitOp, new_self).compose(other) def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) unitary = qiskit.quantum_info.Operator(self.to_circuit()).data return unitary * self.coeff def __str__(self) -> str: qc = self.to_circuit() - prim_str = str(qc.draw(output='text')) + prim_str = str(qc.draw(output="text")) if self.coeff == 1.0: return prim_str else: @@ -159,9 +166,11 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: unrolled_dict = self._unroll_param_dict(param_dict) if isinstance(unrolled_dict, list): from ..list_ops.list_op import ListOp + return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if isinstance(self.coeff, ParameterExpression) \ - and self.coeff.parameters <= set(unrolled_dict.keys()): + if isinstance(self.coeff, ParameterExpression) and self.coeff.parameters <= set( + unrolled_dict.keys() + ): param_instersection = set(unrolled_dict.keys()) & self.coeff.parameters binds = {param: unrolled_dict[param] for param in param_instersection} param_value = float(self.coeff.bind(binds)) @@ -169,8 +178,7 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: # This is different from bind_parameters in Terra because they check for set equality if set(unrolled_dict.keys()) & self.primitive.parameters: # Only bind the params found in the circuit - param_instersection = \ - set(unrolled_dict.keys()) & self.primitive.parameters + param_instersection = set(unrolled_dict.keys()) & self.primitive.parameters binds = {param: unrolled_dict[param] for param in param_instersection} qc = self.to_circuit().assign_parameters(binds) return self.__class__(qc, coeff=param_value) @@ -187,8 +195,9 @@ def eval( from .matrix_op import MatrixOp if isinstance(front, ListOp) and front.distributive: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) # Composable with circuit if isinstance(front, (PauliOp, CircuitOp, MatrixOp, CircuitStateFn)): @@ -214,15 +223,16 @@ def reduce(self) -> OperatorBase: # Check if Identity or empty instruction (need to check that type is exactly # Instruction because some gates have lazy gate.definition population) # pylint: disable=unidiomatic-typecheck - if isinstance(gate, IGate) or (type(gate) == Instruction and - gate.definition.data == []): + if isinstance(gate, IGate) or ( + type(gate) == Instruction and gate.definition.data == [] + ): del self.primitive.data[i] return self - def _expand_dim(self, num_qubits: int) -> 'CircuitOp': + def _expand_dim(self, num_qubits: int) -> "CircuitOp": return self.permute(list(range(num_qubits, num_qubits + self.num_qubits))) - def permute(self, permutation: List[int]) -> 'CircuitOp': + def permute(self, permutation: List[int]) -> "CircuitOp": r""" Permute the qubits of the circuit. diff --git a/qiskit/opflow/primitive_ops/matrix_op.py b/qiskit/opflow/primitive_ops/matrix_op.py index 66adee228670..07ceb35a9cdb 100644 --- a/qiskit/opflow/primitive_ops/matrix_op.py +++ b/qiskit/opflow/primitive_ops/matrix_op.py @@ -31,14 +31,15 @@ class MatrixOp(PrimitiveOp): - """ Class for Operators represented by matrices, backed by Terra's ``Operator`` module. - """ + """Class for Operators represented by matrices, backed by Terra's ``Operator`` module.""" primitive: Operator - def __init__(self, - primitive: Union[list, np.ndarray, spmatrix, Operator], - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__( + self, + primitive: Union[list, np.ndarray, spmatrix, Operator], + coeff: Union[complex, ParameterExpression] = 1.0, + ) -> None: """ Args: primitive: The matrix-like object which defines the behavior of the underlying function. @@ -56,18 +57,20 @@ def __init__(self, primitive = Operator(primitive) if not isinstance(primitive, Operator): - type_hints = get_type_hints(MatrixOp.__init__).get('primitive') + type_hints = get_type_hints(MatrixOp.__init__).get("primitive") valid_cls = [cls.__name__ for cls in type_hints.__args__] - raise TypeError(f"MatrixOp can only be instantiated with {valid_cls}, " - f"not '{primitive_orig.__class__.__name__}'") + raise TypeError( + f"MatrixOp can only be instantiated with {valid_cls}, " + f"not '{primitive_orig.__class__.__name__}'" + ) if not primitive.input_dims() == primitive.output_dims(): - raise ValueError('Cannot handle non-square matrices yet.') + raise ValueError("Cannot handle non-square matrices yet.") super().__init__(primitive, coeff=coeff) def primitive_strings(self) -> Set[str]: - return {'Matrix'} + return {"Matrix"} @property def num_qubits(self) -> int: @@ -76,18 +79,20 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> Union["MatrixOp", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over operators with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over operators with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) if isinstance(other, MatrixOp) and self.primitive == other.primitive: return MatrixOp(self.primitive, coeff=self.coeff + other.coeff) # Terra's Operator cannot handle ParameterExpressions - if isinstance(other, MatrixOp) and \ - not isinstance(self.coeff, ParameterExpression) and \ - not isinstance(other.coeff, ParameterExpression): - return MatrixOp( - (self.coeff * self.primitive) + (other.coeff * other.primitive)) + if ( + isinstance(other, MatrixOp) + and not isinstance(self.coeff, ParameterExpression) + and not isinstance(other.coeff, ParameterExpression) + ): + return MatrixOp((self.coeff * self.primitive) + (other.coeff * other.primitive)) # Covers Paulis, Circuits, and all else. return SummedOp([self, other]) @@ -98,27 +103,29 @@ def adjoint(self) -> "MatrixOp": def equals(self, other: OperatorBase) -> bool: if not isinstance(other, MatrixOp): return False - if isinstance(self.coeff, ParameterExpression) ^ \ - isinstance(other.coeff, ParameterExpression): + if isinstance(self.coeff, ParameterExpression) ^ isinstance( + other.coeff, ParameterExpression + ): return False - if isinstance(self.coeff, ParameterExpression) and \ - isinstance(other.coeff, ParameterExpression): + if isinstance(self.coeff, ParameterExpression) and isinstance( + other.coeff, ParameterExpression + ): return self.coeff == other.coeff and self.primitive == other.primitive return self.coeff * self.primitive == other.coeff * other.primitive - def _expand_dim(self, num_qubits: int) -> 'MatrixOp': - identity = np.identity(2**num_qubits, dtype=complex) + def _expand_dim(self, num_qubits: int) -> "MatrixOp": + identity = np.identity(2 ** num_qubits, dtype=complex) return MatrixOp(self.primitive.tensor(Operator(identity)), coeff=self.coeff) def tensor(self, other: OperatorBase) -> Union["MatrixOp", TensoredOp]: if isinstance(other, MatrixOp): - return MatrixOp(self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff) + return MatrixOp(self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff) return TensoredOp([self, other]) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(MatrixOp, new_self) @@ -126,8 +133,10 @@ def compose(self, other: OperatorBase, if front: return other.compose(new_self) if isinstance(other, MatrixOp): - return MatrixOp(new_self.primitive.compose(other.primitive, front=True), - coeff=new_self.coeff * other.coeff) + return MatrixOp( + new_self.primitive.compose(other.primitive, front=True), + coeff=new_self.coeff * other.coeff, + ) return super(MatrixOp, new_self).compose(other) @@ -155,8 +164,9 @@ def permute(self, permutation: Optional[List[int]] = None) -> OperatorBase: qc = QuantumCircuit(new_matrix_size) # extend the indices to match the size of the new matrix - permutation \ - = list(filter(lambda x: x not in permutation, range(new_matrix_size))) + permutation + permutation = ( + list(filter(lambda x: x not in permutation, range(new_matrix_size))) + permutation + ) # decompose permutation into sequence of transpositions transpositions = arithmetic.transpositions(permutation) @@ -196,8 +206,9 @@ def eval( front = StateFn(front, is_measurement=False) if isinstance(front, ListOp) and front.distributive: - new_front = front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + new_front = front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) elif isinstance(front, OperatorStateFn): new_front = OperatorStateFn(self.adjoint().compose(front.to_matrix_op()).compose(self)) diff --git a/qiskit/opflow/primitive_ops/pauli_op.py b/qiskit/opflow/primitive_ops/pauli_op.py index 25afc893d715..f5e8556cceb5 100644 --- a/qiskit/opflow/primitive_ops/pauli_op.py +++ b/qiskit/opflow/primitive_ops/pauli_op.py @@ -27,32 +27,31 @@ from qiskit.opflow.primitive_ops.primitive_op import PrimitiveOp from qiskit.quantum_info import Pauli, SparsePauliOp, Statevector -PAULI_GATE_MAPPING = {'X': XGate(), 'Y': YGate(), 'Z': ZGate(), 'I': IGate()} +PAULI_GATE_MAPPING = {"X": XGate(), "Y": YGate(), "Z": ZGate(), "I": IGate()} class PauliOp(PrimitiveOp): - """ Class for Operators backed by Terra's ``Pauli`` module. - """ + """Class for Operators backed by Terra's ``Pauli`` module.""" + primitive: Pauli - def __init__(self, - primitive: Pauli, - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__(self, primitive: Pauli, coeff: Union[complex, ParameterExpression] = 1.0) -> None: """ - Args: - primitive: The Pauli which defines the behavior of the underlying function. - coeff: A coefficient multiplying the primitive. + Args: + primitive: The Pauli which defines the behavior of the underlying function. + coeff: A coefficient multiplying the primitive. - Raises: - TypeError: invalid parameters. + Raises: + TypeError: invalid parameters. """ if not isinstance(primitive, Pauli): raise TypeError( - 'PauliOp can only be instantiated with Paulis, not {}'.format(type(primitive))) + "PauliOp can only be instantiated with Paulis, not {}".format(type(primitive)) + ) super().__init__(primitive, coeff=coeff) def primitive_strings(self) -> Set[str]: - return {'Pauli'} + return {"Pauli"} @property def num_qubits(self) -> int: @@ -61,14 +60,16 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over operators with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over operators with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) if isinstance(other, PauliOp) and self.primitive == other.primitive: return PauliOp(self.primitive, coeff=self.coeff + other.coeff) # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp + if ( isinstance(other, PauliOp) and isinstance(self.coeff, (int, float, complex)) @@ -93,8 +94,8 @@ def equals(self, other: OperatorBase) -> bool: return self.primitive == other.primitive - def _expand_dim(self, num_qubits: int) -> 'PauliOp': - return PauliOp(Pauli('I'*num_qubits).expand(self.primitive), coeff=self.coeff) + def _expand_dim(self, num_qubits: int) -> "PauliOp": + return PauliOp(Pauli("I" * num_qubits).expand(self.primitive), coeff=self.coeff) def tensor(self, other: OperatorBase) -> OperatorBase: # Both Paulis @@ -103,17 +104,19 @@ def tensor(self, other: OperatorBase) -> OperatorBase: # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp + if isinstance(other, PauliSumOp): new_primitive = SparsePauliOp(self.primitive).tensor(other.primitive) return PauliSumOp(new_primitive, coeff=self.coeff * other.coeff) from .circuit_op import CircuitOp + if isinstance(other, CircuitOp): return self.to_circuit_op().tensor(other) return TensoredOp([self, other]) - def permute(self, permutation: List[int]) -> 'PauliOp': + def permute(self, permutation: List[int]) -> "PauliOp": """Permutes the sequence of Pauli matrices. Args: @@ -129,16 +132,18 @@ def permute(self, permutation: List[int]) -> 'PauliOp': """ pauli_string = self.primitive.__str__() length = max(permutation) + 1 # size of list must be +1 larger then its max index - new_pauli_list = ['I'] * length + new_pauli_list = ["I"] * length if len(permutation) != self.num_qubits: - raise OpflowError("List of indices to permute must " - "have the same size as Pauli Operator") + raise OpflowError( + "List of indices to permute must " "have the same size as Pauli Operator" + ) for i, index in enumerate(permutation): new_pauli_list[-index - 1] = pauli_string[-i - 1] - return PauliOp(Pauli(''.join(new_pauli_list)), self.coeff) + return PauliOp(Pauli("".join(new_pauli_list)), self.coeff) - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: new_self, other = self._expand_shorter_operator_and_permute(other, permutation) new_self = cast(PauliOp, new_self) @@ -156,6 +161,7 @@ def compose(self, other: OperatorBase, # pylint: disable=cyclic-import from .pauli_sum_op import PauliSumOp + if isinstance(other, PauliSumOp): return PauliSumOp( SparsePauliOp(new_self.primitive).dot(other.primitive), @@ -165,17 +171,18 @@ def compose(self, other: OperatorBase, # pylint: disable=cyclic-import from .circuit_op import CircuitOp from ..state_fns.circuit_state_fn import CircuitStateFn + if isinstance(other, (CircuitOp, CircuitStateFn)): return new_self.to_circuit_op().compose(other) return super(PauliOp, new_self).compose(other) def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", True, self.num_qubits, massive) return self.primitive.to_matrix() * self.coeff def to_spmatrix(self) -> spmatrix: - """ Returns SciPy sparse matrix representation of the Operator. + """Returns SciPy sparse matrix representation of the Operator. Returns: CSR sparse matrix representation of the Operator. @@ -215,16 +222,17 @@ def eval( front = StateFn(front, is_measurement=False) if isinstance(front, ListOp) and front.distributive: - new_front = front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + new_front = front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) else: if self.num_qubits != front.num_qubits: raise ValueError( - 'eval does not support operands with differing numbers of qubits, ' - '{} and {}, respectively.'.format( - self.num_qubits, front.num_qubits)) + "eval does not support operands with differing numbers of qubits, " + "{} and {}, respectively.".format(self.num_qubits, front.num_qubits) + ) if isinstance(front, DictStateFn): @@ -235,34 +243,36 @@ def eval( for bstr, v in front.primitive.items(): bitstr = np.fromiter(bstr, dtype=int).astype(bool) new_b_str = np.logical_xor(bitstr, corrected_x_bits) - new_str = ''.join(map(str, 1 * new_b_str)) + new_str = "".join(map(str, 1 * new_b_str)) z_factor = np.product(1 - 2 * np.logical_and(bitstr, corrected_z_bits)) - y_factor = np.product(np.sqrt(1 - 2 * np.logical_and(corrected_x_bits, - corrected_z_bits) + 0j)) + y_factor = np.product( + np.sqrt(1 - 2 * np.logical_and(corrected_x_bits, corrected_z_bits) + 0j) + ) new_dict[new_str] = (v * z_factor * y_factor) + new_dict.get(new_str, 0) # The coefficient consists of: # 1. the coefficient of *this* PauliOp (self) # 2. the coefficient of the evaluated DictStateFn (front) # 3. AND acquires the phase of the internal primitive. This is necessary to # ensure that (X @ Z) and (-iY) return the same result. - new_front = StateFn(new_dict, coeff=self.coeff * front.coeff * - (-1j) ** self.primitive.phase) + new_front = StateFn( + new_dict, coeff=self.coeff * front.coeff * (-1j) ** self.primitive.phase + ) elif isinstance(front, StateFn) and front.is_measurement: - raise ValueError('Operator composed with a measurement is undefined.') + raise ValueError("Operator composed with a measurement is undefined.") # Composable types with PauliOp elif isinstance(front, (PauliOp, CircuitOp, CircuitStateFn)): new_front = self.compose(front) - # Covers VectorStateFn and OperatorStateFn + # Covers VectorStateFn and OperatorStateFn elif isinstance(front, StateFn): new_front = self.to_matrix_op().eval(front.to_matrix_op()) return new_front def exp_i(self) -> OperatorBase: - """ Return a ``CircuitOp`` equivalent to e^-iH for this operator H. """ + """Return a ``CircuitOp`` equivalent to e^-iH for this operator H.""" # if only one qubit is significant, we can perform the evolution corrected_x = self.primitive.x[::-1] corrected_z = self.primitive.z[::-1] @@ -273,11 +283,14 @@ def exp_i(self) -> OperatorBase: return PauliOp(self.primitive) if np.sum(sig_qubits) == 1: sig_qubit_index = sig_qubits.tolist().index(True) - coeff = np.real(self.coeff) \ - if not isinstance(self.coeff, ParameterExpression) \ + coeff = ( + np.real(self.coeff) + if not isinstance(self.coeff, ParameterExpression) else self.coeff + ) from .circuit_op import CircuitOp + # Y rotation if corrected_x[sig_qubit_index] and corrected_z[sig_qubit_index]: rot_op = CircuitOp(RYGate(2 * coeff)) @@ -290,12 +303,14 @@ def exp_i(self) -> OperatorBase: # pylint: disable=cyclic-import from ..operator_globals import I + left_pad = I.tensorpower(sig_qubit_index) right_pad = I.tensorpower(self.num_qubits - sig_qubit_index - 1) # Need to use overloaded operators here in case left_pad == I^0 return left_pad ^ rot_op ^ right_pad else: from ..evolutions.evolved_op import EvolvedOp + return EvolvedOp(self) def to_circuit(self) -> QuantumCircuit: @@ -306,7 +321,7 @@ def to_circuit(self) -> QuantumCircuit: qc = QuantumCircuit(len(self.primitive)) for q, pauli_str in enumerate(reversed(self.primitive.to_label())): gate = PAULI_GATE_MAPPING[pauli_str] - if not pauli_str == 'I' or is_identity: + if not pauli_str == "I" or is_identity: qc.append(gate, qargs=[q]) return qc diff --git a/qiskit/opflow/primitive_ops/pauli_sum_op.py b/qiskit/opflow/primitive_ops/pauli_sum_op.py index 3b540e64f8e1..eed366a21590 100644 --- a/qiskit/opflow/primitive_ops/pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/pauli_sum_op.py @@ -326,7 +326,7 @@ def eval( return self.to_matrix_op().eval(front.to_matrix_op()) def exp_i(self) -> OperatorBase: - """ Return a ``CircuitOp`` equivalent to e^-iH for this operator H. """ + """Return a ``CircuitOp`` equivalent to e^-iH for this operator H.""" # TODO: optimize for some special cases from ..evolutions.evolved_op import EvolvedOp diff --git a/qiskit/opflow/primitive_ops/primitive_op.py b/qiskit/opflow/primitive_ops/primitive_op.py index 8655286e2a91..f543f8e59691 100644 --- a/qiskit/opflow/primitive_ops/primitive_op.py +++ b/qiskit/opflow/primitive_ops/primitive_op.py @@ -45,11 +45,14 @@ def __init_subclass__(cls): @staticmethod # pylint: disable=unused-argument - def __new__(cls, - primitive: Union[Instruction, QuantumCircuit, List, - np.ndarray, spmatrix, Operator, Pauli, SparsePauliOp], - coeff: Union[complex, ParameterExpression] = 1.0) -> 'PrimitiveOp': - """ A factory method to produce the correct type of PrimitiveOp subclass + def __new__( + cls, + primitive: Union[ + Instruction, QuantumCircuit, List, np.ndarray, spmatrix, Operator, Pauli, SparsePauliOp + ], + coeff: Union[complex, ParameterExpression] = 1.0, + ) -> "PrimitiveOp": + """A factory method to produce the correct type of PrimitiveOp subclass based on the primitive passed in. Primitive and coeff arguments are passed into subclass's init() as-is automatically by new(). @@ -66,30 +69,38 @@ def __new__(cls, # pylint: disable=cyclic-import if isinstance(primitive, (Instruction, QuantumCircuit)): from .circuit_op import CircuitOp + return super().__new__(CircuitOp) if isinstance(primitive, (list, np.ndarray, spmatrix, Operator)): from .matrix_op import MatrixOp + return super().__new__(MatrixOp) if isinstance(primitive, Pauli): from .pauli_op import PauliOp + return super().__new__(PauliOp) if isinstance(primitive, SparsePauliOp): from .pauli_sum_op import PauliSumOp + return super().__new__(PauliSumOp) - raise TypeError('Unsupported primitive type {} passed into PrimitiveOp ' - 'factory constructor'.format(type(primitive))) + raise TypeError( + "Unsupported primitive type {} passed into PrimitiveOp " + "factory constructor".format(type(primitive)) + ) - def __init__(self, - primitive: Union[QuantumCircuit, Operator, Pauli, SparsePauliOp, OperatorBase], - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__( + self, + primitive: Union[QuantumCircuit, Operator, Pauli, SparsePauliOp, OperatorBase], + coeff: Union[complex, ParameterExpression] = 1.0, + ) -> None: """ - Args: - primitive: The operator primitive being wrapped. - coeff: A coefficient multiplying the primitive. + Args: + primitive: The operator primitive being wrapped. + coeff: A coefficient multiplying the primitive. """ super().__init__() self._primitive = primitive @@ -97,7 +108,7 @@ def __init__(self, @property def primitive(self) -> Union[QuantumCircuit, Operator, Pauli, SparsePauliOp, OperatorBase]: - """ The primitive defining the underlying function of the Operator. + """The primitive defining the underlying function of the Operator. Returns: The primitive object. @@ -132,8 +143,10 @@ def equals(self, other: OperatorBase) -> bool: def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase: if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError('Operators can only be scalar multiplied by float or complex, not ' - '{} of type {}.'.format(scalar, type(scalar))) + raise ValueError( + "Operators can only be scalar multiplied by float or complex, not " + "{} of type {}.".format(scalar, type(scalar)) + ) # Need to return self.__class__ in case the object is one of the inherited OpPrimitives return self.__class__(self.primitive, coeff=self.coeff * scalar) @@ -145,17 +158,18 @@ def tensorpower(self, other: int) -> Union[OperatorBase, int]: if other == 0: return 1 if not isinstance(other, int) or other < 0: - raise TypeError('Tensorpower can only take positive int arguments') + raise TypeError("Tensorpower can only take positive int arguments") temp = PrimitiveOp(self.primitive, coeff=self.coeff) # type: OperatorBase for _ in range(other - 1): temp = temp.tensor(self) return temp - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> \ - OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: # pylint: disable=cyclic-import from ..list_ops.composed_op import ComposedOp + new_self, other = self._expand_shorter_operator_and_permute(other, permutation) if isinstance(other, ComposedOp): comp_with_first = new_self.compose(other.oplist[0]) @@ -168,7 +182,7 @@ def compose(self, other: OperatorBase, def power(self, exponent: int) -> OperatorBase: if not isinstance(exponent, int) or exponent <= 0: - raise TypeError('power can only take positive int arguments') + raise TypeError("power can only take positive int arguments") temp = PrimitiveOp(self.primitive, coeff=self.coeff) # type: OperatorBase for _ in range(exponent - 1): temp = temp.compose(self) @@ -181,9 +195,10 @@ def permute(self, permutation: List[int]) -> OperatorBase: raise NotImplementedError def exp_i(self) -> OperatorBase: - """ Return Operator exponentiation, equaling e^(-i * op)""" + """Return Operator exponentiation, equaling e^(-i * op)""" # pylint: disable=cyclic-import from ..evolutions.evolved_op import EvolvedOp + return EvolvedOp(self) def log_i(self, massive: bool = False) -> OperatorBase: @@ -193,8 +208,12 @@ def log_i(self, massive: bool = False) -> OperatorBase: # pylint: disable=cyclic-import from ..operator_globals import EVAL_SIG_DIGITS from .matrix_op import MatrixOp - return MatrixOp(np.around(scipy.linalg.logm(self.to_matrix(massive=massive)) / -1j, - decimals=EVAL_SIG_DIGITS)) + + return MatrixOp( + np.around( + scipy.linalg.logm(self.to_matrix(massive=massive)) / -1j, decimals=EVAL_SIG_DIGITS + ) + ) def __str__(self) -> str: raise NotImplementedError @@ -226,6 +245,7 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: if isinstance(unrolled_dict, list): # pylint: disable=cyclic-import from ..list_ops.list_op import ListOp + return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) if self.coeff.parameters <= set(unrolled_dict.keys()): binds = {param: unrolled_dict[param] for param in self.coeff.parameters} @@ -240,47 +260,53 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: raise NotImplementedError def to_matrix_op(self, massive: bool = False) -> OperatorBase: - """ Returns a ``MatrixOp`` equivalent to this Operator. """ + """Returns a ``MatrixOp`` equivalent to this Operator.""" coeff = self.coeff op = self.copy() op._coeff = 1 prim_mat = op.to_matrix(massive=massive) from .matrix_op import MatrixOp + return MatrixOp(prim_mat, coeff=coeff) def to_instruction(self) -> Instruction: - """ Returns an ``Instruction`` equivalent to this Operator. """ + """Returns an ``Instruction`` equivalent to this Operator.""" raise NotImplementedError def to_circuit(self) -> QuantumCircuit: - """ Returns a ``QuantumCircuit`` equivalent to this Operator. """ + """Returns a ``QuantumCircuit`` equivalent to this Operator.""" qc = QuantumCircuit(self.num_qubits) qc.append(self.to_instruction(), qargs=range(self.primitive.num_qubits)) return qc.decompose() def to_circuit_op(self) -> OperatorBase: - """ Returns a ``CircuitOp`` equivalent to this Operator. """ + """Returns a ``CircuitOp`` equivalent to this Operator.""" from .circuit_op import CircuitOp + if self.coeff == 0: return CircuitOp(QuantumCircuit(self.num_qubits), coeff=0) return CircuitOp(self.to_circuit(), coeff=self.coeff) def to_pauli_op(self, massive: bool = False) -> OperatorBase: - """ Returns a sum of ``PauliOp`` s equivalent to this Operator. """ + """Returns a sum of ``PauliOp`` s equivalent to this Operator.""" # pylint: disable=cyclic-import from .matrix_op import MatrixOp + mat_op = cast(MatrixOp, self.to_matrix_op(massive=massive)) sparse_pauli = SparsePauliOp.from_operator(mat_op.primitive) if not sparse_pauli.to_list(): from ..operator_globals import I + return (I ^ self.num_qubits) * 0.0 from .pauli_op import PauliOp + if len(sparse_pauli) == 1: label, coeff = sparse_pauli.to_list()[0] coeff = coeff.real if np.isreal(coeff) else coeff return PauliOp(Pauli(label), coeff * self.coeff) from ..list_ops.summed_op import SummedOp + return SummedOp( [ PrimitiveOp( diff --git a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py index 0aac5cb62709..f8505faed6d4 100644 --- a/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py +++ b/qiskit/opflow/primitive_ops/tapered_pauli_sum_op.py @@ -73,7 +73,7 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: class Z2Symmetries: - """ Z2 Symmetries """ + """Z2 Symmetries""" def __init__( self, @@ -118,12 +118,12 @@ def __init__( @property def symmetries(self): - """ return symmetries """ + """return symmetries""" return self._symmetries @property def sq_paulis(self): - """ returns sq paulis """ + """returns sq paulis""" return self._sq_paulis @property @@ -141,17 +141,17 @@ def cliffords(self) -> List[PauliSumOp]: @property def sq_list(self): - """ returns sq list """ + """returns sq list""" return self._sq_list @property def tapering_values(self): - """ returns tapering values """ + """returns tapering values""" return self._tapering_values @tapering_values.setter def tapering_values(self, new_value): - """ set tapering values """ + """set tapering values""" self._tapering_values = new_value def __str__(self): @@ -238,7 +238,7 @@ def find_Z2_symmetries(cls, operator: PauliSumOp) -> "Z2Symmetries": Pauli( ( stacked_symmetries[row, : symm_shape[1] // 2], - stacked_symmetries[row, symm_shape[1] // 2:], + stacked_symmetries[row, symm_shape[1] // 2 :], ) ) ) @@ -439,9 +439,9 @@ def _kernel_F2(matrix_in) -> List[np.ndarray]: # pylint: disable=invalid-name for col in range(size[1]): if np.array_equal( - matrix_in_id_ech[0: size[0], col], np.zeros(size[0]) - ) and not np.array_equal(matrix_in_id_ech[size[0]:, col], np.zeros(size[1])): - kernel.append(matrix_in_id_ech[size[0]:, col]) + matrix_in_id_ech[0 : size[0], col], np.zeros(size[0]) + ) and not np.array_equal(matrix_in_id_ech[size[0] :, col], np.zeros(size[1])): + kernel.append(matrix_in_id_ech[size[0] :, col]) return kernel @@ -476,7 +476,7 @@ def _row_echelon_F2(matrix_in) -> np.ndarray: # pylint: disable=invalid-name for row in np.sort(indices)[::-1]: matrix_out_temp = np.delete(matrix_out_temp, (row), axis=0) - matrix_out[0: size[0] - len(indices), :] = matrix_out_temp + matrix_out[0 : size[0] - len(indices), :] = matrix_out_temp matrix_out = matrix_out.astype(int) return matrix_out diff --git a/qiskit/opflow/state_fns/__init__.py b/qiskit/opflow/state_fns/__init__.py index 0c8183276d61..5cfe9e7c2a1f 100644 --- a/qiskit/opflow/state_fns/__init__.py +++ b/qiskit/opflow/state_fns/__init__.py @@ -61,9 +61,11 @@ from .circuit_state_fn import CircuitStateFn from .cvar_measurement import CVaRMeasurement -__all__ = ['StateFn', - 'DictStateFn', - 'VectorStateFn', - 'CircuitStateFn', - 'OperatorStateFn', - 'CVaRMeasurement'] +__all__ = [ + "StateFn", + "DictStateFn", + "VectorStateFn", + "CircuitStateFn", + "OperatorStateFn", + "CVaRMeasurement", +] diff --git a/qiskit/opflow/state_fns/circuit_state_fn.py b/qiskit/opflow/state_fns/circuit_state_fn.py index aaae910bf1bb..43c4bb16fd68 100644 --- a/qiskit/opflow/state_fns/circuit_state_fn.py +++ b/qiskit/opflow/state_fns/circuit_state_fn.py @@ -44,10 +44,12 @@ class CircuitStateFn(StateFn): primitive: QuantumCircuit # TODO allow normalization somehow? - def __init__(self, - primitive: Union[QuantumCircuit, Instruction] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: Union[QuantumCircuit, Instruction] = None, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ Args: primitive: The ``QuantumCircuit`` (or ``Instruction``, which will be converted) which @@ -64,17 +66,19 @@ def __init__(self, primitive = qc if not isinstance(primitive, QuantumCircuit): - raise TypeError('CircuitStateFn can only be instantiated ' - 'with QuantumCircuit, not {}'.format(type(primitive))) + raise TypeError( + "CircuitStateFn can only be instantiated " + "with QuantumCircuit, not {}".format(type(primitive)) + ) if len(primitive.clbits) != 0: - raise TypeError('CircuitOp does not support QuantumCircuits with ClassicalRegisters.') + raise TypeError("CircuitOp does not support QuantumCircuits with ClassicalRegisters.") super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) @staticmethod - def from_dict(density_dict: dict) -> 'CircuitStateFn': - """ Construct the CircuitStateFn from a dict mapping strings to probability densities. + def from_dict(density_dict: dict) -> "CircuitStateFn": + """Construct the CircuitStateFn from a dict mapping strings to probability densities. Args: density_dict: The dict representing the desired state. @@ -91,7 +95,7 @@ def from_dict(density_dict: dict) -> 'CircuitStateFn': qc = QuantumCircuit(len(bstr)) # NOTE: Reversing endianness!! for (index, bit) in enumerate(reversed(bstr)): - if bit == '1': + if bit == "1": qc.x(index) sf_circuit = CircuitStateFn(qc, coeff=prob) statefn_circuits += [sf_circuit] @@ -104,8 +108,8 @@ def from_dict(density_dict: dict) -> 'CircuitStateFn': return CircuitStateFn.from_vector(sf_dict.to_matrix()) @staticmethod - def from_vector(statevector: np.ndarray) -> 'CircuitStateFn': - """ Construct the CircuitStateFn from a vector representing the statevector. + def from_vector(statevector: np.ndarray) -> "CircuitStateFn": + """Construct the CircuitStateFn from a vector representing the statevector. Args: statevector: The statevector representing the desired state. @@ -118,7 +122,7 @@ def from_vector(statevector: np.ndarray) -> 'CircuitStateFn': return CircuitStateFn(Initialize(normalized_sv), coeff=normalization_coeff) def primitive_strings(self) -> Set[str]: - return {'QuantumCircuit'} + return {"QuantumCircuit"} @property def num_qubits(self) -> int: @@ -126,9 +130,11 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: - raise ValueError('Sum over operators with different numbers of qubits, ' - '{} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + raise ValueError( + "Sum over operators with different numbers of qubits, " + "{} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) if isinstance(other, CircuitStateFn) and self.primitive == other.primitive: return CircuitStateFn(self.primitive, coeff=self.coeff + other.coeff) @@ -140,19 +146,22 @@ def adjoint(self) -> "CircuitStateFn": try: inverse = self.primitive.inverse() except CircuitError as missing_inverse: - raise OpflowError('Failed to take the inverse of the underlying circuit, the circuit ' - 'is likely not unitary and can therefore not be inverted.') \ - from missing_inverse - - return CircuitStateFn(inverse, - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement)) - - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + raise OpflowError( + "Failed to take the inverse of the underlying circuit, the circuit " + "is likely not unitary and can therefore not be inverted." + ) from missing_inverse + + return CircuitStateFn( + inverse, coeff=self.coeff.conjugate(), is_measurement=(not self.is_measurement) + ) + + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: if not self.is_measurement and not front: raise ValueError( - 'Composition with a Statefunctions in the first operand is not defined.') + "Composition with a Statefunctions in the first operand is not defined." + ) new_self, other = self._expand_shorter_operator_and_permute(other, permutation) if front: @@ -165,15 +174,19 @@ def compose(self, other: OperatorBase, composed_op_circs = cast(CircuitOp, op_circuit_self.compose(other.to_circuit_op())) # Returning CircuitStateFn - return CircuitStateFn(composed_op_circs.primitive, - is_measurement=self.is_measurement, - coeff=self.coeff * other.coeff) + return CircuitStateFn( + composed_op_circs.primitive, + is_measurement=self.is_measurement, + coeff=self.coeff * other.coeff, + ) if isinstance(other, CircuitStateFn) and self.is_measurement: # pylint: disable=cyclic-import from ..operator_globals import Zero - return self.compose(CircuitOp(other.primitive, - other.coeff)).compose(Zero ^ self.num_qubits) + + return self.compose(CircuitOp(other.primitive, other.coeff)).compose( + Zero ^ self.num_qubits + ) return ComposedOp([new_self, other]) @@ -202,9 +215,11 @@ def tensor(self, other: OperatorBase) -> Union["CircuitStateFn", TensoredOp]: c_op_other = CircuitOp(other.primitive, other.coeff) c_op = c_op_self.tensor(c_op_other) if isinstance(c_op, CircuitOp): - return CircuitStateFn(primitive=c_op.primitive, # pylint: disable=no-member - coeff=c_op.coeff, - is_measurement=self.is_measurement) + return CircuitStateFn( + primitive=c_op.primitive, # pylint: disable=no-member + coeff=c_op.coeff, + is_measurement=self.is_measurement, + ) return TensoredOp([self, other]) def to_density_matrix(self, massive: bool = False) -> np.ndarray: @@ -217,34 +232,37 @@ def to_density_matrix(self, massive: bool = False) -> np.ndarray: to classical tools is appropriate. """ - OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) # Rely on VectorStateFn's logic here. return VectorStateFn(self.to_matrix(massive=massive) * self.coeff).to_density_matrix() def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', False, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) # Need to adjoint to get forward statevector and then reverse if self.is_measurement: return np.conj(self.adjoint().to_matrix(massive=massive)) qc = self.to_circuit(meas=False) - statevector_backend = BasicAer.get_backend('statevector_simulator') + statevector_backend = BasicAer.get_backend("statevector_simulator") transpiled = transpile(qc, statevector_backend, optimization_level=0) statevector = statevector_backend.run(transpiled).result().get_statevector() from ..operator_globals import EVAL_SIG_DIGITS + return np.round(statevector * self.coeff, decimals=EVAL_SIG_DIGITS) def __str__(self) -> str: qc = cast(CircuitStateFn, self.reduce()).to_circuit() - prim_str = str(qc.draw(output='text')) + prim_str = str(qc.draw(output="text")) if self.coeff == 1.0: - return "{}(\n{}\n)".format('CircuitStateFn' if not self.is_measurement - else 'CircuitMeasurement', prim_str) + return "{}(\n{}\n)".format( + "CircuitStateFn" if not self.is_measurement else "CircuitMeasurement", prim_str + ) else: - return "{}(\n{}\n) * {}".format('CircuitStateFn' if not self.is_measurement - else 'CircuitMeasurement', - prim_str, - self.coeff) + return "{}(\n{}\n) * {}".format( + "CircuitStateFn" if not self.is_measurement else "CircuitMeasurement", + prim_str, + self.coeff, + ) def assign_parameters(self, param_dict: dict) -> Union["CircuitStateFn", ListOp]: param_value = self.coeff @@ -253,8 +271,9 @@ def assign_parameters(self, param_dict: dict) -> Union["CircuitStateFn", ListOp] unrolled_dict = self._unroll_param_dict(param_dict) if isinstance(unrolled_dict, list): return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) - if isinstance(self.coeff, ParameterExpression) \ - and self.coeff.parameters <= set(unrolled_dict.keys()): + if isinstance(self.coeff, ParameterExpression) and self.coeff.parameters <= set( + unrolled_dict.keys() + ): param_instersection = set(unrolled_dict.keys()) & self.coeff.parameters binds = {param: unrolled_dict[param] for param in param_instersection} param_value = float(self.coeff.bind(binds)) @@ -279,12 +298,14 @@ def eval( if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( - 'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking ' - 'sf.adjoint() first to convert to measurement.') + "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " + "sf.adjoint() first to convert to measurement." + ) if isinstance(front, ListOp) and front.distributive: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) # Composable with circuit if isinstance(front, (PauliOp, CircuitOp, MatrixOp, CircuitStateFn)): @@ -294,7 +315,7 @@ def eval( return self.to_matrix_op().eval(front) def to_circuit(self, meas: bool = False) -> QuantumCircuit: - """ Return QuantumCircuit representing StateFn """ + """Return QuantumCircuit representing StateFn""" if meas: meas_qc = self.primitive.copy() meas_qc.add_register(ClassicalRegister(self.num_qubits)) @@ -304,25 +325,24 @@ def to_circuit(self, meas: bool = False) -> QuantumCircuit: return self.primitive def to_circuit_op(self) -> OperatorBase: - """ Return ``StateFnCircuit`` corresponding to this StateFn.""" + """Return ``StateFnCircuit`` corresponding to this StateFn.""" return self def to_instruction(self): - """ Return Instruction corresponding to primitive. """ + """Return Instruction corresponding to primitive.""" return self.primitive.to_instruction() # TODO specify backend? - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False) -> dict: + def sample( + self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False + ) -> dict: """ Sample the state function as a normalized probability distribution. Returns dict of bitstrings in order of probability, with values being probability. """ - OperatorBase._check_massive('sample', False, self.num_qubits, massive) + OperatorBase._check_massive("sample", False, self.num_qubits, massive) qc = self.to_circuit(meas=True) - qasm_backend = BasicAer.get_backend('qasm_simulator') + qasm_backend = BasicAer.get_backend("qasm_simulator") transpiled = transpile(qc, qasm_backend, optimization_level=0) counts = qasm_backend.run(transpiled, shots=shots).result().get_counts() if reverse_endianness: @@ -340,17 +360,18 @@ def reduce(self) -> "CircuitStateFn": # Check if Identity or empty instruction (need to check that type is exactly # Instruction because some gates have lazy gate.definition population) # pylint: disable=unidiomatic-typecheck - if isinstance(gate, IGate) or (type(gate) == Instruction and - gate.definition.data == []): + if isinstance(gate, IGate) or ( + type(gate) == Instruction and gate.definition.data == [] + ): del self.primitive.data[i] return self - def _expand_dim(self, num_qubits: int) -> 'CircuitStateFn': + def _expand_dim(self, num_qubits: int) -> "CircuitStateFn": # this is equivalent to self.tensor(identity_operator), but optimized for better performance # just like in tensor method, qiskit endianness is reversed here return self.permute(list(range(num_qubits, num_qubits + self.num_qubits))) - def permute(self, permutation: List[int]) -> 'CircuitStateFn': + def permute(self, permutation: List[int]) -> "CircuitStateFn": r""" Permute the qubits of the circuit. diff --git a/qiskit/opflow/state_fns/cvar_measurement.py b/qiskit/opflow/state_fns/cvar_measurement.py index 29531f2cb259..583d760767b7 100644 --- a/qiskit/opflow/state_fns/cvar_measurement.py +++ b/qiskit/opflow/state_fns/cvar_measurement.py @@ -40,10 +40,12 @@ class CVaRMeasurement(OperatorStateFn): primitive: OperatorBase # TODO allow normalization somehow? - def __init__(self, - primitive: Union[OperatorBase] = None, - alpha: float = 1.0, - coeff: Union[complex, ParameterExpression] = 1.0) -> None: + def __init__( + self, + primitive: Union[OperatorBase] = None, + alpha: float = 1.0, + coeff: Union[complex, ParameterExpression] = 1.0, + ) -> None: """ Args: primitive: The ``OperatorBase`` which defines the diagonal operator @@ -65,12 +67,13 @@ def __init__(self, raise ValueError if not 0 <= alpha <= 1: - raise ValueError('The parameter alpha must be in [0, 1].') + raise ValueError("The parameter alpha must be in [0, 1].") self._alpha = alpha if not _check_is_diagonal(primitive): - raise OpflowError('Input operator to CVaRMeasurement must be diagonal, but is not:', - str(primitive)) + raise OpflowError( + "Input operator to CVaRMeasurement must be diagonal, but is not:", str(primitive) + ) super().__init__(primitive, coeff=coeff, is_measurement=True) @@ -100,21 +103,22 @@ def adjoint(self): Raises: OpflowError: The adjoint of a CVaRMeasurement is not defined. """ - raise OpflowError('Adjoint of a CVaR measurement not defined') + raise OpflowError("Adjoint of a CVaR measurement not defined") def mul(self, scalar: Union[complex, ParameterExpression]) -> "CVaRMeasurement": if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError('Operators can only be scalar multiplied by float or complex, not ' - '{} of type {}.'.format(scalar, type(scalar))) + raise ValueError( + "Operators can only be scalar multiplied by float or complex, not " + "{} of type {}.".format(scalar, type(scalar)) + ) - return self.__class__(self.primitive, - coeff=self.coeff * scalar, - alpha=self._alpha) + return self.__class__(self.primitive, coeff=self.coeff * scalar, alpha=self._alpha) def tensor(self, other: OperatorBase) -> Union["OperatorStateFn", TensoredOp]: if isinstance(other, OperatorStateFn): - return OperatorStateFn(self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff) + return OperatorStateFn( + self.primitive.tensor(other.primitive), coeff=self.coeff * other.coeff + ) return TensoredOp([self, other]) def to_density_matrix(self, massive: bool = False): @@ -134,10 +138,11 @@ def to_circuit_op(self): raise NotImplementedError def __str__(self) -> str: - return 'CVaRMeasurement({}) * {}'.format(str(self.primitive), self.coeff) + return "CVaRMeasurement({}) * {}".format(str(self.primitive), self.coeff) - def eval(self, - front: Union[str, dict, np.ndarray, OperatorBase, Statevector] = None) -> complex: + def eval( + self, front: Union[str, dict, np.ndarray, OperatorBase, Statevector] = None + ) -> complex: r""" Given the energies of each sampled measurement outcome (H_i) as well as the sampling probability of each measurement outcome (p_i, we can compute the @@ -188,8 +193,8 @@ def eval_variance( is computed as H_j^2 + 1/α*(sum_i complex: for h_i, p_i in zip(energies, probabilities): cvar += p_i * (h_i - h_j) - return self.coeff * cvar/alpha + return self.coeff * cvar / alpha - def traverse(self, - convert_fn: Callable, - coeff: Optional[Union[complex, ParameterExpression]] = None - ) -> OperatorBase: + def traverse( + self, convert_fn: Callable, coeff: Optional[Union[complex, ParameterExpression]] = None + ) -> OperatorBase: r""" Apply the convert_fn to the internal primitive if the primitive is an Operator (as in the case of ``OperatorStateFn``). Otherwise do nothing. Used by converters. @@ -333,10 +337,7 @@ def traverse(self, return self.__class__(convert_fn(self.primitive), coeff=coeff, alpha=self._alpha) return self - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False): + def sample(self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False): raise NotImplementedError diff --git a/qiskit/opflow/state_fns/dict_state_fn.py b/qiskit/opflow/state_fns/dict_state_fn.py index 73507140f0cc..7a6e3f095fb0 100644 --- a/qiskit/opflow/state_fns/dict_state_fn.py +++ b/qiskit/opflow/state_fns/dict_state_fn.py @@ -30,25 +30,28 @@ class DictStateFn(StateFn): - """ A class for state functions and measurements which are defined by a lookup table, + """A class for state functions and measurements which are defined by a lookup table, stored in a dict. """ + primitive: Dict[str, complex] # TODO allow normalization somehow? - def __init__(self, - primitive: Union[str, dict, Result] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: Union[str, dict, Result] = None, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ - Args: - primitive: The dict, single bitstring (if defining a basis sate), or Qiskit - Result, which defines the behavior of the underlying function. - coeff: A coefficient by which to multiply the state function. - is_measurement: Whether the StateFn is a measurement operator. - - Raises: - TypeError: invalid parameters. + Args: + primitive: The dict, single bitstring (if defining a basis sate), or Qiskit + Result, which defines the behavior of the underlying function. + coeff: A coefficient by which to multiply the state function. + is_measurement: Whether the StateFn is a measurement operator. + + Raises: + TypeError: invalid parameters. """ # If the initial density is a string, treat this as a density dict # with only a single basis state. @@ -66,18 +69,20 @@ def __init__(self, if isinstance(primitive, Result): counts = primitive.get_counts() # NOTE: Need to square root to take correct Pauli measurements! - primitive = {bstr: (shots / sum(counts.values()))**.5 for - (bstr, shots) in counts.items()} + primitive = { + bstr: (shots / sum(counts.values())) ** 0.5 for (bstr, shots) in counts.items() + } if not isinstance(primitive, dict): raise TypeError( - 'DictStateFn can only be instantiated with dict, ' - 'string, or Qiskit Result, not {}'.format(type(primitive))) + "DictStateFn can only be instantiated with dict, " + "string, or Qiskit Result, not {}".format(type(primitive)) + ) super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) def primitive_strings(self) -> Set[str]: - return {'Dict'} + return {"Dict"} @property def num_qubits(self) -> int: @@ -86,72 +91,94 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over statefns with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, DictStateFn) and self.is_measurement == other.is_measurement: # TODO add compatibility with vector and Operator? if self.primitive == other.primitive: - return DictStateFn(self.primitive, - coeff=self.coeff + other.coeff, - is_measurement=self.is_measurement) + return DictStateFn( + self.primitive, + coeff=self.coeff + other.coeff, + is_measurement=self.is_measurement, + ) else: - new_dict = {b: (v * self.coeff) + (other.primitive.get(b, 0) * other.coeff) - for (b, v) in self.primitive.items()} - new_dict.update({b: v * other.coeff for (b, v) in other.primitive.items() - if b not in self.primitive}) + new_dict = { + b: (v * self.coeff) + (other.primitive.get(b, 0) * other.coeff) + for (b, v) in self.primitive.items() + } + new_dict.update( + { + b: v * other.coeff + for (b, v) in other.primitive.items() + if b not in self.primitive + } + ) return DictStateFn(new_dict, is_measurement=self._is_measurement) # pylint: disable=cyclic-import from ..list_ops.summed_op import SummedOp + return SummedOp([self, other]) def adjoint(self) -> "DictStateFn": - return DictStateFn({b: np.conj(v) for (b, v) in self.primitive.items()}, - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement)) + return DictStateFn( + {b: np.conj(v) for (b, v) in self.primitive.items()}, + coeff=self.coeff.conjugate(), + is_measurement=(not self.is_measurement), + ) - def permute(self, permutation: List[int]) -> 'DictStateFn': + def permute(self, permutation: List[int]) -> "DictStateFn": new_num_qubits = max(permutation) + 1 if self.num_qubits != len(permutation): raise OpflowError("New index must be defined for each qubit of the operator.") # helper function to permute the key def perm(key): - list_key = ['0'] * new_num_qubits + list_key = ["0"] * new_num_qubits for i, k in enumerate(permutation): list_key[k] = key[i] - return ''.join(list_key) + return "".join(list_key) new_dict = {perm(key): value for key, value in self.primitive.items()} return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - def _expand_dim(self, num_qubits: int) -> 'DictStateFn': - pad = '0'*num_qubits + def _expand_dim(self, num_qubits: int) -> "DictStateFn": + pad = "0" * num_qubits new_dict = {key + pad: value for key, value in self.primitive.items()} return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) def tensor(self, other: OperatorBase) -> OperatorBase: # Both dicts if isinstance(other, DictStateFn): - new_dict = {k1 + k2: v1 * v2 for ((k1, v1,), (k2, v2)) in - itertools.product(self.primitive.items(), other.primitive.items())} - return StateFn(new_dict, - coeff=self.coeff * other.coeff, - is_measurement=self.is_measurement) + new_dict = { + k1 + k2: v1 * v2 + for ( + ( + k1, + v1, + ), + (k2, v2), + ) in itertools.product(self.primitive.items(), other.primitive.items()) + } + return StateFn( + new_dict, coeff=self.coeff * other.coeff, is_measurement=self.is_measurement + ) # pylint: disable=cyclic-import from ..list_ops.tensored_op import TensoredOp + return TensoredOp([self, other]) def to_density_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) states = int(2 ** self.num_qubits) return self.to_matrix(massive=massive) * np.eye(states) * self.coeff def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', False, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) states = int(2 ** self.num_qubits) - probs = np.zeros(states) + 0.j + probs = np.zeros(states) + 0.0j for k, v in self.primitive.items(): probs[int(k, 2)] = v vec = probs * self.coeff @@ -171,31 +198,36 @@ def to_spmatrix(self) -> sparse.spmatrix: indices = [int(v, 2) for v in self.primitive.keys()] vals = np.array(list(self.primitive.values())) * self.coeff - spvec = sparse.csr_matrix((vals, (np.zeros(len(indices), dtype=int), indices)), - shape=(1, 2**self.num_qubits)) + spvec = sparse.csr_matrix( + (vals, (np.zeros(len(indices), dtype=int), indices)), shape=(1, 2 ** self.num_qubits) + ) return spvec if not self.is_measurement else spvec.transpose() def to_spmatrix_op(self) -> OperatorBase: """Convert this state function to a ``SparseVectorStateFn``.""" from .sparse_vector_state_fn import SparseVectorStateFn + return SparseVectorStateFn(self.to_spmatrix(), self.coeff, self.is_measurement) def to_circuit_op(self) -> OperatorBase: """Convert this state function to a ``CircuitStateFn``.""" from .circuit_state_fn import CircuitStateFn + csfn = CircuitStateFn.from_dict(self.primitive) * self.coeff return csfn.adjoint() if self.is_measurement else csfn def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return "{}({})".format('DictStateFn' if not self.is_measurement - else 'DictMeasurement', prim_str) + return "{}({})".format( + "DictStateFn" if not self.is_measurement else "DictMeasurement", prim_str + ) else: - return "{}({}) * {}".format('DictStateFn' if not self.is_measurement - else 'DictMeasurement', - prim_str, - self.coeff) + return "{}({}) * {}".format( + "DictStateFn" if not self.is_measurement else "DictMeasurement", + prim_str, + self.coeff, + ) # pylint: disable=too-many-return-statements def eval( @@ -210,12 +242,14 @@ def eval( if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( - 'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking ' - 'sf.adjoint() first to convert to measurement.') + "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " + "sf.adjoint() first to convert to measurement." + ) if isinstance(front, ListOp) and front.distributive: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) # For now, always do this. If it's not performant, we can be more granular. if not isinstance(front, OperatorBase): @@ -229,9 +263,14 @@ def eval( # zero. if isinstance(front, DictStateFn): return np.round( - cast(float, sum([v * front.primitive.get(b, 0) for (b, v) in - self.primitive.items()]) * self.coeff * front.coeff), - decimals=EVAL_SIG_DIGITS) + cast( + float, + sum([v * front.primitive.get(b, 0) for (b, v) in self.primitive.items()]) + * self.coeff + * front.coeff, + ), + decimals=EVAL_SIG_DIGITS, + ) # All remaining possibilities only apply when self.is_measurement is True @@ -240,17 +279,23 @@ def eval( # return sum([v * front.primitive.data[int(b, 2)] * # np.conj(front.primitive.data[int(b, 2)]) return np.round( - cast(float, sum([v * front.primitive.data[int(b, 2)] for (b, v) in - self.primitive.items()]) * self.coeff), - decimals=EVAL_SIG_DIGITS) + cast( + float, + sum([v * front.primitive.data[int(b, 2)] for (b, v) in self.primitive.items()]) + * self.coeff, + ), + decimals=EVAL_SIG_DIGITS, + ) from .circuit_state_fn import CircuitStateFn + if isinstance(front, CircuitStateFn): # Don't reimplement logic from CircuitStateFn self_adjoint = cast(DictStateFn, self.adjoint()) return np.conj(front.adjoint().eval(self_adjoint.primitive)) * self.coeff from .operator_state_fn import OperatorStateFn + if isinstance(front, OperatorStateFn): return cast(Union[OperatorBase, float, complex], front.adjoint().eval(self.adjoint())) @@ -259,15 +304,16 @@ def eval( adjointed_eval = cast(OperatorBase, front.adjoint().eval(self_adjoint.primitive)) return adjointed_eval.adjoint() * self.coeff - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False) -> Dict[str, float]: + def sample( + self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False + ) -> Dict[str, float]: probs = np.square(np.abs(np.array(list(self.primitive.values())))) - unique, counts = np.unique(algorithm_globals.random.choice(list(self.primitive.keys()), - size=shots, - p=(probs / sum(probs))), - return_counts=True) + unique, counts = np.unique( + algorithm_globals.random.choice( + list(self.primitive.keys()), size=shots, p=(probs / sum(probs)) + ), + return_counts=True, + ) counts = dict(zip(unique, counts)) if reverse_endianness: scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} diff --git a/qiskit/opflow/state_fns/operator_state_fn.py b/qiskit/opflow/state_fns/operator_state_fn.py index fb4f5e9b3719..8d343faadbcf 100644 --- a/qiskit/opflow/state_fns/operator_state_fn.py +++ b/qiskit/opflow/state_fns/operator_state_fn.py @@ -36,10 +36,12 @@ class OperatorStateFn(StateFn): primitive: OperatorBase # TODO allow normalization somehow? - def __init__(self, - primitive: OperatorBase, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: OperatorBase, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ Args: primitive: The ``OperatorBase`` which defines the behavior of the underlying State @@ -60,8 +62,9 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> Union["OperatorStateFn", SummedOp]: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over statefns with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, OperatorStateFn) and self.is_measurement == other.is_measurement: @@ -76,24 +79,31 @@ def add(self, other: OperatorBase) -> Union["OperatorStateFn", SummedOp]: # Also assumes scalar multiplication is available return OperatorStateFn( (self.coeff * self.primitive).add(other.primitive * other.coeff), - is_measurement=self._is_measurement) + is_measurement=self._is_measurement, + ) return SummedOp([self, other]) def adjoint(self) -> "OperatorStateFn": - return OperatorStateFn(self.primitive.adjoint(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement)) - - def _expand_dim(self, num_qubits: int) -> 'OperatorStateFn': - return OperatorStateFn(self.primitive._expand_dim(num_qubits), - coeff=self.coeff, - is_measurement=self.is_measurement) - - def permute(self, permutation: List[int]) -> 'OperatorStateFn': - return OperatorStateFn(self.primitive.permute(permutation), - coeff=self.coeff, - is_measurement=self.is_measurement) + return OperatorStateFn( + self.primitive.adjoint(), + coeff=self.coeff.conjugate(), + is_measurement=(not self.is_measurement), + ) + + def _expand_dim(self, num_qubits: int) -> "OperatorStateFn": + return OperatorStateFn( + self.primitive._expand_dim(num_qubits), + coeff=self.coeff, + is_measurement=self.is_measurement, + ) + + def permute(self, permutation: List[int]) -> "OperatorStateFn": + return OperatorStateFn( + self.primitive.permute(permutation), + coeff=self.coeff, + is_measurement=self.is_measurement, + ) def tensor(self, other: OperatorBase) -> Union["OperatorStateFn", TensoredOp]: if isinstance(other, OperatorStateFn): @@ -106,20 +116,22 @@ def tensor(self, other: OperatorBase) -> Union["OperatorStateFn", TensoredOp]: return TensoredOp([self, other]) def to_density_matrix(self, massive: bool = False) -> np.ndarray: - """ Return numpy matrix of density operator, warn if more than 16 qubits + """Return numpy matrix of density operator, warn if more than 16 qubits to force the user to set massive=True if they want such a large matrix. Generally big methods like this should require the use of a converter, but in this case a convenience method for quick hacking and access to classical tools is - appropriate. """ - OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) + appropriate.""" + OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) return self.primitive.to_matrix() * self.coeff def to_matrix_op(self, massive: bool = False) -> "OperatorStateFn": - """ Return a MatrixOp for this operator. """ - return OperatorStateFn(self.primitive.to_matrix_op(massive=massive) * self.coeff, - is_measurement=self.is_measurement) + """Return a MatrixOp for this operator.""" + return OperatorStateFn( + self.primitive.to_matrix_op(massive=massive) * self.coeff, + is_measurement=self.is_measurement, + ) def to_matrix(self, massive: bool = False) -> np.ndarray: r""" @@ -144,7 +156,7 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: Raises: ValueError: Invalid parameters. """ - OperatorBase._check_massive('to_matrix', False, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) # Operator - return diagonal (real values, not complex), # not rank 1 decomposition (statevector)! mat = self.primitive.to_matrix(massive=massive) @@ -164,7 +176,7 @@ def diag_over_tree(op): return diag_over_tree(mat) def to_circuit_op(self): - r""" Return ``StateFnCircuit`` corresponding to this StateFn. Ignore for now because this is + r"""Return ``StateFnCircuit`` corresponding to this StateFn. Ignore for now because this is undefined. TODO maybe call to_pauli_op and diagonalize here, but that could be very inefficient, e.g. splitting one Stabilizer measurement into hundreds of 1 qubit Paulis.""" raise NotImplementedError @@ -172,13 +184,15 @@ def to_circuit_op(self): def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return "{}({})".format('OperatorStateFn' if not self.is_measurement - else 'OperatorMeasurement', prim_str) + return "{}({})".format( + "OperatorStateFn" if not self.is_measurement else "OperatorMeasurement", prim_str + ) else: return "{}({}) * {}".format( - 'OperatorStateFn' if not self.is_measurement else 'OperatorMeasurement', + "OperatorStateFn" if not self.is_measurement else "OperatorMeasurement", prim_str, - self.coeff) + self.coeff, + ) def eval( self, front: Optional[Union[str, dict, np.ndarray, OperatorBase, Statevector]] = None @@ -187,19 +201,23 @@ def eval( matrix = cast(MatrixOp, self.primitive.to_matrix_op()).primitive.data # pylint: disable=cyclic-import from .vector_state_fn import VectorStateFn + return VectorStateFn(matrix[0, :]) if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( - 'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking ' - 'sf.adjoint() first to convert to measurement.') + "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " + "sf.adjoint() first to convert to measurement." + ) if not isinstance(front, OperatorBase): front = StateFn(front) if isinstance(self.primitive, ListOp) and self.primitive.distributive: - evals = [OperatorStateFn(op, is_measurement=self.is_measurement).eval( - front) for op in self.primitive.oplist] + evals = [ + OperatorStateFn(op, is_measurement=self.is_measurement).eval(front) + for op in self.primitive.oplist + ] result = self.primitive.combo_fn(evals) if isinstance(result, list): multiplied = self.primitive.coeff * self.coeff * np.array(result) @@ -208,6 +226,7 @@ def eval( # pylint: disable=cyclic-import from .vector_state_fn import VectorStateFn + if isinstance(self.primitive, PauliSumOp) and isinstance(front, VectorStateFn): return ( front.primitive.expectation_value(self.primitive.primitive) @@ -220,8 +239,9 @@ def eval( # Can't use isinstance because this would include subclasses. # pylint: disable=unidiomatic-typecheck if isinstance(front, ListOp) and type(front) == ListOp: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) # If we evaluate against a circuit, evaluate it to a vector so we # make sure to only do the expensive circuit simulation once @@ -230,8 +250,5 @@ def eval( return front.adjoint().eval(cast(OperatorBase, self.primitive.eval(front))) * self.coeff - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False): + def sample(self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False): raise NotImplementedError diff --git a/qiskit/opflow/state_fns/sparse_vector_state_fn.py b/qiskit/opflow/state_fns/sparse_vector_state_fn.py index 9152d5d81ef5..52e59cb6bc4d 100644 --- a/qiskit/opflow/state_fns/sparse_vector_state_fn.py +++ b/qiskit/opflow/state_fns/sparse_vector_state_fn.py @@ -33,13 +33,16 @@ class SparseVectorStateFn(StateFn): This class uses ``scipy.sparse.spmatrix`` for the internal representation. """ + primitive: scipy.sparse.spmatrix # TODO allow normalization somehow? - def __init__(self, - primitive: scipy.sparse.spmatrix, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: scipy.sparse.spmatrix, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ Args: primitive: The underlying sparse vector. @@ -52,17 +55,17 @@ def __init__(self, """ if primitive.shape[0] != 1: - raise ValueError('The primitive must be a row vector of shape (x, 1).') + raise ValueError("The primitive must be a row vector of shape (x, 1).") # check if the primitive is a statevector of 2^n elements self._num_qubits = int(np.log2(primitive.shape[1])) if np.log2(primitive.shape[1]) != self._num_qubits: - raise ValueError('The number of vector elements must be a power of 2.') + raise ValueError("The number of vector elements must be a power of 2.") super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) def primitive_strings(self) -> Set[str]: - return {'SparseVector'} + return {"SparseVector"} @property def num_qubits(self) -> int: @@ -71,8 +74,9 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over statefns with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, SparseVectorStateFn) and self.is_measurement == other.is_measurement: @@ -83,9 +87,11 @@ def add(self, other: OperatorBase) -> OperatorBase: return SummedOp([self, other]) def adjoint(self) -> "SparseVectorStateFn": - return SparseVectorStateFn(self.primitive.conjugate(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement)) + return SparseVectorStateFn( + self.primitive.conjugate(), + coeff=self.coeff.conjugate(), + is_measurement=(not self.is_measurement), + ) def equals(self, other: OperatorBase) -> bool: if not isinstance(other, SparseVectorStateFn) or not self.coeff == other.coeff: @@ -110,11 +116,11 @@ def to_dict_fn(self) -> StateFn: num_qubits = self.num_qubits dok = self.primitive.todok() - new_dict = {format(i[1], 'b').zfill(num_qubits): v for i, v in dok.items()} + new_dict = {format(i[1], "b").zfill(num_qubits): v for i, v in dok.items()} return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', False, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) vec = self.primitive.toarray() * self.coeff return vec if not self.is_measurement else vec.reshape(1, -1) @@ -128,19 +134,23 @@ def to_circuit_op(self) -> OperatorBase: """Convert this state function to a ``CircuitStateFn``.""" # pylint: disable=cyclic-import from .circuit_state_fn import CircuitStateFn + csfn = CircuitStateFn.from_vector(self.primitive) * self.coeff return csfn.adjoint() if self.is_measurement else csfn def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return "{}({})".format('SparseVectorStateFn' if not self.is_measurement - else 'MeasurementSparseVector', prim_str) + return "{}({})".format( + "SparseVectorStateFn" if not self.is_measurement else "MeasurementSparseVector", + prim_str, + ) else: - return "{}({}) * {}".format('SparseVectorStateFn' if not self.is_measurement - else 'SparseMeasurementVector', - prim_str, - self.coeff) + return "{}({}) * {}".format( + "SparseVectorStateFn" if not self.is_measurement else "SparseMeasurementVector", + prim_str, + self.coeff, + ) # pylint: disable=too-many-return-statements def eval( @@ -153,12 +163,15 @@ def eval( return self if not self.is_measurement and isinstance(front, OperatorBase): - raise ValueError('Cannot compute overlap with StateFn or Operator if not Measurement. ' - 'Try taking sf.adjoint() first to convert to measurement.') + raise ValueError( + "Cannot compute overlap with StateFn or Operator if not Measurement. " + "Try taking sf.adjoint() first to convert to measurement." + ) if isinstance(front, ListOp) and front.distributive: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) if not isinstance(front, OperatorBase): front = StateFn(front) @@ -168,15 +181,24 @@ def eval( from .operator_state_fn import OperatorStateFn from .circuit_state_fn import CircuitStateFn from .dict_state_fn import DictStateFn + if isinstance(front, DictStateFn): - return np.round(sum([v * self.primitive.data[int(b, 2)] * front.coeff - for (b, v) in front.primitive.items()]) * self.coeff, - decimals=EVAL_SIG_DIGITS) + return np.round( + sum( + [ + v * self.primitive.data[int(b, 2)] * front.coeff + for (b, v) in front.primitive.items() + ] + ) + * self.coeff, + decimals=EVAL_SIG_DIGITS, + ) if isinstance(front, VectorStateFn): # Need to extract the element or np.array([1]) is returned. - return np.round(np.dot(self.to_matrix(), front.to_matrix())[0], - decimals=EVAL_SIG_DIGITS) + return np.round( + np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS + ) if isinstance(front, CircuitStateFn): # Don't reimplement logic from CircuitStateFn @@ -187,20 +209,20 @@ def eval( return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff # type: ignore - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False) -> dict: + def sample( + self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False + ) -> dict: as_dict = self.to_dict_fn().primitive all_states = sum(as_dict.keys()) deterministic_counts = {key: value / all_states for key, value in as_dict.items()} # Don't need to square because probabilities_dict already does. probs = np.array(list(deterministic_counts.values())) unique, counts = np.unique( - algorithm_globals.random.choice(list(deterministic_counts.keys()), - size=shots, - p=(probs / sum(probs))), - return_counts=True) + algorithm_globals.random.choice( + list(deterministic_counts.keys()), size=shots, p=(probs / sum(probs)) + ), + return_counts=True, + ) counts = dict(zip(unique, counts)) if reverse_endianness: scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} diff --git a/qiskit/opflow/state_fns/state_fn.py b/qiskit/opflow/state_fns/state_fn.py index 2621f76e7c4c..d824ffd604df 100644 --- a/qiskit/opflow/state_fns/state_fn.py +++ b/qiskit/opflow/state_fns/state_fn.py @@ -49,14 +49,23 @@ def __init_subclass__(cls): @staticmethod # pylint: disable=unused-argument - def __new__(cls, - primitive: Union[str, dict, Result, - list, np.ndarray, Statevector, - QuantumCircuit, Instruction, - OperatorBase] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> 'StateFn': - """ A factory method to produce the correct type of StateFn subclass + def __new__( + cls, + primitive: Union[ + str, + dict, + Result, + list, + np.ndarray, + Statevector, + QuantumCircuit, + Instruction, + OperatorBase, + ] = None, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> "StateFn": + """A factory method to produce the correct type of StateFn subclass based on the primitive passed in. Primitive, coeff, and is_measurement arguments are passed into subclass's init() as-is automatically by new(). @@ -79,31 +88,46 @@ def __new__(cls, # pylint: disable=cyclic-import if isinstance(primitive, (str, dict, Result)): from .dict_state_fn import DictStateFn + return DictStateFn.__new__(DictStateFn) if isinstance(primitive, (list, np.ndarray, Statevector)): from .vector_state_fn import VectorStateFn + return VectorStateFn.__new__(VectorStateFn) if isinstance(primitive, (QuantumCircuit, Instruction)): from .circuit_state_fn import CircuitStateFn + return CircuitStateFn.__new__(CircuitStateFn) if isinstance(primitive, OperatorBase): from .operator_state_fn import OperatorStateFn + return OperatorStateFn.__new__(OperatorStateFn) - raise TypeError('Unsupported primitive type {} passed into StateFn ' - 'factory constructor'.format(type(primitive))) + raise TypeError( + "Unsupported primitive type {} passed into StateFn " + "factory constructor".format(type(primitive)) + ) # TODO allow normalization somehow? - def __init__(self, - primitive: Union[str, dict, Result, - list, np.ndarray, Statevector, - QuantumCircuit, Instruction, - OperatorBase] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: Union[ + str, + dict, + Result, + list, + np.ndarray, + Statevector, + QuantumCircuit, + Instruction, + OperatorBase, + ] = None, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ Args: primitive: The primitive which defines the behavior of the underlying State function. @@ -117,17 +141,17 @@ def __init__(self, @property def primitive(self): - """ The primitive which defines the behavior of the underlying State function. """ + """The primitive which defines the behavior of the underlying State function.""" return self._primitive @property def coeff(self) -> Union[complex, ParameterExpression]: - """ A coefficient by which the state function is multiplied. """ + """A coefficient by which the state function is multiplied.""" return self._coeff @property def is_measurement(self) -> bool: - """ Whether the StateFn object is a measurement Operator. """ + """Whether the StateFn object is a measurement Operator.""" return self._is_measurement def primitive_strings(self) -> Set[str]: @@ -143,7 +167,7 @@ def add(self, other: OperatorBase) -> OperatorBase: def adjoint(self) -> OperatorBase: raise NotImplementedError - def _expand_dim(self, num_qubits: int) -> 'StateFn': + def _expand_dim(self, num_qubits: int) -> "StateFn": raise NotImplementedError def permute(self, permutation: List[int]) -> OperatorBase: @@ -167,12 +191,14 @@ def equals(self, other: OperatorBase) -> bool: def mul(self, scalar: Union[complex, ParameterExpression]) -> OperatorBase: if not isinstance(scalar, (int, float, complex, ParameterExpression)): - raise ValueError('Operators can only be scalar multiplied by float or complex, not ' - '{} of type {}.'.format(scalar, type(scalar))) + raise ValueError( + "Operators can only be scalar multiplied by float or complex, not " + "{} of type {}.".format(scalar, type(scalar)) + ) - return self.__class__(self.primitive, - coeff=self.coeff * scalar, - is_measurement=self.is_measurement) + return self.__class__( + self.primitive, coeff=self.coeff * scalar, is_measurement=self.is_measurement + ) def tensor(self, other: OperatorBase) -> OperatorBase: r""" @@ -198,10 +224,10 @@ def tensor(self, other: OperatorBase) -> OperatorBase: def tensorpower(self, other: int) -> Union[OperatorBase, int]: if not isinstance(other, int) or other <= 0: - raise TypeError('Tensorpower can only take positive int arguments') - temp = StateFn(self.primitive, - coeff=self.coeff, - is_measurement=self.is_measurement) # type: OperatorBase + raise TypeError("Tensorpower can only take positive int arguments") + temp = StateFn( + self.primitive, coeff=self.coeff, is_measurement=self.is_measurement + ) # type: OperatorBase for _ in range(other - 1): temp = temp.tensor(self) return temp @@ -212,12 +238,12 @@ def _expand_shorter_operator_and_permute( # pylint: disable=cyclic-import from ..operator_globals import Zero - if self == StateFn({'0': 1}, is_measurement=True): + if self == StateFn({"0": 1}, is_measurement=True): # Zero is special - we'll expand it to the correct qubit number. - return StateFn('0' * other.num_qubits, is_measurement=True), other + return StateFn("0" * other.num_qubits, is_measurement=True), other elif other == Zero: # Zero is special - we'll expand it to the correct qubit number. - return self, StateFn('0' * self.num_qubits) + return self, StateFn("0" * self.num_qubits) return super()._expand_shorter_operator_and_permute(other, permutation) @@ -225,7 +251,7 @@ def to_matrix(self, massive: bool = False) -> np.ndarray: raise NotImplementedError def to_density_matrix(self, massive: bool = False) -> np.ndarray: - """ Return matrix representing product of StateFn evaluated on pairs of basis states. + """Return matrix representing product of StateFn evaluated on pairs of basis states. Overridden by child classes. Args: @@ -240,8 +266,9 @@ def to_density_matrix(self, massive: bool = False) -> np.ndarray: """ raise NotImplementedError - def compose(self, other: OperatorBase, - permutation: Optional[List[int]] = None, front: bool = False) -> OperatorBase: + def compose( + self, other: OperatorBase, permutation: Optional[List[int]] = None, front: bool = False + ) -> OperatorBase: r""" Composition (Linear algebra-style: A@B(x) = A(B(x))) is not well defined for states in the binary function model, but is well defined for measurements. @@ -260,7 +287,8 @@ def compose(self, other: OperatorBase, # TODO maybe allow outers later to produce density operators or projectors, but not yet. if not self.is_measurement and not front: raise ValueError( - 'Composition with a Statefunction in the first operand is not defined.') + "Composition with a Statefunction in the first operand is not defined." + ) new_self, other = self._expand_shorter_operator_and_permute(other, permutation) @@ -269,19 +297,21 @@ def compose(self, other: OperatorBase, # TODO maybe include some reduction here in the subclasses - vector and Op, op and Op, etc. from ..primitive_ops.circuit_op import CircuitOp - if self.primitive == {'0' * self.num_qubits: 1.0} and isinstance(other, CircuitOp): + if self.primitive == {"0" * self.num_qubits: 1.0} and isinstance(other, CircuitOp): # Returning CircuitStateFn - return StateFn(other.primitive, is_measurement=self.is_measurement, - coeff=self.coeff * other.coeff) + return StateFn( + other.primitive, is_measurement=self.is_measurement, coeff=self.coeff * other.coeff + ) from ..list_ops.composed_op import ComposedOp + if isinstance(other, ComposedOp): return ComposedOp([new_self] + other.oplist, coeff=new_self.coeff * other.coeff) return ComposedOp([new_self, other]) def power(self, exponent: int) -> OperatorBase: - """ Compose with Self Multiple Times, undefined for StateFns. + """Compose with Self Multiple Times, undefined for StateFns. Args: exponent: The number of times to compose self with self. @@ -289,23 +319,23 @@ def power(self, exponent: int) -> OperatorBase: Raises: ValueError: This function is not defined for StateFns. """ - raise ValueError('Composition power over Statefunctions or Measurements is not defined.') + raise ValueError("Composition power over Statefunctions or Measurements is not defined.") def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return "{}({})".format('StateFunction' if not self.is_measurement - else 'Measurement', self.coeff) + return "{}({})".format( + "StateFunction" if not self.is_measurement else "Measurement", self.coeff + ) else: - return "{}({}) * {}".format('StateFunction' if not self.is_measurement - else 'Measurement', - self.coeff, - prim_str) + return "{}({}) * {}".format( + "StateFunction" if not self.is_measurement else "Measurement", self.coeff, prim_str + ) def __repr__(self) -> str: - return "{}({}, coeff={}, is_measurement={})".format(self.__class__.__name__, - repr(self.primitive), - self.coeff, self.is_measurement) + return "{}({}, coeff={}, is_measurement={})".format( + self.__class__.__name__, repr(self.primitive), self.coeff, self.is_measurement + ) def eval( self, @@ -330,6 +360,7 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: unrolled_dict = self._unroll_param_dict(param_dict) if isinstance(unrolled_dict, list): from ..list_ops.list_op import ListOp + return ListOp([self.assign_parameters(param_dict) for param_dict in unrolled_dict]) if self.coeff.parameters <= set(unrolled_dict.keys()): binds = {param: unrolled_dict[param] for param in self.coeff.parameters} @@ -340,10 +371,9 @@ def assign_parameters(self, param_dict: dict) -> OperatorBase: def reduce(self) -> OperatorBase: return self - def traverse(self, - convert_fn: Callable, - coeff: Optional[Union[complex, ParameterExpression]] = None - ) -> OperatorBase: + def traverse( + self, convert_fn: Callable, coeff: Optional[Union[complex, ParameterExpression]] = None + ) -> OperatorBase: r""" Apply the convert_fn to the internal primitive if the primitive is an Operator (as in the case of ``OperatorStateFn``). Otherwise do nothing. Used by converters. @@ -360,13 +390,14 @@ def traverse(self, coeff = self.coeff if isinstance(self.primitive, OperatorBase): - return StateFn(convert_fn(self.primitive), - coeff=coeff, is_measurement=self.is_measurement) + return StateFn( + convert_fn(self.primitive), coeff=coeff, is_measurement=self.is_measurement + ) else: return self def to_matrix_op(self, massive: bool = False) -> OperatorBase: - """ Return a ``VectorStateFn`` for this ``StateFn``. + """Return a ``VectorStateFn`` for this ``StateFn``. Args: massive: Whether to allow large conversions, e.g. creating a matrix representing @@ -377,19 +408,19 @@ def to_matrix_op(self, massive: bool = False) -> OperatorBase: """ # pylint: disable=cyclic-import from .vector_state_fn import VectorStateFn + return VectorStateFn(self.to_matrix(massive=massive), is_measurement=self.is_measurement) def to_circuit_op(self) -> OperatorBase: - """ Returns a ``CircuitOp`` equivalent to this Operator. """ + """Returns a ``CircuitOp`` equivalent to this Operator.""" raise NotImplementedError # TODO to_dict_op - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False) -> Dict[str, float]: - """ Sample the state function as a normalized probability distribution. Returns dict of + def sample( + self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False + ) -> Dict[str, float]: + """Sample the state function as a normalized probability distribution. Returns dict of bitstrings in order of probability, with values being probability. Args: diff --git a/qiskit/opflow/state_fns/vector_state_fn.py b/qiskit/opflow/state_fns/vector_state_fn.py index 125606fa7e64..626afdd8e70c 100644 --- a/qiskit/opflow/state_fns/vector_state_fn.py +++ b/qiskit/opflow/state_fns/vector_state_fn.py @@ -29,16 +29,19 @@ class VectorStateFn(StateFn): - """ A class for state functions and measurements which are defined in vector + """A class for state functions and measurements which are defined in vector representation, and stored using Terra's ``Statevector`` class. """ + primitive: Statevector # TODO allow normalization somehow? - def __init__(self, - primitive: Union[list, np.ndarray, Statevector] = None, - coeff: Union[complex, ParameterExpression] = 1.0, - is_measurement: bool = False) -> None: + def __init__( + self, + primitive: Union[list, np.ndarray, Statevector] = None, + coeff: Union[complex, ParameterExpression] = 1.0, + is_measurement: bool = False, + ) -> None: """ Args: primitive: The ``Statevector``, NumPy array, or list, which defines the behavior of @@ -54,7 +57,7 @@ def __init__(self, super().__init__(primitive, coeff=coeff, is_measurement=is_measurement) def primitive_strings(self) -> Set[str]: - return {'Vector'} + return {"Vector"} @property def num_qubits(self) -> int: @@ -63,22 +66,27 @@ def num_qubits(self) -> int: def add(self, other: OperatorBase) -> OperatorBase: if not self.num_qubits == other.num_qubits: raise ValueError( - 'Sum over statefns with different numbers of qubits, {} and {}, is not well ' - 'defined'.format(self.num_qubits, other.num_qubits)) + "Sum over statefns with different numbers of qubits, {} and {}, is not well " + "defined".format(self.num_qubits, other.num_qubits) + ) # Right now doesn't make sense to add a StateFn to a Measurement if isinstance(other, VectorStateFn) and self.is_measurement == other.is_measurement: # Covers Statevector and custom. - return VectorStateFn((self.coeff * self.primitive) + (other.primitive * other.coeff), - is_measurement=self._is_measurement) + return VectorStateFn( + (self.coeff * self.primitive) + (other.primitive * other.coeff), + is_measurement=self._is_measurement, + ) return SummedOp([self, other]) def adjoint(self) -> "VectorStateFn": - return VectorStateFn(self.primitive.conjugate(), - coeff=self.coeff.conjugate(), - is_measurement=(not self.is_measurement)) + return VectorStateFn( + self.primitive.conjugate(), + coeff=self.coeff.conjugate(), + is_measurement=(not self.is_measurement), + ) - def permute(self, permutation: List[int]) -> 'VectorStateFn': + def permute(self, permutation: List[int]) -> "VectorStateFn": new_self = self new_num_qubits = max(permutation) + 1 @@ -91,8 +99,9 @@ def permute(self, permutation: List[int]) -> 'VectorStateFn': qc = QuantumCircuit(new_num_qubits) # extend the permutation indices to match the size of the new matrix - permutation \ - = list(filter(lambda x: x not in permutation, range(new_num_qubits))) + permutation + permutation = ( + list(filter(lambda x: x not in permutation, range(new_num_qubits))) + permutation + ) # decompose permutation into sequence of transpositions transpositions = arithmetic.transpositions(permutation) @@ -100,12 +109,13 @@ def permute(self, permutation: List[int]) -> 'VectorStateFn': qc.swap(trans[0], trans[1]) from ..primitive_ops.circuit_op import CircuitOp + matrix = CircuitOp(qc).to_matrix() vector = new_self.primitive.data new_vector = cast(np.ndarray, matrix.dot(vector)) - return VectorStateFn(primitive=new_vector, - coeff=self.coeff, - is_measurement=self.is_measurement) + return VectorStateFn( + primitive=new_vector, coeff=self.coeff, is_measurement=self.is_measurement + ) def to_dict_fn(self) -> StateFn: """Creates the equivalent state function of type DictStateFn. @@ -116,28 +126,30 @@ def to_dict_fn(self) -> StateFn: from .dict_state_fn import DictStateFn num_qubits = self.num_qubits - new_dict = {format(i, 'b').zfill(num_qubits): v for i, v in enumerate(self.primitive.data)} + new_dict = {format(i, "b").zfill(num_qubits): v for i, v in enumerate(self.primitive.data)} return DictStateFn(new_dict, coeff=self.coeff, is_measurement=self.is_measurement) - def _expand_dim(self, num_qubits: int) -> 'VectorStateFn': - primitive = np.zeros(2**num_qubits, dtype=complex) - return VectorStateFn(self.primitive.tensor(primitive), - coeff=self.coeff, - is_measurement=self.is_measurement) + def _expand_dim(self, num_qubits: int) -> "VectorStateFn": + primitive = np.zeros(2 ** num_qubits, dtype=complex) + return VectorStateFn( + self.primitive.tensor(primitive), coeff=self.coeff, is_measurement=self.is_measurement + ) def tensor(self, other: OperatorBase) -> OperatorBase: if isinstance(other, VectorStateFn): - return StateFn(self.primitive.tensor(other.primitive), - coeff=self.coeff * other.coeff, - is_measurement=self.is_measurement) + return StateFn( + self.primitive.tensor(other.primitive), + coeff=self.coeff * other.coeff, + is_measurement=self.is_measurement, + ) return TensoredOp([self, other]) def to_density_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_density_matrix', True, self.num_qubits, massive) + OperatorBase._check_massive("to_density_matrix", True, self.num_qubits, massive) return self.primitive.to_operator().data * self.coeff def to_matrix(self, massive: bool = False) -> np.ndarray: - OperatorBase._check_massive('to_matrix', False, self.num_qubits, massive) + OperatorBase._check_massive("to_matrix", False, self.num_qubits, massive) vec = self.primitive.data * self.coeff return vec if not self.is_measurement else vec.reshape(1, -1) @@ -145,22 +157,25 @@ def to_matrix_op(self, massive: bool = False) -> OperatorBase: return self def to_circuit_op(self) -> OperatorBase: - """ Return ``StateFnCircuit`` corresponding to this StateFn.""" + """Return ``StateFnCircuit`` corresponding to this StateFn.""" # pylint: disable=cyclic-import from .circuit_state_fn import CircuitStateFn + csfn = CircuitStateFn.from_vector(self.primitive.data) * self.coeff return csfn.adjoint() if self.is_measurement else csfn def __str__(self) -> str: prim_str = str(self.primitive) if self.coeff == 1.0: - return "{}({})".format('VectorStateFn' if not self.is_measurement - else 'MeasurementVector', prim_str) + return "{}({})".format( + "VectorStateFn" if not self.is_measurement else "MeasurementVector", prim_str + ) else: - return "{}({}) * {}".format('VectorStateFn' if not self.is_measurement - else 'MeasurementVector', - prim_str, - self.coeff) + return "{}({}) * {}".format( + "VectorStateFn" if not self.is_measurement else "MeasurementVector", + prim_str, + self.coeff, + ) # pylint: disable=too-many-return-statements def eval( @@ -174,12 +189,14 @@ def eval( if not self.is_measurement and isinstance(front, OperatorBase): raise ValueError( - 'Cannot compute overlap with StateFn or Operator if not Measurement. Try taking ' - 'sf.adjoint() first to convert to measurement.') + "Cannot compute overlap with StateFn or Operator if not Measurement. Try taking " + "sf.adjoint() first to convert to measurement." + ) if isinstance(front, ListOp) and front.distributive: - return front.combo_fn([self.eval(front.coeff * front_elem) - for front_elem in front.oplist]) + return front.combo_fn( + [self.eval(front.coeff * front_elem) for front_elem in front.oplist] + ) if not isinstance(front, OperatorBase): front = StateFn(front) @@ -189,15 +206,24 @@ def eval( from .operator_state_fn import OperatorStateFn from .circuit_state_fn import CircuitStateFn from .dict_state_fn import DictStateFn + if isinstance(front, DictStateFn): - return np.round(sum([v * self.primitive.data[int(b, 2)] * front.coeff - for (b, v) in front.primitive.items()]) * self.coeff, - decimals=EVAL_SIG_DIGITS) + return np.round( + sum( + [ + v * self.primitive.data[int(b, 2)] * front.coeff + for (b, v) in front.primitive.items() + ] + ) + * self.coeff, + decimals=EVAL_SIG_DIGITS, + ) if isinstance(front, VectorStateFn): # Need to extract the element or np.array([1]) is returned. - return np.round(np.dot(self.to_matrix(), front.to_matrix())[0], - decimals=EVAL_SIG_DIGITS) + return np.round( + np.dot(self.to_matrix(), front.to_matrix())[0], decimals=EVAL_SIG_DIGITS + ) if isinstance(front, CircuitStateFn): # Don't reimplement logic from CircuitStateFn @@ -208,18 +234,18 @@ def eval( return front.adjoint().eval(self.adjoint().primitive).adjoint() * self.coeff # type: ignore - def sample(self, - shots: int = 1024, - massive: bool = False, - reverse_endianness: bool = False) -> dict: + def sample( + self, shots: int = 1024, massive: bool = False, reverse_endianness: bool = False + ) -> dict: deterministic_counts = self.primitive.probabilities_dict() # Don't need to square because probabilities_dict already does. probs = np.array(list(deterministic_counts.values())) unique, counts = np.unique( - algorithm_globals.random.choice(list(deterministic_counts.keys()), - size=shots, - p=(probs / sum(probs))), - return_counts=True) + algorithm_globals.random.choice( + list(deterministic_counts.keys()), size=shots, p=(probs / sum(probs)) + ), + return_counts=True, + ) counts = dict(zip(unique, counts)) if reverse_endianness: scaled_dict = {bstr[::-1]: (prob / shots) for (bstr, prob) in counts.items()} diff --git a/qiskit/opflow/utils.py b/qiskit/opflow/utils.py index 06348759914e..439b44ea4661 100644 --- a/qiskit/opflow/utils.py +++ b/qiskit/opflow/utils.py @@ -50,10 +50,10 @@ def anti_commutator(op_a: OperatorBase, op_b: OperatorBase) -> OperatorBase: def double_commutator( - op_a: OperatorBase, - op_b: OperatorBase, - op_c: OperatorBase, - sign: bool = False, + op_a: OperatorBase, + op_b: OperatorBase, + op_c: OperatorBase, + sign: bool = False, ) -> OperatorBase: r""" Compute symmetric double commutator of `op_a`, `op_b` and `op_c`. @@ -97,12 +97,7 @@ def double_commutator( res = ( op_abc - sign_num * op_cba - + 0.5 * ( - - op_bac - + sign_num * op_cab - - op_acb - + sign_num * op_bca - ) + + 0.5 * (-op_bac + sign_num * op_cab - op_acb + sign_num * op_bca) ) return res.reduce() diff --git a/qiskit/providers/__init__.py b/qiskit/providers/__init__.py index 2cc8372c0f3a..e8f2c8867efa 100644 --- a/qiskit/providers/__init__.py +++ b/qiskit/providers/__init__.py @@ -541,12 +541,18 @@ def status(self): from qiskit.providers.options import Options from qiskit.providers.job import Job from qiskit.providers.job import JobV1 + # Legacy providers interface from qiskit.providers.basebackend import BaseBackend from qiskit.providers.baseprovider import BaseProvider from qiskit.providers.basejob import BaseJob -from qiskit.providers.exceptions import (JobError, JobTimeoutError, QiskitBackendNotFoundError, - BackendPropertyError, BackendConfigurationError) +from qiskit.providers.exceptions import ( + JobError, + JobTimeoutError, + QiskitBackendNotFoundError, + BackendPropertyError, + BackendConfigurationError, +) from qiskit.providers.jobstatus import JobStatus diff --git a/qiskit/providers/backend.py b/qiskit/providers/backend.py index 58522835e5f2..9b80fe27bd38 100644 --- a/qiskit/providers/backend.py +++ b/qiskit/providers/backend.py @@ -27,6 +27,7 @@ class Backend: the versioned abstract classes as the parent class and not this class directly. """ + version = 0 @@ -56,6 +57,7 @@ class BackendV1(Backend, ABC): Expect, future versions of this abstract class to change the data model and interface. """ + version = 1 def __init__(self, configuration, provider=None, **fields): @@ -77,8 +79,7 @@ def __init__(self, configuration, provider=None, **fields): if fields: for field in fields: if field not in self._options.data: - raise AttributeError( - "Options field %s is not valid for this backend" % field) + raise AttributeError("Options field %s is not valid for this backend" % field) self._options.update_config(**fields) @classmethod @@ -113,9 +114,7 @@ def set_options(self, **fields): """ for field in fields: if not hasattr(self._options, field): - raise AttributeError( - "Options field %s is not valid for this " - "backend" % field) + raise AttributeError("Options field %s is not valid for this " "backend" % field) self._options.update_options(**fields) def configuration(self): @@ -149,11 +148,13 @@ def status(self): Returns: BackendStatus: the status of the backend. """ - return BackendStatus(backend_name=self.name(), - backend_version='1', - operational=True, - pending_jobs=0, - status_msg='') + return BackendStatus( + backend_name=self.name(), + backend_version="1", + operational=True, + pending_jobs=0, + status_msg="", + ) def name(self): """Return the backend name. diff --git a/qiskit/providers/basebackend.py b/qiskit/providers/basebackend.py index 6b98782edad2..f39be4517847 100644 --- a/qiskit/providers/basebackend.py +++ b/qiskit/providers/basebackend.py @@ -83,11 +83,13 @@ def status(self): Returns: BackendStatus: the status of the backend. """ - return BackendStatus(backend_name=self.name(), - backend_version=__version__, - operational=True, - pending_jobs=0, - status_msg='') + return BackendStatus( + backend_name=self.name(), + backend_version=__version__, + operational=True, + pending_jobs=0, + status_msg="", + ) def name(self): """Return the backend name. @@ -117,6 +119,4 @@ def __repr__(self): [0] https://docs.python.org/3/reference/datamodel.html#object.__repr__ """ - return "<{}('{}') from {}()>".format(self.__class__.__name__, - self.name(), - self._provider) + return "<{}('{}') from {}()>".format(self.__class__.__name__, self.name(), self._provider) diff --git a/qiskit/providers/basejob.py b/qiskit/providers/basejob.py index 64f7a3729d76..fa99a6fe1090 100644 --- a/qiskit/providers/basejob.py +++ b/qiskit/providers/basejob.py @@ -64,10 +64,7 @@ def in_final_state(self) -> bool: return self.status() in JOB_FINAL_STATES def wait_for_final_state( - self, - timeout: Optional[float] = None, - wait: float = 5, - callback: Optional[Callable] = None + self, timeout: Optional[float] = None, wait: float = 5, callback: Optional[Callable] = None ) -> None: """Poll the job status until it progresses to a final state such as ``DONE`` or ``ERROR``. @@ -93,8 +90,7 @@ def wait_for_final_state( while status not in JOB_FINAL_STATES: elapsed_time = time.time() - start_time if timeout is not None and elapsed_time >= timeout: - raise JobTimeoutError( - 'Timeout while waiting for job {}.'.format(self.job_id())) + raise JobTimeoutError("Timeout while waiting for job {}.".format(self.job_id())) if callback: callback(self.job_id(), status, self) time.sleep(wait) diff --git a/qiskit/providers/baseprovider.py b/qiskit/providers/baseprovider.py index 312ca1cb6c08..cbfa0f070a9d 100644 --- a/qiskit/providers/baseprovider.py +++ b/qiskit/providers/baseprovider.py @@ -39,9 +39,9 @@ def get_backend(self, name=None, **kwargs): """ backends = self.backends(name, **kwargs) if len(backends) > 1: - raise QiskitBackendNotFoundError('More than one backend matches the criteria') + raise QiskitBackendNotFoundError("More than one backend matches the criteria") if not backends: - raise QiskitBackendNotFoundError('No backend matches the criteria') + raise QiskitBackendNotFoundError("No backend matches the criteria") return backends[0] diff --git a/qiskit/providers/basicaer/basicaerjob.py b/qiskit/providers/basicaer/basicaerjob.py index 88811515b861..bc51847a0255 100644 --- a/qiskit/providers/basicaer/basicaerjob.py +++ b/qiskit/providers/basicaer/basicaerjob.py @@ -46,10 +46,12 @@ def result(self, timeout=None): qiskit.Result: Result object """ if timeout is not None: - warnings.warn("The timeout kwarg doesn't have any meaning with " - "BasicAer because execution is synchronous and the " - "result already exists when run() returns.", - UserWarning) + warnings.warn( + "The timeout kwarg doesn't have any meaning with " + "BasicAer because execution is synchronous and the " + "result already exists when run() returns.", + UserWarning, + ) return self._result diff --git a/qiskit/providers/basicaer/basicaerprovider.py b/qiskit/providers/basicaer/basicaerprovider.py index b4d4de84103f..604e6cf66174 100644 --- a/qiskit/providers/basicaer/basicaerprovider.py +++ b/qiskit/providers/basicaer/basicaerprovider.py @@ -28,11 +28,7 @@ logger = logging.getLogger(__name__) -SIMULATORS = [ - QasmSimulatorPy, - StatevectorSimulatorPy, - UnitarySimulatorPy -] +SIMULATORS = [QasmSimulatorPy, StatevectorSimulatorPy, UnitarySimulatorPy] class BasicAerProvider(ProviderV1): @@ -52,9 +48,7 @@ def get_backend(self, name=None, **kwargs): if name: try: resolved_name = resolve_backend_name( - name, backends, - self._deprecated_backend_names(), - {} + name, backends, self._deprecated_backend_names(), {} ) name = resolved_name except LookupError as ex: @@ -73,12 +67,9 @@ def backends(self, name=None, filters=None, **kwargs): if name: try: resolved_name = resolve_backend_name( - name, backends, - self._deprecated_backend_names(), - {} + name, backends, self._deprecated_backend_names(), {} ) - backends = [backend for backend in backends if - backend.name() == resolved_name] + backends = [backend for backend in backends if backend.name() == resolved_name] except LookupError: return [] @@ -88,14 +79,14 @@ def backends(self, name=None, filters=None, **kwargs): def _deprecated_backend_names(): """Returns deprecated backend names.""" return { - 'qasm_simulator_py': 'qasm_simulator', - 'statevector_simulator_py': 'statevector_simulator', - 'unitary_simulator_py': 'unitary_simulator', - 'local_qasm_simulator_py': 'qasm_simulator', - 'local_statevector_simulator_py': 'statevector_simulator', - 'local_unitary_simulator_py': 'unitary_simulator', - 'local_unitary_simulator': 'unitary_simulator', - } + "qasm_simulator_py": "qasm_simulator", + "statevector_simulator_py": "statevector_simulator", + "unitary_simulator_py": "unitary_simulator", + "local_qasm_simulator_py": "qasm_simulator", + "local_statevector_simulator_py": "statevector_simulator", + "local_unitary_simulator_py": "unitary_simulator", + "local_unitary_simulator": "unitary_simulator", + } def _verify_backends(self): """ @@ -129,9 +120,9 @@ def _get_backend_instance(self, backend_cls): try: backend_instance = backend_cls(provider=self) except Exception as err: - raise QiskitError(f'Backend {backend_cls} could not be instantiated: {err}') from err + raise QiskitError(f"Backend {backend_cls} could not be instantiated: {err}") from err return backend_instance def __str__(self): - return 'BasicAer' + return "BasicAer" diff --git a/qiskit/providers/basicaer/basicaertools.py b/qiskit/providers/basicaer/basicaertools.py index 7e8f38df0088..dd5f0176aab9 100644 --- a/qiskit/providers/basicaer/basicaertools.py +++ b/qiskit/providers/basicaer/basicaertools.py @@ -23,7 +23,7 @@ from qiskit.exceptions import QiskitError # Single qubit gates supported by ``single_gate_params``. -SINGLE_QUBIT_GATES = ('U', 'u1', 'u2', 'u3', 'rz', 'sx', 'x') +SINGLE_QUBIT_GATES = ("U", "u1", "u2", "u3", "rz", "sx", "x") def single_gate_matrix(gate: str, params: Optional[List[float]] = None): @@ -41,24 +41,24 @@ def single_gate_matrix(gate: str, params: Optional[List[float]] = None): if params is None: params = [] - if gate == 'U': + if gate == "U": gc = gates.UGate - elif gate == 'u3': + elif gate == "u3": gc = gates.U3Gate - elif gate == 'u2': + elif gate == "u2": gc = gates.U2Gate - elif gate == 'u1': + elif gate == "u1": gc = gates.U1Gate - elif gate == 'rz': + elif gate == "rz": gc = gates.RZGate - elif gate == 'id': + elif gate == "id": gc = gates.IGate - elif gate == 'sx': + elif gate == "sx": gc = gates.SXGate - elif gate == 'x': + elif gate == "x": gc = gates.XGate else: - raise QiskitError('Gate is not a valid basis gate for this simulator: %s' % gate) + raise QiskitError("Gate is not a valid basis gate for this simulator: %s" % gate) return gc(*params).to_matrix() @@ -69,10 +69,7 @@ def single_gate_matrix(gate: str, params: Optional[List[float]] = None): def cx_gate_matrix(): """Get the matrix for a controlled-NOT gate.""" - return np.array([[1, 0, 0, 0], - [0, 0, 0, 1], - [0, 0, 1, 0], - [0, 1, 0, 0]], dtype=complex) + return np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex) def einsum_matmul_index(gate_indices, number_of_qubits): @@ -92,18 +89,18 @@ def einsum_matmul_index(gate_indices, number_of_qubits): str: An indices string for the Numpy.einsum function. """ - mat_l, mat_r, tens_lin, tens_lout = _einsum_matmul_index_helper(gate_indices, - number_of_qubits) + mat_l, mat_r, tens_lin, tens_lout = _einsum_matmul_index_helper(gate_indices, number_of_qubits) # Right indices for the N-qubit input and output tensor tens_r = ascii_uppercase[:number_of_qubits] # Combine indices into matrix multiplication string format # for numpy.einsum function - return "{mat_l}{mat_r}, ".format(mat_l=mat_l, mat_r=mat_r) + \ - "{tens_lin}{tens_r}->{tens_lout}{tens_r}".format(tens_lin=tens_lin, - tens_lout=tens_lout, - tens_r=tens_r) + return "{mat_l}{mat_r}, ".format( + mat_l=mat_l, mat_r=mat_r + ) + "{tens_lin}{tens_r}->{tens_lout}{tens_r}".format( + tens_lin=tens_lin, tens_lout=tens_lout, tens_r=tens_r + ) def einsum_vecmul_index(gate_indices, number_of_qubits): @@ -123,14 +120,13 @@ def einsum_vecmul_index(gate_indices, number_of_qubits): str: An indices string for the Numpy.einsum function. """ - mat_l, mat_r, tens_lin, tens_lout = _einsum_matmul_index_helper(gate_indices, - number_of_qubits) + mat_l, mat_r, tens_lin, tens_lout = _einsum_matmul_index_helper(gate_indices, number_of_qubits) # Combine indices into matrix multiplication string format # for numpy.einsum function - return "{mat_l}{mat_r}, ".format(mat_l=mat_l, mat_r=mat_r) + \ - "{tens_lin}->{tens_lout}".format(tens_lin=tens_lin, - tens_lout=tens_lout) + return "{mat_l}{mat_r}, ".format(mat_l=mat_l, mat_r=mat_r) + "{tens_lin}->{tens_lout}".format( + tens_lin=tens_lin, tens_lout=tens_lout + ) def _einsum_matmul_index_helper(gate_indices, number_of_qubits): diff --git a/qiskit/providers/basicaer/exceptions.py b/qiskit/providers/basicaer/exceptions.py index 99db500263f7..a973e4f73fe0 100644 --- a/qiskit/providers/basicaer/exceptions.py +++ b/qiskit/providers/basicaer/exceptions.py @@ -23,7 +23,7 @@ class BasicAerError(QiskitError): def __init__(self, *message): """Set the error message.""" super().__init__(*message) - self.message = ' '.join(message) + self.message = " ".join(message) def __str__(self): """Return the message.""" diff --git a/qiskit/providers/basicaer/qasm_simulator.py b/qiskit/providers/basicaer/qasm_simulator.py index f9e58c39f8fa..b3d163006556 100644 --- a/qiskit/providers/basicaer/qasm_simulator.py +++ b/qiskit/providers/basicaer/qasm_simulator.py @@ -56,75 +56,52 @@ class QasmSimulatorPy(BackendV1): """Python implementation of a qasm simulator.""" - MAX_QUBITS_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) + MAX_QUBITS_MEMORY = int(log2(local_hardware_info()["memory"] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { - 'backend_name': 'qasm_simulator', - 'backend_version': '2.1.0', - 'n_qubits': min(24, MAX_QUBITS_MEMORY), - 'url': 'https://github.com/Qiskit/qiskit-terra', - 'simulator': True, - 'local': True, - 'conditional': True, - 'open_pulse': False, - 'memory': True, - 'max_shots': 65536, - 'coupling_map': None, - 'description': 'A python simulator for qasm experiments', - 'basis_gates': ['u1', 'u2', 'u3', 'rz', 'sx', 'x', 'cx', 'id', 'unitary'], - 'gates': [ + "backend_name": "qasm_simulator", + "backend_version": "2.1.0", + "n_qubits": min(24, MAX_QUBITS_MEMORY), + "url": "https://github.com/Qiskit/qiskit-terra", + "simulator": True, + "local": True, + "conditional": True, + "open_pulse": False, + "memory": True, + "max_shots": 65536, + "coupling_map": None, + "description": "A python simulator for qasm experiments", + "basis_gates": ["u1", "u2", "u3", "rz", "sx", "x", "cx", "id", "unitary"], + "gates": [ { - 'name': 'u1', - 'parameters': ['lambda'], - 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' + "name": "u1", + "parameters": ["lambda"], + "qasm_def": "gate u1(lambda) q { U(0,0,lambda) q; }", }, { - 'name': 'u2', - 'parameters': ['phi', 'lambda'], - 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' + "name": "u2", + "parameters": ["phi", "lambda"], + "qasm_def": "gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }", }, { - 'name': 'u3', - 'parameters': ['theta', 'phi', 'lambda'], - 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' + "name": "u3", + "parameters": ["theta", "phi", "lambda"], + "qasm_def": "gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }", }, + {"name": "rz", "parameters": ["phi"], "qasm_def": "gate rz(phi) q { U(0,0,phi) q; }"}, { - 'name': 'rz', - 'parameters': ['phi'], - 'qasm_def': 'gate rz(phi) q { U(0,0,phi) q; }' + "name": "sx", + "parameters": [], + "qasm_def": "gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }", }, - { - 'name': 'sx', - 'parameters': [], - 'qasm_def': 'gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }' - }, - { - 'name': 'x', - 'parameters': [], - 'qasm_def': 'gate x q { U(pi,7*pi/2,pi/2) q; }' - }, - { - 'name': 'cx', - 'parameters': [], - 'qasm_def': 'gate cx c,t { CX c,t; }' - }, - { - 'name': 'id', - 'parameters': [], - 'qasm_def': 'gate id a { U(0,0,0) a; }' - }, - { - 'name': 'unitary', - 'parameters': ['matrix'], - 'qasm_def': 'unitary(matrix) q1, q2,...' - } - ] + {"name": "x", "parameters": [], "qasm_def": "gate x q { U(pi,7*pi/2,pi/2) q; }"}, + {"name": "cx", "parameters": [], "qasm_def": "gate cx c,t { CX c,t; }"}, + {"name": "id", "parameters": [], "qasm_def": "gate id a { U(0,0,0) a; }"}, + {"name": "unitary", "parameters": ["matrix"], "qasm_def": "unitary(matrix) q1, q2,..."}, + ], } - DEFAULT_OPTIONS = { - "initial_statevector": None, - "chop_threshold": 1e-15 - } + DEFAULT_OPTIONS = {"initial_statevector": None, "chop_threshold": 1e-15} # Class level variable to return the final state at the end of simulation # This should be set to True for the statevector simulator @@ -132,10 +109,12 @@ class QasmSimulatorPy(BackendV1): def __init__(self, configuration=None, provider=None, **fields): super().__init__( - configuration=(configuration or QasmBackendConfiguration.from_dict( - self.DEFAULT_CONFIGURATION)), + configuration=( + configuration or QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION) + ), provider=provider, - **fields) + **fields, + ) # Define attributes in __init__. self._local_random = np.random.RandomState() self._classical_memory = 0 @@ -145,7 +124,7 @@ def __init__(self, configuration=None, provider=None, **fields): self._number_of_qubits = 0 self._shots = 0 self._memory = False - self._initial_statevector = self.options.get('initial_statevector') + self._initial_statevector = self.options.get("initial_statevector") self._chop_threshold = self.options.get("chop_threashold") self._qobj_config = None # TEMP @@ -153,10 +132,15 @@ def __init__(self, configuration=None, provider=None, **fields): @classmethod def _default_options(cls): - return Options(shots=1024, memory=False, - initial_statevector=None, chop_threshold=1e-15, - allow_sample_measuring=True, seed_simulator=None, - parameter_binds=None) + return Options( + shots=1024, + memory=False, + initial_statevector=None, + chop_threshold=1e-15, + allow_sample_measuring=True, + seed_simulator=None, + parameter_binds=None, + ) def _add_unitary(self, gate, qubits): """Apply an N-qubit unitary matrix. @@ -170,11 +154,11 @@ def _add_unitary(self, gate, qubits): # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_vecmul_index(qubits, self._number_of_qubits) # Convert to complex rank-2N tensor - gate_tensor = np.reshape(np.array(gate, dtype=complex), - num_qubits * [2, 2]) + gate_tensor = np.reshape(np.array(gate, dtype=complex), num_qubits * [2, 2]) # Apply matrix multiplication - self._statevector = np.einsum(indexes, gate_tensor, self._statevector, - dtype=complex, casting='no') + self._statevector = np.einsum( + indexes, gate_tensor, self._statevector, dtype=complex, casting="no" + ) def _get_measure_outcome(self, qubit): """Simulate the outcome of measurement of a qubit. @@ -193,9 +177,9 @@ def _get_measure_outcome(self, qubit): # Compute einsum index string for 1-qubit matrix multiplication random_number = self._local_random.rand() if random_number < probabilities[0]: - return '0', probabilities[0] + return "0", probabilities[0] # Else outcome was '1' - return '1', probabilities[1] + return "1", probabilities[1] def _add_sample_measure(self, measure_params, num_samples): """Generate memory samples from current statevector. @@ -220,14 +204,13 @@ def _add_sample_measure(self, measure_params, num_samples): # Remove from largest qubit to smallest so list position is correct # with respect to position from end of the list axis.remove(self._number_of_qubits - 1 - qubit) - probabilities = np.reshape(np.sum(np.abs(self._statevector) ** 2, - axis=tuple(axis)), - 2 ** num_measured) + probabilities = np.reshape( + np.sum(np.abs(self._statevector) ** 2, axis=tuple(axis)), 2 ** num_measured + ) # Generate samples on measured qubits as ints with qubit # position in the bit-string for each int given by the qubit # position in the sorted measured_qubits list - samples = self._local_random.choice(range(2 ** num_measured), - num_samples, p=probabilities) + samples = self._local_random.choice(range(2 ** num_measured), num_samples, p=probabilities) # Convert the ints to bitstrings memory = [] for sample in samples: @@ -257,11 +240,12 @@ def _add_qasm_measure(self, qubit, cmembit, cregbit=None): if cregbit is not None: regbit = 1 << cregbit - self._classical_register = \ - (self._classical_register & (~regbit)) | (int(outcome) << cregbit) + self._classical_register = (self._classical_register & (~regbit)) | ( + int(outcome) << cregbit + ) # update quantum state - if outcome == '0': + if outcome == "0": update_diag = [[1 / np.sqrt(probability), 0], [0, 0]] else: update_diag = [[0, 0], [0, 1 / np.sqrt(probability)]] @@ -281,7 +265,7 @@ def _add_qasm_reset(self, qubit): # get measure outcome outcome, probability = self._get_measure_outcome(qubit) # update quantum state - if outcome == '0': + if outcome == "0": update = [[1 / np.sqrt(probability), 0], [0, 0]] self._add_unitary(update, [qubit]) else: @@ -297,50 +281,51 @@ def _validate_initial_statevector(self): length = len(self._initial_statevector) required_dim = 2 ** self._number_of_qubits if length != required_dim: - raise BasicAerError('initial statevector is incorrect length: ' + - '{} != {}'.format(length, required_dim)) + raise BasicAerError( + "initial statevector is incorrect length: " + + "{} != {}".format(length, required_dim) + ) def _set_options(self, qobj_config=None, backend_options=None): """Set the backend options for all experiments in a qobj""" # Reset default options self._initial_statevector = self.options.get("initial_statevector") self._chop_threshold = self.options.get("chop_threshold") - if 'backend_options' in backend_options and backend_options['backend_options']: - backend_options = backend_options['backend_options'] + if "backend_options" in backend_options and backend_options["backend_options"]: + backend_options = backend_options["backend_options"] # Check for custom initial statevector in backend_options first, # then config second - if 'initial_statevector' in backend_options: - self._initial_statevector = np.array(backend_options['initial_statevector'], - dtype=complex) - elif hasattr(qobj_config, 'initial_statevector'): - self._initial_statevector = np.array(qobj_config.initial_statevector, - dtype=complex) + if "initial_statevector" in backend_options: + self._initial_statevector = np.array( + backend_options["initial_statevector"], dtype=complex + ) + elif hasattr(qobj_config, "initial_statevector"): + self._initial_statevector = np.array(qobj_config.initial_statevector, dtype=complex) if self._initial_statevector is not None: # Check the initial statevector is normalized norm = np.linalg.norm(self._initial_statevector) if round(norm, 12) != 1: - raise BasicAerError('initial statevector is not normalized: ' + - 'norm {} != 1'.format(norm)) + raise BasicAerError( + "initial statevector is not normalized: " + "norm {} != 1".format(norm) + ) # Check for custom chop threshold # Replace with custom options - if 'chop_threshold' in backend_options: - self._chop_threshold = backend_options['chop_threshold'] - elif hasattr(qobj_config, 'chop_threshold'): + if "chop_threshold" in backend_options: + self._chop_threshold = backend_options["chop_threshold"] + elif hasattr(qobj_config, "chop_threshold"): self._chop_threshold = qobj_config.chop_threshold def _initialize_statevector(self): """Set the initial statevector for simulation""" if self._initial_statevector is None: # Set to default state of all qubits in |0> - self._statevector = np.zeros(2 ** self._number_of_qubits, - dtype=complex) + self._statevector = np.zeros(2 ** self._number_of_qubits, dtype=complex) self._statevector[0] = 1 else: self._statevector = self._initial_statevector.copy() # Reshape to rank-N tensor - self._statevector = np.reshape(self._statevector, - self._number_of_qubits * [2]) + self._statevector = np.reshape(self._statevector, self._number_of_qubits * [2]) def _get_statevector(self): """Return the current statevector""" @@ -362,7 +347,7 @@ def _validate_measure_sampling(self, experiment): return # Check for config flag - if hasattr(experiment.config, 'allows_measure_sampling'): + if hasattr(experiment.config, "allows_measure_sampling"): self._sample_measure = experiment.config.allows_measure_sampling # If flag isn't found do a simple test to see if a circuit contains # no reset instructions, and no gates instructions after @@ -415,24 +400,25 @@ def run(self, qobj, **backend_options): """ if isinstance(qobj, (QuantumCircuit, list)): from qiskit.compiler import assemble + out_options = {} for key in backend_options: if not hasattr(self.options, key): warnings.warn( - "Option %s is not used by this backend" % key, - UserWarning, stacklevel=2) + "Option %s is not used by this backend" % key, UserWarning, stacklevel=2 + ) else: out_options[key] = backend_options[key] qobj = assemble(qobj, self, **out_options) qobj_options = qobj.config else: - warnings.warn('Using a qobj for run() is deprecated and will be ' - 'removed in a future release.', - PendingDeprecationWarning, - stacklevel=2) + warnings.warn( + "Using a qobj for run() is deprecated and will be " "removed in a future release.", + PendingDeprecationWarning, + stacklevel=2, + ) qobj_options = qobj.config - self._set_options(qobj_config=qobj_options, - backend_options=backend_options) + self._set_options(qobj_config=qobj_options, backend_options=backend_options) job_id = str(uuid.uuid4()) job = BasicAerJob(self, job_id, self._run_job(job_id, qobj)) return job @@ -450,21 +436,23 @@ def _run_job(self, job_id, qobj): self._validate(qobj) result_list = [] self._shots = qobj.config.shots - self._memory = getattr(qobj.config, 'memory', False) + self._memory = getattr(qobj.config, "memory", False) self._qobj_config = qobj.config start = time.time() for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() - result = {'backend_name': self.name(), - 'backend_version': self._configuration.backend_version, - 'qobj_id': qobj.qobj_id, - 'job_id': job_id, - 'results': result_list, - 'status': 'COMPLETED', - 'success': True, - 'time_taken': (end - start), - 'header': qobj.header.to_dict()} + result = { + "backend_name": self.name(), + "backend_version": self._configuration.backend_version, + "qobj_id": qobj.qobj_id, + "job_id": job_id, + "results": result_list, + "status": "COMPLETED", + "success": True, + "time_taken": (end - start), + "header": qobj.header.to_dict(), + } return Result.from_dict(result) @@ -504,14 +492,14 @@ def run_experiment(self, experiment): # Validate the dimension of initial statevector if set self._validate_initial_statevector() # Get the seed looking in circuit, qobj, and then random. - if hasattr(experiment.config, 'seed_simulator'): + if hasattr(experiment.config, "seed_simulator"): seed_simulator = experiment.config.seed_simulator - elif hasattr(self._qobj_config, 'seed_simulator'): + elif hasattr(self._qobj_config, "seed_simulator"): seed_simulator = self._qobj_config.seed_simulator else: # For compatibility on Windows force dyte to be int32 # and set the maximum value to be (2 ** 31) - 1 - seed_simulator = np.random.randint(2147483647, dtype='int32') + seed_simulator = np.random.randint(2147483647, dtype="int32") self._local_random.seed(seed=seed_simulator) # Check if measure sampling is supported for current circuit @@ -536,7 +524,7 @@ def run_experiment(self, experiment): self._classical_memory = 0 self._classical_register = 0 for operation in experiment.instructions: - conditional = getattr(operation, 'conditional', None) + conditional = getattr(operation, "conditional", None) if isinstance(conditional, int): conditional_bit_set = (self._classical_register >> conditional) & 1 if not conditional_bit_set: @@ -552,35 +540,35 @@ def run_experiment(self, experiment): continue # Check if single gate - if operation.name == 'unitary': + if operation.name == "unitary": qubits = operation.qubits gate = operation.params[0] self._add_unitary(gate, qubits) elif operation.name in SINGLE_QUBIT_GATES: - params = getattr(operation, 'params', None) + params = getattr(operation, "params", None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary(gate, [qubit]) # Check if CX gate - elif operation.name in ('id', 'u0'): + elif operation.name in ("id", "u0"): pass - elif operation.name in ('CX', 'cx'): + elif operation.name in ("CX", "cx"): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = cx_gate_matrix() self._add_unitary(gate, [qubit0, qubit1]) # Check if reset - elif operation.name == 'reset': + elif operation.name == "reset": qubit = operation.qubits[0] self._add_qasm_reset(qubit) # Check if barrier - elif operation.name == 'barrier': + elif operation.name == "barrier": pass # Check if measure - elif operation.name == 'measure': + elif operation.name == "measure": qubit = operation.qubits[0] cmembit = operation.memory[0] - cregbit = operation.register[0] if hasattr(operation, 'register') else None + cregbit = operation.register[0] if hasattr(operation, "register") else None if self._sample_measure: # If sampling measurements record the qubit and cmembit @@ -589,39 +577,41 @@ def run_experiment(self, experiment): else: # If not sampling perform measurement as normal self._add_qasm_measure(qubit, cmembit, cregbit) - elif operation.name == 'bfunc': + elif operation.name == "bfunc": mask = int(operation.mask, 16) relation = operation.relation val = int(operation.val, 16) cregbit = operation.register - cmembit = operation.memory if hasattr(operation, 'memory') else None + cmembit = operation.memory if hasattr(operation, "memory") else None compared = (self._classical_register & mask) - val - if relation == '==': - outcome = (compared == 0) - elif relation == '!=': - outcome = (compared != 0) - elif relation == '<': - outcome = (compared < 0) - elif relation == '<=': - outcome = (compared <= 0) - elif relation == '>': - outcome = (compared > 0) - elif relation == '>=': - outcome = (compared >= 0) + if relation == "==": + outcome = compared == 0 + elif relation == "!=": + outcome = compared != 0 + elif relation == "<": + outcome = compared < 0 + elif relation == "<=": + outcome = compared <= 0 + elif relation == ">": + outcome = compared > 0 + elif relation == ">=": + outcome = compared >= 0 else: - raise BasicAerError('Invalid boolean function relation.') + raise BasicAerError("Invalid boolean function relation.") # Store outcome in register and optionally memory slot regbit = 1 << cregbit - self._classical_register = \ - (self._classical_register & (~regbit)) | (int(outcome) << cregbit) + self._classical_register = (self._classical_register & (~regbit)) | ( + int(outcome) << cregbit + ) if cmembit is not None: membit = 1 << cmembit - self._classical_memory = \ - (self._classical_memory & (~membit)) | (int(outcome) << cmembit) + self._classical_memory = (self._classical_memory & (~membit)) | ( + int(outcome) << cmembit + ) else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' @@ -638,41 +628,48 @@ def run_experiment(self, experiment): memory.append(hex(int(outcome, 2))) # Add data - data = {'counts': dict(Counter(memory))} + data = {"counts": dict(Counter(memory))} # Optionally add memory list if self._memory: - data['memory'] = memory + data["memory"] = memory # Optionally add final statevector if self.SHOW_FINAL_STATE: - data['statevector'] = self._get_statevector() + data["statevector"] = self._get_statevector() # Remove empty counts and memory for statevector simulator - if not data['counts']: - data.pop('counts') - if 'memory' in data and not data['memory']: - data.pop('memory') + if not data["counts"]: + data.pop("counts") + if "memory" in data and not data["memory"]: + data.pop("memory") end = time.time() - return {'name': experiment.header.name, - 'seed_simulator': seed_simulator, - 'shots': self._shots, - 'data': data, - 'status': 'DONE', - 'success': True, - 'time_taken': (end - start), - 'header': experiment.header.to_dict()} + return { + "name": experiment.header.name, + "seed_simulator": seed_simulator, + "shots": self._shots, + "data": data, + "status": "DONE", + "success": True, + "time_taken": (end - start), + "header": experiment.header.to_dict(), + } def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas.""" n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: - raise BasicAerError('Number of qubits {} '.format(n_qubits) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'for "{}".'.format(self.name())) + raise BasicAerError( + "Number of qubits {} ".format(n_qubits) + + "is greater than maximum ({}) ".format(max_qubits) + + 'for "{}".'.format(self.name()) + ) for experiment in qobj.experiments: name = experiment.header.name if experiment.config.memory_slots == 0: - logger.warning('No classical registers in circuit "%s", ' - 'counts will be empty.', name) - elif 'measure' not in [op.name for op in experiment.instructions]: - logger.warning('No measurements in circuit "%s", ' - 'classical register will remain all zeros.', name) + logger.warning( + 'No classical registers in circuit "%s", ' "counts will be empty.", name + ) + elif "measure" not in [op.name for op in experiment.instructions]: + logger.warning( + 'No measurements in circuit "%s", ' "classical register will remain all zeros.", + name, + ) diff --git a/qiskit/providers/basicaer/statevector_simulator.py b/qiskit/providers/basicaer/statevector_simulator.py index 3918376e6617..27c92709fb3d 100644 --- a/qiskit/providers/basicaer/statevector_simulator.py +++ b/qiskit/providers/basicaer/statevector_simulator.py @@ -36,78 +36,62 @@ class StatevectorSimulatorPy(QasmSimulatorPy): """Python statevector simulator.""" - MAX_QUBITS_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) + MAX_QUBITS_MEMORY = int(log2(local_hardware_info()["memory"] * (1024 ** 3) / 16)) DEFAULT_CONFIGURATION = { - 'backend_name': 'statevector_simulator', - 'backend_version': '1.1.0', - 'n_qubits': min(24, MAX_QUBITS_MEMORY), - 'url': 'https://github.com/Qiskit/qiskit-terra', - 'simulator': True, - 'local': True, - 'conditional': True, - 'open_pulse': False, - 'memory': True, - 'max_shots': 65536, - 'coupling_map': None, - 'description': 'A Python statevector simulator for qobj files', - 'basis_gates': ['u1', 'u2', 'u3', 'rz', 'sx', 'x', 'cx', 'id', 'unitary'], - 'gates': [ + "backend_name": "statevector_simulator", + "backend_version": "1.1.0", + "n_qubits": min(24, MAX_QUBITS_MEMORY), + "url": "https://github.com/Qiskit/qiskit-terra", + "simulator": True, + "local": True, + "conditional": True, + "open_pulse": False, + "memory": True, + "max_shots": 65536, + "coupling_map": None, + "description": "A Python statevector simulator for qobj files", + "basis_gates": ["u1", "u2", "u3", "rz", "sx", "x", "cx", "id", "unitary"], + "gates": [ { - 'name': 'u1', - 'parameters': ['lambda'], - 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' + "name": "u1", + "parameters": ["lambda"], + "qasm_def": "gate u1(lambda) q { U(0,0,lambda) q; }", }, { - 'name': 'u2', - 'parameters': ['phi', 'lambda'], - 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' + "name": "u2", + "parameters": ["phi", "lambda"], + "qasm_def": "gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }", }, { - 'name': 'u3', - 'parameters': ['theta', 'phi', 'lambda'], - 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' + "name": "u3", + "parameters": ["theta", "phi", "lambda"], + "qasm_def": "gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }", }, + {"name": "rz", "parameters": ["phi"], "qasm_def": "gate rz(phi) q { U(0,0,phi) q; }"}, { - 'name': 'rz', - 'parameters': ['phi'], - 'qasm_def': 'gate rz(phi) q { U(0,0,phi) q; }' + "name": "sx", + "parameters": [], + "qasm_def": "gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }", }, - { - 'name': 'sx', - 'parameters': [], - 'qasm_def': 'gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }' - }, - { - 'name': 'x', - 'parameters': [], - 'qasm_def': 'gate x q { U(pi,7*pi/2,pi/2) q; }' - }, - { - 'name': 'cx', - 'parameters': [], - 'qasm_def': 'gate cx c,t { CX c,t; }' - }, - { - 'name': 'id', - 'parameters': [], - 'qasm_def': 'gate id a { U(0,0,0) a; }' - }, - { - 'name': 'unitary', - 'parameters': ['matrix'], - 'qasm_def': 'unitary(matrix) q1, q2,...' - } - ] + {"name": "x", "parameters": [], "qasm_def": "gate x q { U(pi,7*pi/2,pi/2) q; }"}, + {"name": "cx", "parameters": [], "qasm_def": "gate cx c,t { CX c,t; }"}, + {"name": "id", "parameters": [], "qasm_def": "gate id a { U(0,0,0) a; }"}, + {"name": "unitary", "parameters": ["matrix"], "qasm_def": "unitary(matrix) q1, q2,..."}, + ], } # Override base class value to return the final state vector SHOW_FINAL_STATE = True def __init__(self, configuration=None, provider=None, **fields): - super().__init__(configuration=( - configuration or QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION)), - provider=provider, **fields) + super().__init__( + configuration=( + configuration or QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION) + ), + provider=provider, + **fields, + ) def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. @@ -119,17 +103,20 @@ def _validate(self, qobj): num_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if num_qubits > max_qubits: - raise BasicAerError('Number of qubits {} '.format(num_qubits) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'for "{}".'.format(self.name())) + raise BasicAerError( + "Number of qubits {} ".format(num_qubits) + + "is greater than maximum ({}) ".format(max_qubits) + + 'for "{}".'.format(self.name()) + ) if qobj.config.shots != 1: - logger.info('"%s" only supports 1 shot. Setting shots=1.', - self.name()) + logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name - if getattr(experiment.config, 'shots', 1) != 1: - logger.info('"%s" only supports 1 shot. ' - 'Setting shots=1 for circuit "%s".', - self.name(), name) + if getattr(experiment.config, "shots", 1) != 1: + logger.info( + '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', + self.name(), + name, + ) experiment.config.shots = 1 diff --git a/qiskit/providers/basicaer/unitary_simulator.py b/qiskit/providers/basicaer/unitary_simulator.py index 04ab0c20a1a0..85affe1d23cd 100644 --- a/qiskit/providers/basicaer/unitary_simulator.py +++ b/qiskit/providers/basicaer/unitary_simulator.py @@ -57,95 +57,72 @@ class UnitarySimulatorPy(BackendV1): """Python implementation of a unitary simulator.""" - MAX_QUBITS_MEMORY = int(log2(sqrt(local_hardware_info()['memory'] * (1024 ** 3) / 16))) + MAX_QUBITS_MEMORY = int(log2(sqrt(local_hardware_info()["memory"] * (1024 ** 3) / 16))) DEFAULT_CONFIGURATION = { - 'backend_name': 'unitary_simulator', - 'backend_version': '1.1.0', - 'n_qubits': min(24, MAX_QUBITS_MEMORY), - 'url': 'https://github.com/Qiskit/qiskit-terra', - 'simulator': True, - 'local': True, - 'conditional': False, - 'open_pulse': False, - 'memory': False, - 'max_shots': 65536, - 'coupling_map': None, - 'description': 'A python simulator for unitary matrix corresponding to a circuit', - 'basis_gates': ['u1', 'u2', 'u3', 'rz', 'sx', 'x', 'cx', 'id', 'unitary'], - 'gates': [ + "backend_name": "unitary_simulator", + "backend_version": "1.1.0", + "n_qubits": min(24, MAX_QUBITS_MEMORY), + "url": "https://github.com/Qiskit/qiskit-terra", + "simulator": True, + "local": True, + "conditional": False, + "open_pulse": False, + "memory": False, + "max_shots": 65536, + "coupling_map": None, + "description": "A python simulator for unitary matrix corresponding to a circuit", + "basis_gates": ["u1", "u2", "u3", "rz", "sx", "x", "cx", "id", "unitary"], + "gates": [ { - 'name': 'u1', - 'parameters': ['lambda'], - 'qasm_def': 'gate u1(lambda) q { U(0,0,lambda) q; }' + "name": "u1", + "parameters": ["lambda"], + "qasm_def": "gate u1(lambda) q { U(0,0,lambda) q; }", }, { - 'name': 'u2', - 'parameters': ['phi', 'lambda'], - 'qasm_def': 'gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }' + "name": "u2", + "parameters": ["phi", "lambda"], + "qasm_def": "gate u2(phi,lambda) q { U(pi/2,phi,lambda) q; }", }, { - 'name': 'u3', - 'parameters': ['theta', 'phi', 'lambda'], - 'qasm_def': 'gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }' + "name": "u3", + "parameters": ["theta", "phi", "lambda"], + "qasm_def": "gate u3(theta,phi,lambda) q { U(theta,phi,lambda) q; }", }, + {"name": "rz", "parameters": ["phi"], "qasm_def": "gate rz(phi) q { U(0,0,phi) q; }"}, { - 'name': 'rz', - 'parameters': ['phi'], - 'qasm_def': 'gate rz(phi) q { U(0,0,phi) q; }' + "name": "sx", + "parameters": [], + "qasm_def": "gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }", }, - { - 'name': 'sx', - 'parameters': [], - 'qasm_def': 'gate sx(phi) q { U(pi/2,7*pi/2,pi/2) q; }' - }, - { - 'name': 'x', - 'parameters': [], - 'qasm_def': 'gate x q { U(pi,7*pi/2,pi/2) q; }' - }, - { - 'name': 'cx', - 'parameters': [], - 'qasm_def': 'gate cx c,t { CX c,t; }' - }, - { - 'name': 'id', - 'parameters': [], - 'qasm_def': 'gate id a { U(0,0,0) a; }' - }, - { - 'name': 'unitary', - 'parameters': ['matrix'], - 'qasm_def': 'unitary(matrix) q1, q2,...' - } - ] + {"name": "x", "parameters": [], "qasm_def": "gate x q { U(pi,7*pi/2,pi/2) q; }"}, + {"name": "cx", "parameters": [], "qasm_def": "gate cx c,t { CX c,t; }"}, + {"name": "id", "parameters": [], "qasm_def": "gate id a { U(0,0,0) a; }"}, + {"name": "unitary", "parameters": ["matrix"], "qasm_def": "unitary(matrix) q1, q2,..."}, + ], } - DEFAULT_OPTIONS = { - "initial_unitary": None, - "chop_threshold": 1e-15 - } + DEFAULT_OPTIONS = {"initial_unitary": None, "chop_threshold": 1e-15} def __init__(self, configuration=None, provider=None, **fields): super().__init__( - configuration=(configuration or QasmBackendConfiguration.from_dict( - self.DEFAULT_CONFIGURATION)), + configuration=( + configuration or QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION) + ), provider=provider, - **fields) + **fields, + ) # Define attributes inside __init__. self._unitary = None self._number_of_qubits = 0 self._initial_unitary = None self._global_phase = 0 - self._chop_threshold = self.options.get('chop_threshold') + self._chop_threshold = self.options.get("chop_threshold") @classmethod def _default_options(cls): - return Options(shots=1, - initial_unitary=None, chop_threshold=1e-15, - parameter_binds=None) + return Options(shots=1, initial_unitary=None, chop_threshold=1e-15, parameter_binds=None) def _add_unitary(self, gate, qubits): """Apply an N-qubit unitary matrix. @@ -159,11 +136,9 @@ def _add_unitary(self, gate, qubits): # Compute einsum index string for 1-qubit matrix multiplication indexes = einsum_matmul_index(qubits, self._number_of_qubits) # Convert to complex rank-2N tensor - gate_tensor = np.reshape(np.array(gate, dtype=complex), - num_qubits * [2, 2]) + gate_tensor = np.reshape(np.array(gate, dtype=complex), num_qubits * [2, 2]) # Apply matrix multiplication - self._unitary = np.einsum(indexes, gate_tensor, self._unitary, - dtype=complex, casting='no') + self._unitary = np.einsum(indexes, gate_tensor, self._unitary, dtype=complex, casting="no") def _validate_initial_unitary(self): """Validate an initial unitary matrix""" @@ -172,36 +147,34 @@ def _validate_initial_unitary(self): return # Check unitary is correct length for number of qubits shape = np.shape(self._initial_unitary) - required_shape = (2 ** self._number_of_qubits, - 2 ** self._number_of_qubits) + required_shape = (2 ** self._number_of_qubits, 2 ** self._number_of_qubits) if shape != required_shape: - raise BasicAerError('initial unitary is incorrect shape: ' + - '{} != 2 ** {}'.format(shape, required_shape)) + raise BasicAerError( + "initial unitary is incorrect shape: " + + "{} != 2 ** {}".format(shape, required_shape) + ) def _set_options(self, qobj_config=None, backend_options=None): """Set the backend options for all experiments in a qobj""" # Reset default options self._initial_unitary = self.options.get("initial_unitary") self._chop_threshold = self.options.get("chop_threshold") - if 'backend_options' in backend_options: - backend_options = backend_options['backend_options'] + if "backend_options" in backend_options: + backend_options = backend_options["backend_options"] # Check for custom initial statevector in backend_options first, # then config second - if 'initial_unitary' in backend_options: - self._initial_unitary = np.array(backend_options['initial_unitary'], - dtype=complex) - elif hasattr(qobj_config, 'initial_unitary'): - self._initial_unitary = np.array(qobj_config.initial_unitary, - dtype=complex) + if "initial_unitary" in backend_options: + self._initial_unitary = np.array(backend_options["initial_unitary"], dtype=complex) + elif hasattr(qobj_config, "initial_unitary"): + self._initial_unitary = np.array(qobj_config.initial_unitary, dtype=complex) if self._initial_unitary is not None: # Check the initial unitary is actually unitary shape = np.shape(self._initial_unitary) if len(shape) != 2 or shape[0] != shape[1]: raise BasicAerError("initial unitary is not a square matrix") iden = np.eye(len(self._initial_unitary)) - u_dagger_u = np.dot(self._initial_unitary.T.conj(), - self._initial_unitary) + u_dagger_u = np.dot(self._initial_unitary.T.conj(), self._initial_unitary) norm = np.linalg.norm(u_dagger_u - iden) if round(norm, 10) != 0: raise BasicAerError("initial unitary is not unitary") @@ -209,9 +182,9 @@ def _set_options(self, qobj_config=None, backend_options=None): # Check for custom chop threshold # Replace with custom options - if 'chop_threshold' in backend_options: - self._chop_threshold = backend_options['chop_threshold'] - elif hasattr(qobj_config, 'chop_threshold'): + if "chop_threshold" in backend_options: + self._chop_threshold = backend_options["chop_threshold"] + elif hasattr(qobj_config, "chop_threshold"): self._chop_threshold = qobj_config.chop_threshold def _initialize_unitary(self): @@ -219,13 +192,11 @@ def _initialize_unitary(self): self._validate_initial_unitary() if self._initial_unitary is None: # Set to identity matrix - self._unitary = np.eye(2 ** self._number_of_qubits, - dtype=complex) + self._unitary = np.eye(2 ** self._number_of_qubits, dtype=complex) else: self._unitary = self._initial_unitary.copy() # Reshape to rank-N tensor - self._unitary = np.reshape(self._unitary, - self._number_of_qubits * [2, 2]) + self._unitary = np.reshape(self._unitary, self._number_of_qubits * [2, 2]) def _get_unitary(self): """Return the current unitary""" @@ -272,20 +243,20 @@ def run(self, qobj, **backend_options): """ if isinstance(qobj, (QuantumCircuit, list)): from qiskit.compiler import assemble + out_options = {} for key in backend_options: if not hasattr(self.options, key): warnings.warn( - "Option %s is not used by this backend" % key, - UserWarning, stacklevel=2) + "Option %s is not used by this backend" % key, UserWarning, stacklevel=2 + ) else: out_options[key] = backend_options[key] qobj = assemble(qobj, self, **out_options) qobj_options = qobj.config else: qobj_options = None - self._set_options(qobj_config=qobj_options, - backend_options=backend_options) + self._set_options(qobj_config=qobj_options, backend_options=backend_options) job_id = str(uuid.uuid4()) job = BasicAerJob(self, job_id, self._run_job(job_id, qobj)) return job @@ -306,15 +277,17 @@ def _run_job(self, job_id, qobj): for experiment in qobj.experiments: result_list.append(self.run_experiment(experiment)) end = time.time() - result = {'backend_name': self.name(), - 'backend_version': self._configuration.backend_version, - 'qobj_id': qobj.qobj_id, - 'job_id': job_id, - 'results': result_list, - 'status': 'COMPLETED', - 'success': True, - 'time_taken': (end - start), - 'header': qobj.header.to_dict()} + result = { + "backend_name": self.name(), + "backend_version": self._configuration.backend_version, + "qobj_id": qobj.qobj_id, + "job_id": job_id, + "results": result_list, + "status": "COMPLETED", + "success": True, + "time_taken": (end - start), + "header": qobj.header.to_dict(), + } return Result.from_dict(result) @@ -354,41 +327,43 @@ def run_experiment(self, experiment): self._initialize_unitary() for operation in experiment.instructions: - if operation.name == 'unitary': + if operation.name == "unitary": qubits = operation.qubits gate = operation.params[0] self._add_unitary(gate, qubits) # Check if single gate elif operation.name in SINGLE_QUBIT_GATES: - params = getattr(operation, 'params', None) + params = getattr(operation, "params", None) qubit = operation.qubits[0] gate = single_gate_matrix(operation.name, params) self._add_unitary(gate, [qubit]) - elif operation.name in ('id', 'u0'): + elif operation.name in ("id", "u0"): pass # Check if CX gate - elif operation.name in ('CX', 'cx'): + elif operation.name in ("CX", "cx"): qubit0 = operation.qubits[0] qubit1 = operation.qubits[1] gate = cx_gate_matrix() self._add_unitary(gate, [qubit0, qubit1]) # Check if barrier - elif operation.name == 'barrier': + elif operation.name == "barrier": pass else: backend = self.name() err_msg = '{0} encountered unrecognized operation "{1}"' raise BasicAerError(err_msg.format(backend, operation.name)) # Add final state to data - data = {'unitary': self._get_unitary()} + data = {"unitary": self._get_unitary()} end = time.time() - return {'name': experiment.header.name, - 'shots': 1, - 'data': data, - 'status': 'DONE', - 'success': True, - 'time_taken': (end - start), - 'header': experiment.header.to_dict()} + return { + "name": experiment.header.name, + "shots": 1, + "data": data, + "status": "DONE", + "success": True, + "time_taken": (end - start), + "header": experiment.header.to_dict(), + } def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. @@ -399,22 +374,28 @@ def _validate(self, qobj): n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: - raise BasicAerError('Number of qubits {} '.format(n_qubits) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'for "{}".'.format(self.name())) - if hasattr(qobj.config, 'shots') and qobj.config.shots != 1: - logger.info('"%s" only supports 1 shot. Setting shots=1.', - self.name()) + raise BasicAerError( + "Number of qubits {} ".format(n_qubits) + + "is greater than maximum ({}) ".format(max_qubits) + + 'for "{}".'.format(self.name()) + ) + if hasattr(qobj.config, "shots") and qobj.config.shots != 1: + logger.info('"%s" only supports 1 shot. Setting shots=1.', self.name()) qobj.config.shots = 1 for experiment in qobj.experiments: name = experiment.header.name - if getattr(experiment.config, 'shots', 1) != 1: - logger.info('"%s" only supports 1 shot. ' - 'Setting shots=1 for circuit "%s".', - self.name(), name) + if getattr(experiment.config, "shots", 1) != 1: + logger.info( + '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', + self.name(), + name, + ) experiment.config.shots = 1 for operation in experiment.instructions: - if operation.name in ['measure', 'reset']: - raise BasicAerError('Unsupported "%s" instruction "%s" ' + - 'in circuit "%s" ', self.name(), - operation.name, name) + if operation.name in ["measure", "reset"]: + raise BasicAerError( + 'Unsupported "%s" instruction "%s" ' + 'in circuit "%s" ', + self.name(), + operation.name, + name, + ) diff --git a/qiskit/providers/job.py b/qiskit/providers/job.py index 78d90284ca3e..ed9a4d756f0d 100644 --- a/qiskit/providers/job.py +++ b/qiskit/providers/job.py @@ -29,6 +29,7 @@ class Job: the versioned abstract classes as the parent class and not this class directly. """ + version = 0 @@ -41,6 +42,7 @@ class JobV1(Job, ABC): future versions of this abstract class to change the data model and interface. """ + version = 1 _async = True @@ -82,10 +84,7 @@ def in_final_state(self) -> bool: return self.status() in JOB_FINAL_STATES def wait_for_final_state( - self, - timeout: Optional[float] = None, - wait: float = 5, - callback: Optional[Callable] = None + self, timeout: Optional[float] = None, wait: float = 5, callback: Optional[Callable] = None ) -> None: """Poll the job status until it progresses to a final state such as ``DONE`` or ``ERROR``. @@ -113,8 +112,7 @@ def wait_for_final_state( while status not in JOB_FINAL_STATES: elapsed_time = time.time() - start_time if timeout is not None and elapsed_time >= timeout: - raise JobTimeoutError( - 'Timeout while waiting for job {}.'.format(self.job_id())) + raise JobTimeoutError("Timeout while waiting for job {}.".format(self.job_id())) if callback: callback(self.job_id(), status, self) time.sleep(wait) diff --git a/qiskit/providers/jobstatus.py b/qiskit/providers/jobstatus.py index eace3763db8e..7ba333db16e5 100644 --- a/qiskit/providers/jobstatus.py +++ b/qiskit/providers/jobstatus.py @@ -18,17 +18,13 @@ class JobStatus(enum.Enum): """Class for job status enumerated type.""" - INITIALIZING = 'job is being initialized' - QUEUED = 'job is queued' - VALIDATING = 'job is being validated' - RUNNING = 'job is actively running' - CANCELLED = 'job has been cancelled' - DONE = 'job has successfully run' - ERROR = 'job incurred error' + INITIALIZING = "job is being initialized" + QUEUED = "job is queued" + VALIDATING = "job is being validated" + RUNNING = "job is actively running" + CANCELLED = "job has been cancelled" + DONE = "job has successfully run" + ERROR = "job incurred error" -JOB_FINAL_STATES = ( - JobStatus.DONE, - JobStatus.CANCELLED, - JobStatus.ERROR -) +JOB_FINAL_STATES = (JobStatus.DONE, JobStatus.CANCELLED, JobStatus.ERROR) diff --git a/qiskit/providers/models/__init__.py b/qiskit/providers/models/__init__.py index cde0c338a686..37485b2b5002 100644 --- a/qiskit/providers/models/__init__.py +++ b/qiskit/providers/models/__init__.py @@ -37,8 +37,13 @@ JobStatus """ -from .backendconfiguration import (BackendConfiguration, PulseBackendConfiguration, - QasmBackendConfiguration, UchannelLO, GateConfig) +from .backendconfiguration import ( + BackendConfiguration, + PulseBackendConfiguration, + QasmBackendConfiguration, + UchannelLO, + GateConfig, +) from .backendproperties import BackendProperties from .backendstatus import BackendStatus from .jobstatus import JobStatus diff --git a/qiskit/providers/models/backendconfiguration.py b/qiskit/providers/models/backendconfiguration.py index 6acf1f126454..82fe20c93b1e 100644 --- a/qiskit/providers/models/backendconfiguration.py +++ b/qiskit/providers/models/backendconfiguration.py @@ -20,8 +20,13 @@ from qiskit.exceptions import QiskitError from qiskit.providers.exceptions import BackendConfigurationError -from qiskit.pulse.channels import (AcquireChannel, Channel, ControlChannel, - DriveChannel, MeasureChannel) +from qiskit.pulse.channels import ( + AcquireChannel, + Channel, + ControlChannel, + DriveChannel, + MeasureChannel, +) class GateConfig: @@ -34,8 +39,16 @@ class GateConfig: and CX. """ - def __init__(self, name, parameters, qasm_def, coupling_map=None, - latency_map=None, conditional=None, description=None): + def __init__( + self, + name, + parameters, + qasm_def, + coupling_map=None, + latency_map=None, + conditional=None, + description=None, + ): """Initialize a GateConfig object Args: @@ -93,18 +106,18 @@ def to_dict(self): dict: The dictionary form of the GateConfig. """ out_dict = { - 'name': self.name, - 'parameters': self.parameters, - 'qasm_def': self.qasm_def, + "name": self.name, + "parameters": self.parameters, + "qasm_def": self.qasm_def, } - if hasattr(self, 'coupling_map'): - out_dict['coupling_map'] = self.coupling_map - if hasattr(self, 'latency_map'): - out_dict['latency_map'] = self.latency_map - if hasattr(self, 'conditional'): - out_dict['conditional'] = self.conditional - if hasattr(self, 'description'): - out_dict['description'] = self.description + if hasattr(self, "coupling_map"): + out_dict["coupling_map"] = self.coupling_map + if hasattr(self, "latency_map"): + out_dict["latency_map"] = self.latency_map + if hasattr(self, "conditional"): + out_dict["conditional"] = self.conditional + if hasattr(self, "description"): + out_dict["description"] = self.description return out_dict def __eq__(self, other): @@ -114,12 +127,11 @@ def __eq__(self, other): return False def __repr__(self): - out_str = "GateConfig(%s, %s, %s" % (self.name, self.parameters, - self.qasm_def) - for i in ['coupling_map', 'latency_map', 'conditional', 'description']: + out_str = "GateConfig(%s, %s, %s" % (self.name, self.parameters, self.qasm_def) + for i in ["coupling_map", "latency_map", "conditional", "description"]: if hasattr(self, i): - out_str += ', ' + repr(getattr(self, i)) - out_str += ')' + out_str += ", " + repr(getattr(self, i)) + out_str += ")" return out_str @@ -142,7 +154,7 @@ def __init__(self, q, scale): QiskitError: If q is < 0 """ if q < 0: - raise QiskitError('q must be >=0') + raise QiskitError("q must be >=0") self.q = q self.scale = scale @@ -167,8 +179,8 @@ def to_dict(self): dict: The dictionary form of the UChannelLO. """ out_dict = { - 'q': self.q, - 'scale': self.scale, + "q": self.q, + "scale": self.scale, } return out_dict @@ -201,16 +213,39 @@ class QasmBackendConfiguration: _data = {} - def __init__(self, backend_name, backend_version, n_qubits, - basis_gates, gates, local, simulator, - conditional, open_pulse, memory, - max_shots, coupling_map, supported_instructions=None, - dynamic_reprate_enabled=False, rep_delay_range=None, - default_rep_delay=None, max_experiments=None, - sample_name=None, n_registers=None, register_map=None, - configurable=None, credits_required=None, online_date=None, - display_name=None, description=None, tags=None, dt=None, dtm=None, - processor_type=None, **kwargs): + def __init__( + self, + backend_name, + backend_version, + n_qubits, + basis_gates, + gates, + local, + simulator, + conditional, + open_pulse, + memory, + max_shots, + coupling_map, + supported_instructions=None, + dynamic_reprate_enabled=False, + rep_delay_range=None, + default_rep_delay=None, + max_experiments=None, + sample_name=None, + n_registers=None, + register_map=None, + configurable=None, + credits_required=None, + online_date=None, + display_name=None, + description=None, + tags=None, + dt=None, + dtm=None, + processor_type=None, + **kwargs, + ): """Initialize a QasmBackendConfiguration Object Args: @@ -286,7 +321,7 @@ def __init__(self, backend_name, backend_version, n_qubits, if rep_delay_range: self.rep_delay_range = [_rd * 1e-6 for _rd in rep_delay_range] # convert to sec if default_rep_delay is not None: - self.default_rep_delay = default_rep_delay * 1e-6 # convert to sec + self.default_rep_delay = default_rep_delay * 1e-6 # convert to sec # max_experiments must be >=1 if max_experiments: @@ -320,17 +355,21 @@ def __init__(self, backend_name, backend_version, n_qubits, if processor_type is not None: self.processor_type = processor_type - if 'qubit_lo_range' in kwargs.keys(): - kwargs['qubit_lo_range'] = [[min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in kwargs['qubit_lo_range']] + if "qubit_lo_range" in kwargs.keys(): + kwargs["qubit_lo_range"] = [ + [min_range * 1e9, max_range * 1e9] + for (min_range, max_range) in kwargs["qubit_lo_range"] + ] - if 'meas_lo_range' in kwargs.keys(): - kwargs['meas_lo_range'] = [[min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in kwargs['meas_lo_range']] + if "meas_lo_range" in kwargs.keys(): + kwargs["meas_lo_range"] = [ + [min_range * 1e9, max_range * 1e9] + for (min_range, max_range) in kwargs["meas_lo_range"] + ] # convert rep_times from μs to sec - if 'rep_times' in kwargs.keys(): - kwargs['rep_times'] = [_rt * 1e-6 for _rt in kwargs['rep_times']] + if "rep_times" in kwargs.keys(): + kwargs["rep_times"] = [_rt * 1e-6 for _rt in kwargs["rep_times"]] self._data.update(kwargs) @@ -338,7 +377,7 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex @classmethod def from_dict(cls, data): @@ -352,8 +391,8 @@ def from_dict(cls, data): GateConfig: The GateConfig from the input dictionary. """ in_data = copy.copy(data) - gates = [GateConfig.from_dict(x) for x in in_data.pop('gates')] - in_data['gates'] = gates + gates = [GateConfig.from_dict(x) for x in in_data.pop("gates")] + in_data["gates"] = gates return cls(**in_data) def to_dict(self): @@ -363,53 +402,64 @@ def to_dict(self): dict: The dictionary form of the GateConfig. """ out_dict = { - 'backend_name': self.backend_name, - 'backend_version': self.backend_version, - 'n_qubits': self.n_qubits, - 'basis_gates': self.basis_gates, - 'gates': [x.to_dict() for x in self.gates], - 'local': self.local, - 'simulator': self.simulator, - 'conditional': self.conditional, - 'open_pulse': self.open_pulse, - 'memory': self.memory, - 'max_shots': self.max_shots, - 'coupling_map': self.coupling_map, - 'dynamic_reprate_enabled': self.dynamic_reprate_enabled + "backend_name": self.backend_name, + "backend_version": self.backend_version, + "n_qubits": self.n_qubits, + "basis_gates": self.basis_gates, + "gates": [x.to_dict() for x in self.gates], + "local": self.local, + "simulator": self.simulator, + "conditional": self.conditional, + "open_pulse": self.open_pulse, + "memory": self.memory, + "max_shots": self.max_shots, + "coupling_map": self.coupling_map, + "dynamic_reprate_enabled": self.dynamic_reprate_enabled, } - if hasattr(self, 'supported_instructions'): - out_dict['supported_instructions'] = self.supported_instructions - - if hasattr(self, 'rep_delay_range'): - out_dict['rep_delay_range'] = [_rd * 1e6 for _rd in self.rep_delay_range] - if hasattr(self, 'default_rep_delay'): - out_dict['default_rep_delay'] = self.default_rep_delay*1e6 - - for kwarg in ['max_experiments', 'sample_name', 'n_registers', - 'register_map', 'configurable', 'credits_required', - 'online_date', 'display_name', 'description', - 'tags', 'dt', 'dtm', 'processor_type']: + if hasattr(self, "supported_instructions"): + out_dict["supported_instructions"] = self.supported_instructions + + if hasattr(self, "rep_delay_range"): + out_dict["rep_delay_range"] = [_rd * 1e6 for _rd in self.rep_delay_range] + if hasattr(self, "default_rep_delay"): + out_dict["default_rep_delay"] = self.default_rep_delay * 1e6 + + for kwarg in [ + "max_experiments", + "sample_name", + "n_registers", + "register_map", + "configurable", + "credits_required", + "online_date", + "display_name", + "description", + "tags", + "dt", + "dtm", + "processor_type", + ]: if hasattr(self, kwarg): out_dict[kwarg] = getattr(self, kwarg) out_dict.update(self._data) - if 'dt' in out_dict: - out_dict['dt'] *= 1e9 - if 'dtm' in out_dict: - out_dict['dtm'] *= 1e9 + if "dt" in out_dict: + out_dict["dt"] *= 1e9 + if "dtm" in out_dict: + out_dict["dtm"] *= 1e9 - if 'qubit_lo_range' in out_dict: - out_dict['qubit_lo_range'] = [ - [min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in out_dict['qubit_lo_range'] + if "qubit_lo_range" in out_dict: + out_dict["qubit_lo_range"] = [ + [min_range * 1e9, max_range * 1e9] + for (min_range, max_range) in out_dict["qubit_lo_range"] ] - if 'meas_lo_range' in out_dict: - out_dict['meas_lo_range'] = [ - [min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in out_dict['meas_lo_range'] + if "meas_lo_range" in out_dict: + out_dict["meas_lo_range"] = [ + [min_range * 1e9, max_range * 1e9] + for (min_range, max_range) in out_dict["meas_lo_range"] ] return out_dict @@ -436,6 +486,7 @@ def __contains__(self, item): class BackendConfiguration(QasmBackendConfiguration): """Backwards compat shim representing an abstract backend configuration.""" + pass @@ -444,46 +495,48 @@ class PulseBackendConfiguration(QasmBackendConfiguration): about the set up of the device which can be useful for building Pulse programs. """ - def __init__(self, - backend_name: str, - backend_version: str, - n_qubits: int, - basis_gates: List[str], - gates: GateConfig, - local: bool, - simulator: bool, - conditional: bool, - open_pulse: bool, - memory: bool, - max_shots: int, - coupling_map, - n_uchannels: int, - u_channel_lo: List[List[UchannelLO]], - meas_levels: List[int], - qubit_lo_range: List[List[float]], - meas_lo_range: List[List[float]], - dt: float, - dtm: float, - rep_times: List[float], - meas_kernels: List[str], - discriminators: List[str], - hamiltonian: Dict[str, Any] = None, - channel_bandwidth=None, - acquisition_latency=None, - conditional_latency=None, - meas_map=None, - max_experiments=None, - sample_name=None, - n_registers=None, - register_map=None, - configurable=None, - credits_required=None, - online_date=None, - display_name=None, - description=None, - tags=None, - channels: Dict[str, Any] = None, - **kwargs): + def __init__( + self, + backend_name: str, + backend_version: str, + n_qubits: int, + basis_gates: List[str], + gates: GateConfig, + local: bool, + simulator: bool, + conditional: bool, + open_pulse: bool, + memory: bool, + max_shots: int, + coupling_map, + n_uchannels: int, + u_channel_lo: List[List[UchannelLO]], + meas_levels: List[int], + qubit_lo_range: List[List[float]], + meas_lo_range: List[List[float]], + dt: float, + dtm: float, + rep_times: List[float], + meas_kernels: List[str], + discriminators: List[str], + hamiltonian: Dict[str, Any] = None, + channel_bandwidth=None, + acquisition_latency=None, + conditional_latency=None, + meas_map=None, + max_experiments=None, + sample_name=None, + n_registers=None, + register_map=None, + configurable=None, + credits_required=None, + online_date=None, + display_name=None, + description=None, + tags=None, + channels: Dict[str, Any] = None, + **kwargs, + ): """ Initialize a backend configuration that contains all the extra configuration that is made available for OpenPulse backends. @@ -543,18 +596,20 @@ def __init__(self, self.n_uchannels = n_uchannels self.u_channel_lo = u_channel_lo self.meas_levels = meas_levels - self.qubit_lo_range = [[min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in qubit_lo_range] - self.meas_lo_range = [[min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in meas_lo_range] + self.qubit_lo_range = [ + [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in qubit_lo_range + ] + self.meas_lo_range = [ + [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in meas_lo_range + ] self.meas_kernels = meas_kernels self.discriminators = discriminators self.hamiltonian = hamiltonian if hamiltonian is not None: self.hamiltonian = dict(hamiltonian) - self.hamiltonian['vars'] = { + self.hamiltonian["vars"] = { k: v * 1e9 if isinstance(v, numbers.Number) else v - for k, v in self.hamiltonian['vars'].items() + for k, v in self.hamiltonian["vars"].items() } self.rep_times = [_rt * 1e-6 for _rt in rep_times] # convert to sec @@ -565,31 +620,49 @@ def __init__(self, if channels is not None: self.channels = channels - (self._qubit_channel_map, - self._channel_qubit_map, - self._control_channels) = self._parse_channels(channels=channels) + ( + self._qubit_channel_map, + self._channel_qubit_map, + self._control_channels, + ) = self._parse_channels(channels=channels) else: self._control_channels = defaultdict(list) if channel_bandwidth is not None: - self.channel_bandwidth = [[min_range * 1e9, max_range * 1e9] for - (min_range, max_range) in channel_bandwidth] + self.channel_bandwidth = [ + [min_range * 1e9, max_range * 1e9] for (min_range, max_range) in channel_bandwidth + ] if acquisition_latency is not None: self.acquisition_latency = acquisition_latency if conditional_latency is not None: self.conditional_latency = conditional_latency if meas_map is not None: self.meas_map = meas_map - super().__init__(backend_name=backend_name, backend_version=backend_version, - n_qubits=n_qubits, basis_gates=basis_gates, gates=gates, - local=local, simulator=simulator, conditional=conditional, - open_pulse=open_pulse, memory=memory, max_shots=max_shots, - coupling_map=coupling_map, max_experiments=max_experiments, - sample_name=sample_name, n_registers=n_registers, - register_map=register_map, configurable=configurable, - credits_required=credits_required, online_date=online_date, - display_name=display_name, description=description, - tags=tags, **kwargs) + super().__init__( + backend_name=backend_name, + backend_version=backend_version, + n_qubits=n_qubits, + basis_gates=basis_gates, + gates=gates, + local=local, + simulator=simulator, + conditional=conditional, + open_pulse=open_pulse, + memory=memory, + max_shots=max_shots, + coupling_map=coupling_map, + max_experiments=max_experiments, + sample_name=sample_name, + n_registers=n_registers, + register_map=register_map, + configurable=configurable, + credits_required=credits_required, + online_date=online_date, + display_name=display_name, + description=description, + tags=tags, + **kwargs, + ) @classmethod def from_dict(cls, data): @@ -603,13 +676,13 @@ def from_dict(cls, data): GateConfig: The GateConfig from the input dictionary. """ in_data = copy.copy(data) - gates = [GateConfig.from_dict(x) for x in in_data.pop('gates')] - in_data['gates'] = gates - input_uchannels = in_data.pop('u_channel_lo') + gates = [GateConfig.from_dict(x) for x in in_data.pop("gates")] + in_data["gates"] = gates + input_uchannels = in_data.pop("u_channel_lo") u_channels = [] for channel in input_uchannels: u_channels.append([UchannelLO.from_dict(x) for x in channel]) - in_data['u_channel_lo'] = u_channels + in_data["u_channel_lo"] = u_channels return cls(**in_data) def to_dict(self): @@ -625,63 +698,68 @@ def to_dict(self): for y in x: channel.append(y.to_dict()) u_channel_lo.append(channel) - out_dict.update({ - 'n_uchannels': self.n_uchannels, - 'u_channel_lo': u_channel_lo, - 'meas_levels': self.meas_levels, - 'qubit_lo_range': self.qubit_lo_range, - 'meas_lo_range': self.meas_lo_range, - 'meas_kernels': self.meas_kernels, - 'discriminators': self.discriminators, - 'rep_times': self.rep_times, - 'dt': self.dt, - 'dtm': self.dtm, - }) - - if hasattr(self, 'channel_bandwidth'): - out_dict['channel_bandwidth'] = self.channel_bandwidth - if hasattr(self, 'meas_map'): - out_dict['meas_map'] = self.meas_map - if hasattr(self, 'acquisition_latency'): - out_dict['acquisition_latency'] = self.acquisition_latency - if hasattr(self, 'conditional_latency'): - out_dict['conditional_latency'] = self.conditional_latency - if 'channels' in out_dict: - out_dict.pop('_qubit_channel_map') - out_dict.pop('_channel_qubit_map') - out_dict.pop('_control_channels') + out_dict.update( + { + "n_uchannels": self.n_uchannels, + "u_channel_lo": u_channel_lo, + "meas_levels": self.meas_levels, + "qubit_lo_range": self.qubit_lo_range, + "meas_lo_range": self.meas_lo_range, + "meas_kernels": self.meas_kernels, + "discriminators": self.discriminators, + "rep_times": self.rep_times, + "dt": self.dt, + "dtm": self.dtm, + } + ) + + if hasattr(self, "channel_bandwidth"): + out_dict["channel_bandwidth"] = self.channel_bandwidth + if hasattr(self, "meas_map"): + out_dict["meas_map"] = self.meas_map + if hasattr(self, "acquisition_latency"): + out_dict["acquisition_latency"] = self.acquisition_latency + if hasattr(self, "conditional_latency"): + out_dict["conditional_latency"] = self.conditional_latency + if "channels" in out_dict: + out_dict.pop("_qubit_channel_map") + out_dict.pop("_channel_qubit_map") + out_dict.pop("_control_channels") if self.qubit_lo_range: - out_dict['qubit_lo_range'] = [ - [min_range * 1e-9, max_range * 1e-9] for - (min_range, max_range) in self.qubit_lo_range] + out_dict["qubit_lo_range"] = [ + [min_range * 1e-9, max_range * 1e-9] + for (min_range, max_range) in self.qubit_lo_range + ] if self.meas_lo_range: - out_dict['meas_lo_range'] = [ - [min_range * 1e-9, max_range * 1e-9] for - (min_range, max_range) in self.meas_lo_range] + out_dict["meas_lo_range"] = [ + [min_range * 1e-9, max_range * 1e-9] + for (min_range, max_range) in self.meas_lo_range + ] if self.rep_times: - out_dict['rep_times'] = [_rt * 1e6 for _rt in self.rep_times] + out_dict["rep_times"] = [_rt * 1e6 for _rt in self.rep_times] - out_dict['dt'] *= 1e9 - out_dict['dtm'] *= 1e9 + out_dict["dt"] *= 1e9 + out_dict["dtm"] *= 1e9 - if hasattr(self, 'channel_bandwidth'): - out_dict['channel_bandwidth'] = [ - [min_range * 1e-9, max_range * 1e-9] for - (min_range, max_range) in self.channel_bandwidth] + if hasattr(self, "channel_bandwidth"): + out_dict["channel_bandwidth"] = [ + [min_range * 1e-9, max_range * 1e-9] + for (min_range, max_range) in self.channel_bandwidth + ] if self.hamiltonian: hamiltonian = copy.deepcopy(self.hamiltonian) - hamiltonian['vars'] = { + hamiltonian["vars"] = { k: v * 1e-9 if isinstance(v, numbers.Number) else v - for k, v in hamiltonian['vars'].items() + for k, v in hamiltonian["vars"].items() } - out_dict['hamiltonian'] = hamiltonian + out_dict["hamiltonian"] = hamiltonian - if hasattr(self, 'channels'): - out_dict['channels'] = self.channels + if hasattr(self, "channels"): + out_dict["channels"] = self.channels return out_dict @@ -741,8 +819,7 @@ def acquire(self, qubit: int) -> AcquireChannel: raise BackendConfigurationError("Invalid index for {}-qubit systems.".format(qubit)) return AcquireChannel(qubit) - def control(self, qubits: Iterable[int] = None, - channel: int = None) -> List[ControlChannel]: + def control(self, qubits: Iterable[int] = None, channel: int = None) -> List[ControlChannel]: """ Return the secondary drive channel for the given qubit -- typically utilized for controlling multiqubit interactions. This channel is derived from other channels. @@ -759,10 +836,12 @@ def control(self, qubits: Iterable[int] = None, List of control channels. """ if channel is not None: - warnings.warn('The channel argument has been deprecated in favor of qubits. ' - 'This method will now return accurate ControlChannels determined ' - 'by qubit indices.', - DeprecationWarning) + warnings.warn( + "The channel argument has been deprecated in favor of qubits. " + "This method will now return accurate ControlChannels determined " + "by qubit indices.", + DeprecationWarning, + ) qubits = [channel] try: if isinstance(qubits, list): @@ -881,12 +960,12 @@ def _parse_channels(self, channels: Dict[set, Any]) -> Dict[Any, Any]: DriveChannel.prefix: DriveChannel, ControlChannel.prefix: ControlChannel, MeasureChannel.prefix: MeasureChannel, - 'acquire': AcquireChannel + "acquire": AcquireChannel, } for channel, config in channels.items(): channel_prefix, index = self._get_channel_prefix_index(channel) channel_type = channels_dict[channel_prefix] - qubits = tuple(config['operates']['qubits']) + qubits = tuple(config["operates"]["qubits"]) if channel_prefix in channels_dict: qubit_channel_map[qubits].append(channel_type(index)) channel_qubit_map[(channel_type(index))].extend(list(qubits)) @@ -909,6 +988,6 @@ def _get_channel_prefix_index(self, channel: str) -> str: """ channel_prefix = re.match(r"(?P[a-z]+)(?P[0-9]+)", channel) try: - return channel_prefix.group('channel'), int(channel_prefix.group('index')) + return channel_prefix.group("channel"), int(channel_prefix.group("index")) except AttributeError as ex: raise BackendConfigurationError(f"Invalid channel name - '{channel}' found.") from ex diff --git a/qiskit/providers/models/backendproperties.py b/qiskit/providers/models/backendproperties.py index 19d4418b1613..d1dd30184480 100644 --- a/qiskit/providers/models/backendproperties.py +++ b/qiskit/providers/models/backendproperties.py @@ -30,6 +30,7 @@ class Nduv: unit: unit. value: value. """ + def __init__(self, date, name, unit, value): """Initialize a new name-date-unit-value object @@ -65,10 +66,10 @@ def to_dict(self): dict: The dictionary form of the Nduv. """ out_dict = { - 'date': self.date, - 'name': self.name, - 'unit': self.unit, - 'value': self.value, + "date": self.date, + "name": self.name, + "unit": self.unit, + "value": self.value, } return out_dict @@ -79,17 +80,16 @@ def __eq__(self, other): return False def __repr__(self): - return "Nduv(%s, %s, %s, %s)" % (repr(self.date), self.name, self.unit, - self.value) + return "Nduv(%s, %s, %s, %s)" % (repr(self.date), self.name, self.unit, self.value) class Gate: """Class representing a gate's properties - Attributes: - qubits: qubits. - gate: gate. - parameters: parameters. + Attributes: + qubits: qubits. + gate: gate. + parameters: parameters. """ _data = {} @@ -114,7 +114,7 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex @classmethod def from_dict(cls, data): @@ -130,9 +130,9 @@ def from_dict(cls, data): """ in_data = copy.copy(data) nduvs = [] - for nduv in in_data.pop('parameters'): + for nduv in in_data.pop("parameters"): nduvs.append(Nduv.from_dict(nduv)) - in_data['parameters'] = nduvs + in_data["parameters"] = nduvs return cls(**in_data) def to_dict(self): @@ -142,9 +142,9 @@ def to_dict(self): dict: The dictionary form of the Gate. """ out_dict = {} - out_dict['qubits'] = self.qubits - out_dict['gate'] = self.gate - out_dict['parameters'] = [x.to_dict() for x in self.parameters] + out_dict["qubits"] = self.qubits + out_dict["gate"] = self.gate + out_dict["parameters"] = [x.to_dict() for x in self.parameters] out_dict.update(self._data) return out_dict @@ -165,8 +165,9 @@ class BackendProperties: _data = {} - def __init__(self, backend_name, backend_version, last_update_date, qubits, - gates, general, **kwargs): + def __init__( + self, backend_name, backend_version, last_update_date, qubits, gates, general, **kwargs + ): """Initialize a BackendProperties instance. Args: @@ -215,7 +216,7 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex @classmethod def from_dict(cls, data): @@ -231,19 +232,20 @@ def from_dict(cls, data): dictionary. """ in_data = copy.copy(data) - backend_name = in_data.pop('backend_name') - backend_version = in_data.pop('backend_version') - last_update_date = in_data.pop('last_update_date') + backend_name = in_data.pop("backend_name") + backend_version = in_data.pop("backend_version") + last_update_date = in_data.pop("last_update_date") qubits = [] - for qubit in in_data.pop('qubits'): + for qubit in in_data.pop("qubits"): nduvs = [] for nduv in qubit: nduvs.append(Nduv.from_dict(nduv)) qubits.append(nduvs) - gates = [Gate.from_dict(x) for x in in_data.pop('gates')] - general = [Nduv.from_dict(x) for x in in_data.pop('general')] - return cls(backend_name, backend_version, last_update_date, - qubits, gates, general, **in_data) + gates = [Gate.from_dict(x) for x in in_data.pop("gates")] + general = [Nduv.from_dict(x) for x in in_data.pop("general")] + return cls( + backend_name, backend_version, last_update_date, qubits, gates, general, **in_data + ) def to_dict(self): """Return a dictionary format representation of the BackendProperties. @@ -252,18 +254,18 @@ def to_dict(self): dict: The dictionary form of the BackendProperties. """ out_dict = { - 'backend_name': self.backend_name, - 'backend_version': self.backend_version, - 'last_update_date': self.last_update_date + "backend_name": self.backend_name, + "backend_version": self.backend_version, + "last_update_date": self.last_update_date, } - out_dict['qubits'] = [] + out_dict["qubits"] = [] for qubit in self.qubits: qubit_props = [] for item in qubit: qubit_props.append(item.to_dict()) - out_dict['qubits'].append(qubit_props) - out_dict['gates'] = [x.to_dict() for x in self.gates] - out_dict['general'] = [x.to_dict() for x in self.general] + out_dict["qubits"].append(qubit_props) + out_dict["gates"] = [x.to_dict() for x in self.gates] + out_dict["general"] = [x.to_dict() for x in self.general] out_dict.update(self._data) return out_dict @@ -273,10 +275,9 @@ def __eq__(self, other): return True return False - def gate_property(self, - gate: str, - qubits: Union[int, Iterable[int]] = None, - name: str = None) -> Tuple[Any, datetime.datetime]: + def gate_property( + self, gate: str, qubits: Union[int, Iterable[int]] = None, name: str = None + ) -> Tuple[Any, datetime.datetime]: """ Return the property of the given gate. @@ -301,15 +302,15 @@ def gate_property(self, if name: result = result[name] elif name: - raise BackendPropertyError("Provide qubits to get {n} of {g}".format(n=name, - g=gate)) + raise BackendPropertyError( + "Provide qubits to get {n} of {g}".format(n=name, g=gate) + ) except KeyError as ex: raise BackendPropertyError(f"Could not find the desired property for {gate}") from ex return result def faulty_qubits(self): - """Return a list of faulty qubits. - """ + """Return a list of faulty qubits.""" faulty = [] for qubit in self._qubits: if not self.is_qubit_operational(qubit): @@ -317,17 +318,14 @@ def faulty_qubits(self): return faulty def faulty_gates(self): - """Return a list of faulty gates. - """ + """Return a list of faulty gates.""" faulty = [] for gate in self.gates: if not self.is_gate_operational(gate.gate, gate.qubits): faulty.append(gate) return faulty - def is_gate_operational(self, - gate: str, - qubits: Union[int, Iterable[int]] = None) -> bool: + def is_gate_operational(self, gate: str, qubits: Union[int, Iterable[int]] = None) -> bool: """ Return the operational status of the given gate. @@ -340,8 +338,8 @@ def is_gate_operational(self, False otherwise. """ properties = self.gate_property(gate, qubits) - if 'operational' in properties: - return bool(properties['operational'][0]) + if "operational" in properties: + return bool(properties["operational"][0]) return True # if property operational not existent, then True. def gate_error(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: @@ -355,8 +353,7 @@ def gate_error(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: Returns: Gate error of the given gate and qubit(s). """ - return self.gate_property(gate, qubits, - 'gate_error')[0] # Throw away datetime at index 1 + return self.gate_property(gate, qubits, "gate_error")[0] # Throw away datetime at index 1 def gate_length(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: """ @@ -369,12 +366,9 @@ def gate_length(self, gate: str, qubits: Union[int, Iterable[int]]) -> float: Returns: Gate length of the given gate and qubit(s). """ - return self.gate_property(gate, qubits, - 'gate_length')[0] # Throw away datetime at index 1 + return self.gate_property(gate, qubits, "gate_length")[0] # Throw away datetime at index 1 - def qubit_property(self, - qubit: int, - name: str = None) -> Tuple[Any, datetime.datetime]: + def qubit_property(self, qubit: int, name: str = None) -> Tuple[Any, datetime.datetime]: """ Return the property of the given qubit. @@ -393,9 +387,10 @@ def qubit_property(self, if name is not None: result = result[name] except KeyError as ex: - raise BackendPropertyError("Couldn't find the propert{name} for qubit " - "{qubit}.".format(name="y '" + name + "'" if name else "ies", - qubit=qubit)) from ex + raise BackendPropertyError( + "Couldn't find the propert{name} for qubit " + "{qubit}.".format(name="y '" + name + "'" if name else "ies", qubit=qubit) + ) from ex return result def t1(self, qubit: int) -> float: # pylint: disable=invalid-name @@ -408,7 +403,7 @@ def t1(self, qubit: int) -> float: # pylint: disable=invalid-name Returns: T1 time of the given qubit. """ - return self.qubit_property(qubit, 'T1')[0] # Throw away datetime at index 1 + return self.qubit_property(qubit, "T1")[0] # Throw away datetime at index 1 def t2(self, qubit: int) -> float: # pylint: disable=invalid-name """ @@ -420,7 +415,7 @@ def t2(self, qubit: int) -> float: # pylint: disable=invalid-name Returns: T2 time of the given qubit. """ - return self.qubit_property(qubit, 'T2')[0] # Throw away datetime at index 1 + return self.qubit_property(qubit, "T2")[0] # Throw away datetime at index 1 def frequency(self, qubit: int) -> float: """ @@ -432,7 +427,7 @@ def frequency(self, qubit: int) -> float: Returns: Frequency of the given qubit. """ - return self.qubit_property(qubit, 'frequency')[0] # Throw away datetime at index 1 + return self.qubit_property(qubit, "frequency")[0] # Throw away datetime at index 1 def readout_error(self, qubit: int) -> float: """ @@ -444,7 +439,7 @@ def readout_error(self, qubit: int) -> float: Return: Readout error of the given qubit. """ - return self.qubit_property(qubit, 'readout_error')[0] # Throw away datetime at index 1 + return self.qubit_property(qubit, "readout_error")[0] # Throw away datetime at index 1 def readout_length(self, qubit: int) -> float: """ @@ -456,7 +451,7 @@ def readout_length(self, qubit: int) -> float: Return: Readout length of the given qubit. """ - return self.qubit_property(qubit, 'readout_length')[0] # Throw away datetime at index 1 + return self.qubit_property(qubit, "readout_length")[0] # Throw away datetime at index 1 def is_qubit_operational(self, qubit: int) -> bool: """ @@ -469,8 +464,8 @@ def is_qubit_operational(self, qubit: int) -> bool: Operational status of the given qubit. """ properties = self.qubit_property(qubit) - if 'operational' in properties: - return bool(properties['operational'][0]) + if "operational" in properties: + return bool(properties["operational"][0]) return True # if property operational not existent, then True. def _apply_prefix(self, value: float, unit: str) -> float: diff --git a/qiskit/providers/models/backendstatus.py b/qiskit/providers/models/backendstatus.py index 57b7fcbb4526..d266fbb3ad2f 100644 --- a/qiskit/providers/models/backendstatus.py +++ b/qiskit/providers/models/backendstatus.py @@ -18,8 +18,7 @@ class BackendStatus: """Class representing Backend Status.""" - def __init__(self, backend_name, backend_version, operational, - pending_jobs, status_msg): + def __init__(self, backend_name, backend_version, operational, pending_jobs, status_msg): """Initialize a BackendStatus object Args: @@ -36,7 +35,7 @@ def __init__(self, backend_name, backend_version, operational, self.backend_version = backend_version self.operational = operational if pending_jobs < 0: - raise QiskitError('Pending jobs must be >=0') + raise QiskitError("Pending jobs must be >=0") self.pending_jobs = pending_jobs self.status_msg = status_msg diff --git a/qiskit/providers/models/jobstatus.py b/qiskit/providers/models/jobstatus.py index dee7a15866aa..4a52f0db56a5 100644 --- a/qiskit/providers/models/jobstatus.py +++ b/qiskit/providers/models/jobstatus.py @@ -53,9 +53,9 @@ def to_dict(self): dict: The dictionary form of the JobStatus. """ out_dict = { - 'job_id': self.job_id, - 'status': self.status, - 'status_msg': self.status_msg, + "job_id": self.job_id, + "status": self.status, + "status_msg": self.status_msg, } out_dict.update(self._data) return out_dict @@ -64,4 +64,4 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex diff --git a/qiskit/providers/models/pulsedefaults.py b/qiskit/providers/models/pulsedefaults.py index 61ae67c79e14..8cd7686d0ef8 100644 --- a/qiskit/providers/models/pulsedefaults.py +++ b/qiskit/providers/models/pulsedefaults.py @@ -41,7 +41,7 @@ def to_dict(self): Returns: dict: The dictionary form of the MeasurementKernel. """ - return {'name': self.name, 'params': self.params} + return {"name": self.name, "params": self.params} @classmethod def from_dict(cls, data): @@ -77,7 +77,7 @@ def to_dict(self): Returns: dict: The dictionary form of the Discriminator. """ - return {'name': self.name, 'params': self.params} + return {"name": self.name, "params": self.params} @classmethod def from_dict(cls, data): @@ -100,6 +100,7 @@ class Command: Attributes: name: Pulse command name. """ + _data = {} def __init__(self, name: str, qubits=None, sequence=None, **kwargs): @@ -123,7 +124,7 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex def to_dict(self): """Return a dictionary format representation of the Command. @@ -131,11 +132,11 @@ def to_dict(self): Returns: dict: The dictionary form of the Command. """ - out_dict = {'name': self.name} - if hasattr(self, 'qubits'): - out_dict['qubits'] = self.qubits - if hasattr(self, 'sequence'): - out_dict['sequence'] = [x.to_dict() for x in self.sequence] + out_dict = {"name": self.name} + if hasattr(self, "qubits"): + out_dict["qubits"] = self.qubits + if hasattr(self, "sequence"): + out_dict["sequence"] = [x.to_dict() for x in self.sequence] out_dict.update(self._data) return out_dict @@ -153,10 +154,10 @@ def from_dict(cls, data): dictionary. """ in_data = copy.copy(data) - if 'sequence' in in_data: - in_data['sequence'] = [ - PulseQobjInstruction.from_dict(x) for x in in_data.pop( - 'sequence')] + if "sequence" in in_data: + in_data["sequence"] = [ + PulseQobjInstruction.from_dict(x) for x in in_data.pop("sequence") + ] return cls(**in_data) @@ -168,15 +169,17 @@ class PulseDefaults: _data = {} - def __init__(self, - qubit_freq_est: List[float], - meas_freq_est: List[float], - buffer: int, - pulse_library: List[PulseLibraryItem], - cmd_def: List[Command], - meas_kernel: MeasurementKernel = None, - discriminator: Discriminator = None, - **kwargs: Dict[str, Any]): + def __init__( + self, + qubit_freq_est: List[float], + meas_freq_est: List[float], + buffer: int, + pulse_library: List[PulseLibraryItem], + cmd_def: List[Command], + meas_kernel: MeasurementKernel = None, + discriminator: Discriminator = None, + **kwargs: Dict[str, Any], + ): """ Validate and reformat transport layer inputs to initialize. Args: @@ -216,7 +219,7 @@ def __getattr__(self, name): try: return self._data[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex def to_dict(self): """Return a dictionary format representation of the PulseDefaults. @@ -224,26 +227,33 @@ def to_dict(self): dict: The dictionary form of the PulseDefaults. """ out_dict = { - 'qubit_freq_est': self.qubit_freq_est, - 'meas_freq_est': self.qubit_freq_est, - 'buffer': self.buffer, - 'pulse_library': [x.to_dict() for x in self.pulse_library], - 'cmd_def': [x.to_dict() for x in self.cmd_def], + "qubit_freq_est": self.qubit_freq_est, + "meas_freq_est": self.qubit_freq_est, + "buffer": self.buffer, + "pulse_library": [x.to_dict() for x in self.pulse_library], + "cmd_def": [x.to_dict() for x in self.cmd_def], } - if hasattr(self, 'meas_kernel'): - out_dict['meas_kernel'] = self.meas_kernel.to_dict() - if hasattr(self, 'discriminator'): - out_dict['discriminator'] = self.discriminator.to_dict() + if hasattr(self, "meas_kernel"): + out_dict["meas_kernel"] = self.meas_kernel.to_dict() + if hasattr(self, "discriminator"): + out_dict["discriminator"] = self.discriminator.to_dict() for key, value in self.__dict__.items(): - if key not in ['qubit_freq_est', 'meas_freq_est', 'buffer', - 'pulse_library', 'cmd_def', 'meas_kernel', - 'discriminator', 'converter', - 'instruction_schedule_map']: + if key not in [ + "qubit_freq_est", + "meas_freq_est", + "buffer", + "pulse_library", + "cmd_def", + "meas_kernel", + "discriminator", + "converter", + "instruction_schedule_map", + ]: out_dict[key] = value out_dict.update(self._data) - out_dict['qubit_freq_est'] = [freq * 1e-9 for freq in self.qubit_freq_est] - out_dict['meas_freq_est'] = [freq * 1e-9 for freq in self.meas_freq_est] + out_dict["qubit_freq_est"] = [freq * 1e-9 for freq in self.qubit_freq_est] + out_dict["meas_freq_est"] = [freq * 1e-9 for freq in self.meas_freq_est] return out_dict @classmethod @@ -258,16 +268,14 @@ def from_dict(cls, data): PulseDefaults: The PulseDefaults from the input dictionary. """ in_data = copy.copy(data) - in_data['pulse_library'] = [ - PulseLibraryItem.from_dict(x) for x in in_data.pop('pulse_library')] - in_data['cmd_def'] = [ - Command.from_dict(x) for x in in_data.pop('cmd_def')] - if 'meas_kernel' in in_data: - in_data['meas_kernel'] = MeasurementKernel.from_dict( - in_data.pop('meas_kernel')) - if 'discriminator' in in_data: - in_data['discriminator'] = Discriminator.from_dict( - in_data.pop('discriminator')) + in_data["pulse_library"] = [ + PulseLibraryItem.from_dict(x) for x in in_data.pop("pulse_library") + ] + in_data["cmd_def"] = [Command.from_dict(x) for x in in_data.pop("cmd_def")] + if "meas_kernel" in in_data: + in_data["meas_kernel"] = MeasurementKernel.from_dict(in_data.pop("meas_kernel")) + if "discriminator" in in_data: + in_data["discriminator"] = Discriminator.from_dict(in_data.pop("discriminator")) return cls(**in_data) def __str__(self): @@ -275,6 +283,9 @@ def __str__(self): meas_freqs = [freq / 1e9 for freq in self.meas_freq_est] qfreq = "Qubit Frequencies [GHz]\n{freqs}".format(freqs=qubit_freqs) mfreq = "Measurement Frequencies [GHz]\n{freqs} ".format(freqs=meas_freqs) - return ("<{name}({insts}{qfreq}\n{mfreq})>" - "".format(name=self.__class__.__name__, insts=str(self.instruction_schedule_map), - qfreq=qfreq, mfreq=mfreq)) + return "<{name}({insts}{qfreq}\n{mfreq})>" "".format( + name=self.__class__.__name__, + insts=str(self.instruction_schedule_map), + qfreq=qfreq, + mfreq=mfreq, + ) diff --git a/qiskit/providers/provider.py b/qiskit/providers/provider.py index dfa1ab45a199..2bd4efa7081a 100644 --- a/qiskit/providers/provider.py +++ b/qiskit/providers/provider.py @@ -25,11 +25,13 @@ class Provider: the versioned abstract classes as the parent class and not this class directly. """ + version = 0 class ProviderV1(Provider, ABC): """Base class for a Backend Provider.""" + version = 1 def get_backend(self, name=None, **kwargs): @@ -48,9 +50,9 @@ def get_backend(self, name=None, **kwargs): """ backends = self.backends(name, **kwargs) if len(backends) > 1: - raise QiskitBackendNotFoundError('More than one backend matches the criteria') + raise QiskitBackendNotFoundError("More than one backend matches the criteria") if not backends: - raise QiskitBackendNotFoundError('No backend matches the criteria') + raise QiskitBackendNotFoundError("No backend matches the criteria") return backends[0] diff --git a/qiskit/providers/providerutils.py b/qiskit/providers/providerutils.py index 4c90eb377f3b..38bf61ab6376 100644 --- a/qiskit/providers/providerutils.py +++ b/qiskit/providers/providerutils.py @@ -34,10 +34,10 @@ def filter_backends(backends, filters=None, **kwargs): list[BaseBackend]: a list of backend instances matching the conditions. """ + def _match_all(obj, criteria): """Return True if all items in criteria matches items in obj.""" - return all(getattr(obj, key_, None) == value_ for - key_, value_ in criteria.items()) + return all(getattr(obj, key_, None) == value_ for key_, value_ in criteria.items()) # Inspect the backends to decide which filters belong to # backend.configuration and which ones to backend.status, as it does @@ -52,14 +52,12 @@ def _match_all(obj, criteria): # 1. Apply backend.configuration filtering. if configuration_filters: - backends = [b for b in backends if - _match_all(b.configuration(), configuration_filters)] + backends = [b for b in backends if _match_all(b.configuration(), configuration_filters)] # 2. Apply backend.status filtering (it involves one API call for # each backend). if status_filters: - backends = [b for b in backends if - _match_all(b.status(), status_filters)] + backends = [b for b in backends if _match_all(b.status(), status_filters)] # 3. Apply acceptor filter. backends = list(filter(filters, backends)) @@ -96,7 +94,6 @@ def resolve_backend_name(name, backends, deprecated, aliased): raise LookupError("backend '{}' not found.".format(name)) if name in deprecated: - logger.warning("Backend '%s' is deprecated. Use '%s'.", name, - resolved_name) + logger.warning("Backend '%s' is deprecated. Use '%s'.", name, resolved_name) return resolved_name diff --git a/qiskit/pulse/builder.py b/qiskit/pulse/builder.py index 142ffd454820..04bd6123b7a7 100644 --- a/qiskit/pulse/builder.py +++ b/qiskit/pulse/builder.py @@ -211,7 +211,7 @@ Tuple, TypeVar, Union, - NewType + NewType, ) import numpy as np @@ -227,7 +227,7 @@ macros, library, transforms, - utils + utils, ) from qiskit.pulse.instructions import directives from qiskit.pulse.schedule import Schedule, ScheduleBlock @@ -236,19 +236,20 @@ #: contextvars.ContextVar[BuilderContext]: active builder BUILDER_CONTEXTVAR = contextvars.ContextVar("backend") -T = TypeVar('T') # pylint: disable=invalid-name +T = TypeVar("T") # pylint: disable=invalid-name -StorageLocation = NewType('StorageLocation', Union[chans.MemorySlot, chans.RegisterSlot]) +StorageLocation = NewType("StorageLocation", Union[chans.MemorySlot, chans.RegisterSlot]) -def _compile_lazy_circuit_before(function: Callable[..., T] - ) -> Callable[..., T]: +def _compile_lazy_circuit_before(function: Callable[..., T]) -> Callable[..., T]: """Decorator thats schedules and calls the lazily compiled circuit before executing the decorated builder method.""" + @functools.wraps(function) def wrapper(self, *args, **kwargs): self._compile_lazy_circuit() return function(self, *args, **kwargs) + return wrapper @@ -256,32 +257,36 @@ def _requires_backend(function: Callable[..., T]) -> Callable[..., T]: """Decorator a function to raise if it is called without a builder with a set backend. """ + @functools.wraps(function) def wrapper(self, *args, **kwargs): if self.backend is None: raise exceptions.BackendNotSet( - 'This function requires the builder to ' - 'have a "backend" set.') + "This function requires the builder to " 'have a "backend" set.' + ) return function(self, *args, **kwargs) + return wrapper -class _PulseBuilder(): +class _PulseBuilder: """Builder context class.""" __alignment_kinds__ = { - 'left': transforms.AlignLeft(), - 'right': transforms.AlignRight(), - 'sequential': transforms.AlignSequential() + "left": transforms.AlignLeft(), + "right": transforms.AlignRight(), + "sequential": transforms.AlignSequential(), } - def __init__(self, - backend=None, - block: Optional[ScheduleBlock] = None, - name: Optional[str] = None, - default_alignment: Union[str, AlignmentKind] = 'left', - default_transpiler_settings: Mapping = None, - default_circuit_scheduler_settings: Mapping = None): + def __init__( + self, + backend=None, + block: Optional[ScheduleBlock] = None, + name: Optional[str] = None, + default_alignment: Union[str, AlignmentKind] = "left", + default_transpiler_settings: Mapping = None, + default_circuit_scheduler_settings: Mapping = None, + ): """Initialize the builder context. .. note:: @@ -335,17 +340,21 @@ def __init__(self, root_block = ScheduleBlock() root_block.append(instructions.Call(subroutine=block)) else: - raise exceptions.PulseError(f'Input `block` type {block.__class__.__name__} is ' - 'not a valid format. Specify a pulse program.') + raise exceptions.PulseError( + f"Input `block` type {block.__class__.__name__} is " + "not a valid format. Specify a pulse program." + ) self._context_stack.append(root_block) # Set default alignment context alignment = _PulseBuilder.__alignment_kinds__.get(default_alignment, default_alignment) if not isinstance(alignment, AlignmentKind): - raise exceptions.PulseError(f'Given `default_alignment` {repr(default_alignment)} is ' - 'not a valid transformation. Set one of ' - f'{", ".join(_PulseBuilder.__alignment_kinds__.keys())}, ' - 'or set an instance of `AlignmentKind` subclass.') + raise exceptions.PulseError( + f"Given `default_alignment` {repr(default_alignment)} is " + "not a valid transformation. Set one of " + f'{", ".join(_PulseBuilder.__alignment_kinds__.keys())}, ' + "or set an instance of `AlignmentKind` subclass." + ) self.push_context(alignment) def __enter__(self) -> ScheduleBlock: @@ -385,7 +394,7 @@ def push_context(self, alignment: AlignmentKind): def pop_context(self) -> ScheduleBlock: """Pop the last context from the stack.""" if len(self._context_stack) == 1: - raise exceptions.PulseError('The root context cannot be popped out.') + raise exceptions.PulseError("The root context cannot be popped out.") return self._context_stack.pop() @@ -455,12 +464,10 @@ def _compile_circuit(self, circ) -> Schedule: """Take a QuantumCircuit and output the pulse schedule associated with the circuit.""" import qiskit.compiler as compiler # pylint: disable=cyclic-import - transpiled_circuit = compiler.transpile(circ, - self.backend, - **self.transpiler_settings) - sched = compiler.schedule(transpiled_circuit, - self.backend, - **self.circuit_scheduler_settings) + transpiled_circuit = compiler.transpile(circ, self.backend, **self.transpiler_settings) + sched = compiler.schedule( + transpiled_circuit, self.backend, **self.circuit_scheduler_settings + ) return sched def _new_circuit(self): @@ -487,11 +494,13 @@ def append_block(self, context_block: ScheduleBlock): if len(context_block) > 0: self._context_stack[-1].append(context_block) - def call_subroutine(self, - subroutine: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], - name: Optional[str] = None, - value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, - **kw_params: ParameterValueType): + def call_subroutine( + self, + subroutine: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], + name: Optional[str] = None, + value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, + **kw_params: ParameterValueType, + ): """Call a schedule or circuit defined outside of the current scope. The ``subroutine`` is appended to the context schedule as a call instruction. @@ -526,9 +535,11 @@ def call_subroutine(self, if len(subroutine.blocks) > 0: empty_subroutine = False else: - raise exceptions.PulseError(f'Subroutine type {subroutine.__class__.__name__} is ' - 'not valid data format. Call QuantumCircuit, ' - 'Schedule, or ScheduleBlock.') + raise exceptions.PulseError( + f"Subroutine type {subroutine.__class__.__name__} is " + "not valid data format. Call QuantumCircuit, " + "Schedule, or ScheduleBlock." + ) if not empty_subroutine: param_value_map = dict() @@ -539,8 +550,9 @@ def call_subroutine(self, param_value_map[param_obj] = assigned_value else: raise exceptions.PulseError( - f'Parameter {param_name} is not defined in the target subroutine. ' - f'{", ".join(map(str, subroutine.parameters))} can be specified.') + f"Parameter {param_name} is not defined in the target subroutine. " + f'{", ".join(map(str, subroutine.parameters))} can be specified.' + ) if value_dict: param_value_map.update(value_dict) @@ -550,10 +562,7 @@ def call_subroutine(self, self.append_instruction(call_def) @_requires_backend - def call_gate(self, - gate: circuit.Gate, - qubits: Tuple[int, ...], - lazy: bool = True): + def call_gate(self, gate: circuit.Gate, qubits: Tuple[int, ...], lazy: bool = True): """Call the circuit ``gate`` in the pulse program. The qubits are assumed to be defined on physical qubits. @@ -590,13 +599,14 @@ def _call_gate(self, gate, qargs): self._lazy_circuit.append(gate, qargs=qargs) -def build(backend=None, - schedule: Optional[ScheduleBlock] = None, - name: Optional[str] = None, - default_alignment: Optional[Union[str, AlignmentKind]] = 'left', - default_transpiler_settings: Optional[Dict[str, Any]] = None, - default_circuit_scheduler_settings: Optional[Dict[str, Any]] = None - ) -> ContextManager[ScheduleBlock]: +def build( + backend=None, + schedule: Optional[ScheduleBlock] = None, + name: Optional[str] = None, + default_alignment: Optional[Union[str, AlignmentKind]] = "left", + default_transpiler_settings: Optional[Dict[str, Any]] = None, + default_circuit_scheduler_settings: Optional[Dict[str, Any]] = None, +) -> ContextManager[ScheduleBlock]: """Create a context manager for launching the imperative pulse builder DSL. To enter a building context and starting building a pulse program: @@ -642,11 +652,13 @@ def build(backend=None, name=name, default_alignment=default_alignment, default_transpiler_settings=default_transpiler_settings, - default_circuit_scheduler_settings=default_circuit_scheduler_settings) + default_circuit_scheduler_settings=default_circuit_scheduler_settings, + ) # Builder Utilities + def _active_builder() -> _PulseBuilder: """Get the active builder in the active context. @@ -661,9 +673,10 @@ def _active_builder() -> _PulseBuilder: return BUILDER_CONTEXTVAR.get() except LookupError as ex: raise exceptions.NoActiveBuilder( - 'A Pulse builder function was called outside of ' - 'a builder context. Try calling within a builder ' - 'context, eg., "with pulse.build() as schedule: ...".') from ex + "A Pulse builder function was called outside of " + "a builder context. Try calling within a builder " + 'context, eg., "with pulse.build() as schedule: ...".' + ) from ex def active_backend(): @@ -679,8 +692,8 @@ def active_backend(): builder = _active_builder().backend if builder is None: raise exceptions.BackendNotSet( - 'This function requires the active builder to ' - 'have a "backend" set.') + "This function requires the active builder to " 'have a "backend" set.' + ) return builder @@ -698,9 +711,11 @@ def append_schedule(schedule: Union[Schedule, ScheduleBlock]): elif isinstance(schedule, ScheduleBlock): _active_builder().append_block(schedule) else: - raise exceptions.PulseError(f'Input program {schedule.__class__.__name__} is not ' - 'acceptable program format. Input `Schedule` or ' - '`ScheduleBlock`.') + raise exceptions.PulseError( + f"Input program {schedule.__class__.__name__} is not " + "acceptable program format. Input `Schedule` or " + "`ScheduleBlock`." + ) def append_instruction(instruction: instructions.Instruction): @@ -796,8 +811,7 @@ def qubit_channels(qubit: int) -> Set[chans.Channel]: return set(active_backend().configuration().get_qubit_channels(qubit)) -def _qubits_to_channels(*channels_or_qubits: Union[int, chans.Channel] - ) -> Set[chans.Channel]: +def _qubits_to_channels(*channels_or_qubits: Union[int, chans.Channel]) -> Set[chans.Channel]: """Returns the unique channels of the input qubits.""" channels = set() for channel_or_qubit in channels_or_qubits: @@ -807,8 +821,8 @@ def _qubits_to_channels(*channels_or_qubits: Union[int, chans.Channel] channels.add(channel_or_qubit) else: raise exceptions.PulseError( - '{} is not a "Channel" or ' - 'qubit (integer).'.format(channel_or_qubit)) + '{} is not a "Channel" or ' "qubit (integer).".format(channel_or_qubit) + ) return channels @@ -859,6 +873,7 @@ def active_circuit_scheduler_settings() -> Dict[str, Any]: # pylint: disable=in # Contexts + @contextmanager def align_left() -> ContextManager[None]: """Left alignment pulse scheduling context. @@ -974,8 +989,7 @@ def align_sequential() -> AlignmentKind: @contextmanager -def align_equispaced(duration: Union[int, ParameterExpression] - ) -> AlignmentKind: +def align_equispaced(duration: Union[int, ParameterExpression]) -> AlignmentKind: """Equispaced alignment pulse scheduling context. Pulse instructions within this context are scheduled with the same interval spacing such that @@ -1026,9 +1040,9 @@ def align_equispaced(duration: Union[int, ParameterExpression] @contextmanager -def align_func(duration: Union[int, ParameterExpression], - func: Callable[[int], float] - ) -> AlignmentKind: +def align_func( + duration: Union[int, ParameterExpression], func: Callable[[int], float] +) -> AlignmentKind: """Callback defined alignment pulse scheduling context. Pulse instructions within this context are scheduled at the location specified by @@ -1101,7 +1115,7 @@ def general_transforms(alignment_context: AlignmentKind) -> ContextManager[None] PulseError: When input ``alignment_context`` is not ``AlignmentKind`` subclasses. """ if not isinstance(alignment_context, AlignmentKind): - raise exceptions.PulseError('Input alignment context is not `AlignmentKind` subclass.') + raise exceptions.PulseError("Input alignment context is not `AlignmentKind` subclass.") builder = _active_builder() builder.push_context(alignment_context) @@ -1121,6 +1135,7 @@ def inline() -> ContextManager[None]: .. warning:: This will cause all scheduling directives within this context to be ignored. """ + def _flatten(block): for inst in block.blocks: if isinstance(inst, ScheduleBlock): @@ -1151,13 +1166,16 @@ def pad(*chs: chans.Channel) -> ContextManager[None]: # pylint: disable=unused- Yields: None """ - warnings.warn('Context-wise padding is being deprecated. Requested padding is being ignored. ' - 'Now the pulse builder generate a program in `ScheduleBlock` representation. ' - 'The padding with delay as a blocker is no longer necessary for this program. ' - 'However, if you still want delays, you can convert the output program ' - 'into `Schedule` representation by calling ' - '`qiskit.pulse.transforms.target_qobj_transform`. Then, you can apply ' - '`qiskit.pulse.transforms.pad` to the converted schedule. ', DeprecationWarning) + warnings.warn( + "Context-wise padding is being deprecated. Requested padding is being ignored. " + "Now the pulse builder generate a program in `ScheduleBlock` representation. " + "The padding with delay as a blocker is no longer necessary for this program. " + "However, if you still want delays, you can convert the output program " + "into `Schedule` representation by calling " + "`qiskit.pulse.transforms.target_qobj_transform`. Then, you can apply " + "`qiskit.pulse.transforms.pad` to the converted schedule. ", + DeprecationWarning, + ) try: yield finally: @@ -1184,8 +1202,7 @@ def transpiler_settings(**settings) -> ContextManager[None]: """ builder = _active_builder() curr_transpiler_settings = builder.transpiler_settings - builder.transpiler_settings = collections.ChainMap( - settings, curr_transpiler_settings) + builder.transpiler_settings = collections.ChainMap(settings, curr_transpiler_settings) try: yield finally: @@ -1213,7 +1230,8 @@ def circuit_scheduler_settings(**settings) -> ContextManager[None]: builder = _active_builder() curr_circuit_scheduler_settings = builder.circuit_scheduler_settings builder.circuit_scheduler_settings = collections.ChainMap( - settings, curr_circuit_scheduler_settings) + settings, curr_circuit_scheduler_settings + ) try: yield finally: @@ -1221,9 +1239,7 @@ def circuit_scheduler_settings(**settings) -> ContextManager[None]: @contextmanager -def phase_offset(phase: float, - *channels: chans.PulseChannel - ) -> ContextManager[None]: +def phase_offset(phase: float, *channels: chans.PulseChannel) -> ContextManager[None]: """Shift the phase of input channels on entry into context and undo on exit. Examples: @@ -1259,10 +1275,9 @@ def phase_offset(phase: float, @contextmanager -def frequency_offset(frequency: float, - *channels: chans.PulseChannel, - compensate_phase: bool = False - ) -> ContextManager[None]: +def frequency_offset( + frequency: float, *channels: chans.PulseChannel, compensate_phase: bool = False +) -> ContextManager[None]: """Shift the frequency of inputs channels on entry into context and undo on exit. Examples: @@ -1414,8 +1429,7 @@ def control_channels(*qubits: Iterable[int]) -> List[chans.ControlChannel]: # Base Instructions -def delay(duration: int, - channel: chans.Channel, name: Optional[str] = None): +def delay(duration: int, channel: chans.Channel, name: Optional[str] = None): """Delay on a ``channel`` for a ``duration``. Examples: @@ -1437,8 +1451,9 @@ def delay(duration: int, append_instruction(instructions.Delay(duration, channel, name=name)) -def play(pulse: Union[library.Pulse, np.ndarray], - channel: chans.PulseChannel, name: Optional[str] = None): +def play( + pulse: Union[library.Pulse, np.ndarray], channel: chans.PulseChannel, name: Optional[str] = None +): """Play a ``pulse`` on a ``channel``. Examples: @@ -1463,11 +1478,12 @@ def play(pulse: Union[library.Pulse, np.ndarray], append_instruction(instructions.Play(pulse, channel, name=name)) -def acquire(duration: int, - qubit_or_channel: Union[int, chans.AcquireChannel], - register: StorageLocation, - **metadata: Union[configuration.Kernel, - configuration.Discriminator]): +def acquire( + duration: int, + qubit_or_channel: Union[int, chans.AcquireChannel], + register: StorageLocation, + **metadata: Union[configuration.Kernel, configuration.Discriminator], +): """Acquire for a ``duration`` on a ``channel`` and store the result in a ``register``. @@ -1505,18 +1521,20 @@ def acquire(duration: int, qubit_or_channel = chans.AcquireChannel(qubit_or_channel) if isinstance(register, chans.MemorySlot): - append_instruction(instructions.Acquire( - duration, qubit_or_channel, mem_slot=register, **metadata)) + append_instruction( + instructions.Acquire(duration, qubit_or_channel, mem_slot=register, **metadata) + ) elif isinstance(register, chans.RegisterSlot): - append_instruction(instructions.Acquire( - duration, qubit_or_channel, reg_slot=register, **metadata)) + append_instruction( + instructions.Acquire(duration, qubit_or_channel, reg_slot=register, **metadata) + ) else: raise exceptions.PulseError( - 'Register of type: "{}" is not supported'.format(type(register))) + 'Register of type: "{}" is not supported'.format(type(register)) + ) -def set_frequency(frequency: float, - channel: chans.PulseChannel, name: Optional[str] = None): +def set_frequency(frequency: float, channel: chans.PulseChannel, name: Optional[str] = None): """Set the ``frequency`` of a pulse ``channel``. Examples: @@ -1538,8 +1556,7 @@ def set_frequency(frequency: float, append_instruction(instructions.SetFrequency(frequency, channel, name=name)) -def shift_frequency(frequency: float, - channel: chans.PulseChannel, name: Optional[str] = None): +def shift_frequency(frequency: float, channel: chans.PulseChannel, name: Optional[str] = None): """Shift the ``frequency`` of a pulse ``channel``. Examples: @@ -1562,8 +1579,7 @@ def shift_frequency(frequency: float, append_instruction(instructions.ShiftFrequency(frequency, channel, name=name)) -def set_phase(phase: float, - channel: chans.PulseChannel, name: Optional[str] = None): +def set_phase(phase: float, channel: chans.PulseChannel, name: Optional[str] = None): """Set the ``phase`` of a pulse ``channel``. Examples: @@ -1588,8 +1604,7 @@ def set_phase(phase: float, append_instruction(instructions.SetPhase(phase, channel, name=name)) -def shift_phase(phase: float, - channel: chans.PulseChannel, name: Optional[str] = None): +def shift_phase(phase: float, channel: chans.PulseChannel, name: Optional[str] = None): """Shift the ``phase`` of a pulse ``channel``. Examples: @@ -1613,8 +1628,7 @@ def shift_phase(phase: float, append_instruction(instructions.ShiftPhase(phase, channel, name)) -def snapshot(label: str, - snapshot_type: str = 'statevector'): +def snapshot(label: str, snapshot_type: str = "statevector"): """Simulator snapshot. Examples: @@ -1630,8 +1644,7 @@ def snapshot(label: str, label: Label for snapshot. snapshot_type: Type of snapshot. """ - append_instruction( - instructions.Snapshot(label, snapshot_type=snapshot_type)) + append_instruction(instructions.Snapshot(label, snapshot_type=snapshot_type)) def call_schedule(schedule: Schedule): @@ -1657,8 +1670,11 @@ def call_schedule(schedule: Schedule): Args: Schedule to call. """ - warnings.warn('``call_schedule`` is being deprecated. ' - '``call`` function can take both a schedule and a circuit.', DeprecationWarning) + warnings.warn( + "``call_schedule`` is being deprecated. " + "``call`` function can take both a schedule and a circuit.", + DeprecationWarning, + ) call(schedule) @@ -1703,16 +1719,21 @@ def call_circuit(circ: circuit.QuantumCircuit): Args: Circuit to call. """ - warnings.warn('``call_circuit`` is being deprecated. ' - '``call`` function can take both a schedule and a circuit.', DeprecationWarning) + warnings.warn( + "``call_circuit`` is being deprecated. " + "``call`` function can take both a schedule and a circuit.", + DeprecationWarning, + ) call(circ) -def call(target: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], - name: Optional[str] = None, - value_dict: Optional[Dict[ParameterValueType, ParameterValueType]] = None, - **kw_params: ParameterValueType): +def call( + target: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], + name: Optional[str] = None, + value_dict: Optional[Dict[ParameterValueType, ParameterValueType]] = None, + **kw_params: ParameterValueType, +): """Call the ``target`` within the currently active builder context with arbitrary parameters which will be assigned to the target program. @@ -1787,7 +1808,8 @@ def call(target: Union[circuit.QuantumCircuit, Schedule, ScheduleBlock], """ if not isinstance(target, (circuit.QuantumCircuit, Schedule, ScheduleBlock)): raise exceptions.PulseError( - f'Target of type "{target.__class__.__name__}" is not supported.') + f'Target of type "{target.__class__.__name__}" is not supported.' + ) _active_builder().call_subroutine(target, name, value_dict, **kw_params) @@ -1909,7 +1931,7 @@ def measure(qubit: int): Returns: Callable: The wrapped ``func``. """ - func_name = getattr(func, '__name__', repr(func)) + func_name = getattr(func, "__name__", repr(func)) @functools.wraps(func) def wrapper(*args, **kwargs): @@ -1924,9 +1946,10 @@ def wrapper(*args, **kwargs): return wrapper -def measure(qubits: Union[List[int], int], - registers: Union[List[StorageLocation], StorageLocation] = None, - ) -> Union[List[StorageLocation], StorageLocation]: +def measure( + qubits: Union[List[int], int], + registers: Union[List[StorageLocation], StorageLocation] = None, +) -> Union[List[StorageLocation], StorageLocation]: """Measure a qubit within the currently active builder context. At the pulse level a measurement is composed of both a stimulus pulse and @@ -2000,7 +2023,8 @@ def measure(qubits: Union[List[int], int], qubits=qubits, inst_map=backend.defaults().instruction_schedule_map, meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}) + qubit_mem_slots={qubit: register.index for qubit, register in zip(qubits, registers)}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. @@ -2045,7 +2069,8 @@ def measure_all() -> List[chans.MemorySlot]: qubits=qubits, inst_map=backend.defaults().instruction_schedule_map, meas_map=backend.configuration().meas_map, - qubit_mem_slots={qubit: qubit for qubit in qubits}) + qubit_mem_slots={qubit: qubit for qubit in qubits}, + ) # note this is not a subroutine. # just a macro to automate combination of stimulus and acquisition. @@ -2054,8 +2079,7 @@ def measure_all() -> List[chans.MemorySlot]: return registers -def delay_qubits(duration: int, - *qubits: Union[int, Iterable[int]]): +def delay_qubits(duration: int, *qubits: Union[int, Iterable[int]]): r"""Insert delays on all of the :class:`channels.Channel`\s that correspond to the input ``qubits`` at the same time. @@ -2079,8 +2103,7 @@ def delay_qubits(duration: int, qubits: Physical qubits to delay on. Delays will be inserted based on the channels returned by :func:`pulse.qubit_channels`. """ - qubit_chans = set(itertools.chain.from_iterable( - qubit_channels(qubit) for qubit in qubits)) + qubit_chans = set(itertools.chain.from_iterable(qubit_channels(qubit) for qubit in qubits)) with align_left(): # pylint: disable=not-context-manager for chan in qubit_chans: delay(duration, chan) diff --git a/qiskit/pulse/channels.py b/qiskit/pulse/channels.py index 894d3737c4c1..8a07e679c5db 100644 --- a/qiskit/pulse/channels.py +++ b/qiskit/pulse/channels.py @@ -99,7 +99,7 @@ def _validate_index(self, index: Any) -> None: index = int(index) if not isinstance(index, (int, np.integer)) and index < 0: - raise PulseError('Channel index must be a nonnegative integer') + raise PulseError("Channel index must be a nonnegative integer") @property def parameters(self) -> Set: @@ -113,7 +113,7 @@ def is_parameterized(self) -> bool: return isinstance(self.index, ParameterExpression) @deprecated_functionality - def assign(self, parameter: Parameter, value: ParameterValueType) -> 'Channel': + def assign(self, parameter: Parameter, value: ParameterValueType) -> "Channel": """Return a new channel with the input Parameter assigned to value. Args: @@ -127,8 +127,9 @@ def assign(self, parameter: Parameter, value: ParameterValueType) -> 'Channel': PulseError: If the parameter is not present in the channel. """ if parameter not in self.parameters: - raise PulseError('Cannot bind parameters ({}) not present in the channel.' - ''.format(parameter)) + raise PulseError( + "Cannot bind parameters ({}) not present in the channel." "".format(parameter) + ) new_index = self.index.assign(parameter, value) if not new_index.parameters: @@ -140,12 +141,12 @@ def assign(self, parameter: Parameter, value: ParameterValueType) -> 'Channel': @property def name(self) -> str: """Return the shorthand alias for this channel, which is based on its type and index.""" - return '{}{}'.format(self.__class__.prefix, self._index) + return "{}{}".format(self.__class__.prefix, self._index) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, self._index) + return "{}({})".format(self.__class__.__name__, self._index) - def __eq__(self, other: 'Channel') -> bool: + def __eq__(self, other: "Channel") -> bool: """Return True iff self and other are equal, specifically, iff they have the same type and the same index. @@ -163,17 +164,20 @@ def __hash__(self): class PulseChannel(Channel, metaclass=ABCMeta): """Base class of transmit Channels. Pulses can be played on these channels.""" + pass class DriveChannel(PulseChannel): """Drive channels transmit signals to qubits which enact gate operations.""" - prefix = 'd' + + prefix = "d" class MeasureChannel(PulseChannel): """Measure channels transmit measurement stimulus pulses for readout.""" - prefix = 'm' + + prefix = "m" class ControlChannel(PulseChannel): @@ -181,17 +185,20 @@ class ControlChannel(PulseChannel): These are often associated with multi-qubit gate operations. They may not map trivially to a particular qubit index. """ - prefix = 'u' + + prefix = "u" class AcquireChannel(Channel): """Acquire channels are used to collect data.""" - prefix = 'a' + + prefix = "a" class SnapshotChannel(Channel): """Snapshot channels are used to specify instructions for simulators.""" - prefix = 's' + + prefix = "s" def __init__(self): """Create new snapshot channel.""" @@ -200,11 +207,13 @@ def __init__(self): class MemorySlot(Channel): """Memory slot channels represent classical memory storage.""" - prefix = 'm' + + prefix = "m" class RegisterSlot(Channel): """Classical resister slot channels represent classical registers (low-latency classical memory). """ - prefix = 'c' + + prefix = "c" diff --git a/qiskit/pulse/configuration.py b/qiskit/pulse/configuration.py index 670f25daf9b7..3a5e70ac8f7f 100644 --- a/qiskit/pulse/configuration.py +++ b/qiskit/pulse/configuration.py @@ -35,10 +35,11 @@ def __init__(self, name: Optional[str] = None, **params): self.params = params def __repr__(self): - return "{}({}{})".format(self.__class__.__name__, - "'" + self.name + "', " or "", - ', '.join("{}={}".format(str(k), str(v)) - for k, v in self.params.items())) + return "{}({}{})".format( + self.__class__.__name__, + "'" + self.name + "', " or "", + ", ".join("{}={}".format(str(k), str(v)) for k, v in self.params.items()), + ) class Discriminator: @@ -57,10 +58,11 @@ def __init__(self, name: Optional[str] = None, **params): self.params = params def __repr__(self): - return "{}({}{})".format(self.__class__.__name__, - "'" + self.name + "', " or "", - ', '.join("{}={}".format(str(k), str(v)) - for k, v in self.params.items())) + return "{}({}{})".format( + self.__class__.__name__, + "'" + self.name + "', " or "", + ", ".join("{}={}".format(str(k), str(v)) for k, v in self.params.items()), + ) class LoRange: @@ -106,9 +108,7 @@ def __eq__(self, other): Returns: bool: are self and other equal. """ - if (type(self) is type(other) and - self._ub == other._ub and - self._lb == other._lb): + if type(self) is type(other) and self._ub == other._ub and self._lb == other._lb: return True return False @@ -116,8 +116,11 @@ def __eq__(self, other): class LoConfig: """Pulse channel LO frequency container.""" - def __init__(self, channel_los: Optional[Dict[PulseChannel, float]] = None, - lo_ranges: Optional[Dict[PulseChannel, Union[LoRange, Tuple[int]]]] = None): + def __init__( + self, + channel_los: Optional[Dict[PulseChannel, float]] = None, + lo_ranges: Optional[Dict[PulseChannel, Union[LoRange, Tuple[int]]]] = None, + ): """Lo channel configuration data structure. Args: @@ -151,8 +154,7 @@ def add_lo(self, channel: Union[DriveChannel, MeasureChannel], freq: float): self.check_lo(channel, freq) self._m_lo_freq[channel] = freq else: - raise PulseError("Specified channel %s cannot be configured." % - channel.name) + raise PulseError("Specified channel %s cannot be configured." % channel.name) def add_lo_range(self, channel: DriveChannel, lo_range: Union[LoRange, Tuple[int]]): """Add lo range to configuration. @@ -179,8 +181,7 @@ def check_lo(self, channel: Union[DriveChannel, MeasureChannel], freq: float) -> if channel in lo_ranges: lo_range = lo_ranges[channel] if not lo_range.includes(freq): - raise PulseError("Specified LO freq %f is out of range %s" % - (freq, lo_range)) + raise PulseError("Specified LO freq %f is out of range %s" % (freq, lo_range)) def channel_lo(self, channel: Union[DriveChannel, MeasureChannel]) -> float: """Return channel lo. @@ -200,7 +201,7 @@ def channel_lo(self, channel: Union[DriveChannel, MeasureChannel]) -> float: if channel in self.meas_los: return self.meas_los[channel] - raise PulseError('Channel %s is not configured' % channel) + raise PulseError("Channel %s is not configured" % channel) @property def qubit_los(self) -> Dict: diff --git a/qiskit/pulse/exceptions.py b/qiskit/pulse/exceptions.py index 4c1abd310bdc..272f8ceb4f93 100644 --- a/qiskit/pulse/exceptions.py +++ b/qiskit/pulse/exceptions.py @@ -20,7 +20,7 @@ class PulseError(QiskitError): def __init__(self, *message): """Set the error message.""" super().__init__(*message) - self.message = ' '.join(message) + self.message = " ".join(message) def __str__(self): """Return the message.""" diff --git a/qiskit/pulse/filters.py b/qiskit/pulse/filters.py index ff5174492fe0..f2b341b4540a 100644 --- a/qiskit/pulse/filters.py +++ b/qiskit/pulse/filters.py @@ -22,10 +22,9 @@ from qiskit.pulse.schedule import Interval -def filter_instructions(sched: Schedule, - filters: List[Callable], - negate: bool = False, - recurse_subroutines: bool = True) -> Schedule: +def filter_instructions( + sched: Schedule, filters: List[Callable], negate: bool = False, recurse_subroutines: bool = True +) -> Schedule: """A filtering function that takes a schedule and returns a schedule consisting of filtered instructions. @@ -58,10 +57,12 @@ def filter_instructions(sched: Schedule, return Schedule(*time_inst_tuples[valid_insts], name=sched.name, metadata=sched.metadata) -def composite_filter(channels: Optional[Union[Iterable[Channel], Channel]] = None, - instruction_types: Optional[Union[Iterable[abc.ABCMeta], abc.ABCMeta]] = None, - time_ranges: Optional[Iterable[Tuple[int, int]]] = None, - intervals: Optional[Iterable[Interval]] = None) -> List[Callable]: +def composite_filter( + channels: Optional[Union[Iterable[Channel], Channel]] = None, + instruction_types: Optional[Union[Iterable[abc.ABCMeta], abc.ABCMeta]] = None, + time_ranges: Optional[Iterable[Tuple[int, int]]] = None, + intervals: Optional[Iterable[Interval]] = None, +) -> List[Callable]: """A helper function to generate a list of filter functions based on typical elements to be filtered. @@ -112,6 +113,7 @@ def channel_filter(time_inst) -> bool: If instruction matches with condition. """ return any(chan in channels for chan in time_inst[1].channels) + return channel_filter diff --git a/qiskit/pulse/instruction_schedule_map.py b/qiskit/pulse/instruction_schedule_map.py index f1043c08d449..781e8ff1d2b4 100644 --- a/qiskit/pulse/instruction_schedule_map.py +++ b/qiskit/pulse/instruction_schedule_map.py @@ -36,11 +36,13 @@ from qiskit.pulse.exceptions import PulseError from qiskit.pulse.schedule import Schedule, ScheduleBlock, ParameterizedSchedule -Generator = NamedTuple('Generator', [('function', Union[Callable, Schedule, ScheduleBlock]), - ('signature', inspect.Signature)]) +Generator = NamedTuple( + "Generator", + [("function", Union[Callable, Schedule, ScheduleBlock]), ("signature", inspect.Signature)], +) -class InstructionScheduleMap(): +class InstructionScheduleMap: """Mapping from :py:class:`~qiskit.circuit.QuantumCircuit` :py:class:`qiskit.circuit.Instruction` names and qubits to :py:class:`~qiskit.pulse.Schedule` s. In particular, the mapping is formatted as type:: @@ -73,9 +75,9 @@ def instructions(self) -> List[str]: """ return list(self._map.keys()) - def qubits_with_instruction(self, - instruction: Union[str, Instruction]) -> List[Union[int, - Tuple[int]]]: + def qubits_with_instruction( + self, instruction: Union[str, Instruction] + ) -> List[Union[int, Tuple[int]]]: """Return a list of the qubits for which the given instruction is defined. Single qubit instructions return a flat list, and multiqubit instructions return a list of ordered tuples. @@ -93,8 +95,10 @@ def qubits_with_instruction(self, instruction = _get_instruction_string(instruction) if instruction not in self._map: return [] - return [qubits[0] if len(qubits) == 1 else qubits - for qubits in sorted(self._map[instruction].keys())] + return [ + qubits[0] if len(qubits) == 1 else qubits + for qubits in sorted(self._map[instruction].keys()) + ] def qubit_instructions(self, qubits: Union[int, Iterable[int]]) -> List[str]: """Return a list of the instruction names that are defined by the backend for the given @@ -124,12 +128,11 @@ def has(self, instruction: Union[str, Instruction], qubits: Union[int, Iterable[ True iff the instruction is defined. """ instruction = _get_instruction_string(instruction) - return instruction in self._map and \ - _to_tuple(qubits) in self._map[instruction] + return instruction in self._map and _to_tuple(qubits) in self._map[instruction] - def assert_has(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]]) -> None: + def assert_has( + self, instruction: Union[str, Instruction], qubits: Union[int, Iterable[int]] + ) -> None: """Error if the given instruction is not defined. Args: @@ -142,19 +145,23 @@ def assert_has(self, instruction = _get_instruction_string(instruction) if not self.has(instruction, _to_tuple(qubits)): if instruction in self._map: - raise PulseError("Operation '{inst}' exists, but is only defined for qubits " - "{qubits}.".format( - inst=instruction, - qubits=self.qubits_with_instruction(instruction))) - raise PulseError("Operation '{inst}' is not defined for this " - "system.".format(inst=instruction)) - - def get(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]], - *params: Union[int, float, complex, ParameterExpression], - **kwparams: Union[int, float, complex, ParameterExpression] - ) -> Union[Schedule, ScheduleBlock]: + raise PulseError( + "Operation '{inst}' exists, but is only defined for qubits " + "{qubits}.".format( + inst=instruction, qubits=self.qubits_with_instruction(instruction) + ) + ) + raise PulseError( + "Operation '{inst}' is not defined for this " "system.".format(inst=instruction) + ) + + def get( + self, + instruction: Union[str, Instruction], + qubits: Union[int, Iterable[int]], + *params: Union[int, float, complex, ParameterExpression], + **kwparams: Union[int, float, complex, ParameterExpression], + ) -> Union[Schedule, ScheduleBlock]: """Return the defined :py:class:`~qiskit.pulse.Schedule` or :py:class:`~qiskit.pulse.ScheduleBlock` for the given instruction on the given qubits. @@ -176,8 +183,10 @@ def get(self, self.assert_has(instruction, qubits) generator = self._map[instruction][_to_tuple(qubits)] - _error_message = f'*params={params}, **kwparams={kwparams} do not match with ' \ - f'the schedule generator signature {generator.signature}.' + _error_message = ( + f"*params={params}, **kwparams={kwparams} do not match with " + f"the schedule generator signature {generator.signature}." + ) function = generator.function if callable(function): @@ -210,11 +219,13 @@ def get(self, else: return function - def add(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]], - schedule: Union[Schedule, ScheduleBlock, Callable[..., Union[Schedule, ScheduleBlock]]], - arguments: Optional[List[str]] = None) -> None: + def add( + self, + instruction: Union[str, Instruction], + qubits: Union[int, Iterable[int]], + schedule: Union[Schedule, ScheduleBlock, Callable[..., Union[Schedule, ScheduleBlock]]], + arguments: Optional[List[str]] = None, + ) -> None: """Add a new known instruction for the given qubits and its mapping to a pulse schedule. Args: @@ -241,8 +252,10 @@ def add(self, ordered_names = sorted(list(set(par.name for par in schedule.parameters))) if arguments: if set(arguments) != set(ordered_names): - raise PulseError('Arguments does not match with schedule parameters. ' - f'{set(arguments)} != {schedule.parameters}.') + raise PulseError( + "Arguments does not match with schedule parameters. " + f"{set(arguments)} != {schedule.parameters}." + ) ordered_names = arguments parameters = list() @@ -250,42 +263,50 @@ def add(self, param_signature = inspect.Parameter( name=argname, annotation=ParameterValueType, - kind=inspect.Parameter.POSITIONAL_OR_KEYWORD + kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, ) parameters.append(param_signature) signature = inspect.Signature(parameters=parameters, return_annotation=type(schedule)) elif isinstance(schedule, ParameterizedSchedule): # TODO remove this - warnings.warn('ParameterizedSchedule has been deprecated. ' - 'Define Schedule with Parameter objects.', DeprecationWarning) + warnings.warn( + "ParameterizedSchedule has been deprecated. " + "Define Schedule with Parameter objects.", + DeprecationWarning, + ) parameters = list() for argname in schedule.parameters: param_signature = inspect.Parameter( name=argname, annotation=ParameterValueType, - kind=inspect.Parameter.POSITIONAL_OR_KEYWORD + kind=inspect.Parameter.POSITIONAL_OR_KEYWORD, ) parameters.append(param_signature) signature = inspect.Signature(parameters=parameters, return_annotation=Schedule) elif callable(schedule): if arguments: - warnings.warn('Arguments are overridden by the callback function signature. ' - 'Input `arguments` are ignored.', UserWarning) + warnings.warn( + "Arguments are overridden by the callback function signature. " + "Input `arguments` are ignored.", + UserWarning, + ) signature = inspect.signature(schedule) else: - raise PulseError('Supplied schedule must be one of the Schedule, ScheduleBlock or a ' - 'callable that outputs a schedule.') + raise PulseError( + "Supplied schedule must be one of the Schedule, ScheduleBlock or a " + "callable that outputs a schedule." + ) self._map[instruction][qubits] = Generator(schedule, signature) self._qubit_instructions[qubits].add(instruction) - def remove(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]]) -> None: + def remove( + self, instruction: Union[str, Instruction], qubits: Union[int, Iterable[int]] + ) -> None: """Remove the given instruction from the listing of instructions defined in self. Args: @@ -302,12 +323,13 @@ def remove(self, if not self._qubit_instructions[qubits]: self._qubit_instructions.pop(qubits) - def pop(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]], - *params: Union[int, float, complex, ParameterExpression], - **kwparams: Union[int, float, complex, ParameterExpression] - ) -> Union[Schedule, ScheduleBlock]: + def pop( + self, + instruction: Union[str, Instruction], + qubits: Union[int, Iterable[int]], + *params: Union[int, float, complex, ParameterExpression], + **kwparams: Union[int, float, complex, ParameterExpression], + ) -> Union[Schedule, ScheduleBlock]: """Remove and return the defined schedule for the given instruction on the given qubits. @@ -325,10 +347,9 @@ def pop(self, self.remove(instruction, qubits) return schedule - def get_parameters(self, - instruction: Union[str, Instruction], - qubits: Union[int, Iterable[int]] - ) -> Tuple[str]: + def get_parameters( + self, instruction: Union[str, Instruction], qubits: Union[int, Iterable[int]] + ) -> Tuple[str]: """Return the list of parameters taken by the given instruction on the given qubits. Args: @@ -353,8 +374,7 @@ def __str__(self): else: multi_q_insts += " {qubits}: {insts}\n".format(qubits=qubits, insts=insts) instructions = single_q_insts + multi_q_insts - return ("<{name}({insts})>" - "".format(name=self.__class__.__name__, insts=instructions)) + return "<{name}({insts})>" "".format(name=self.__class__.__name__, insts=instructions) def _to_tuple(values: Union[int, Iterable[int]]) -> Tuple[int, ...]: @@ -379,5 +399,6 @@ def _get_instruction_string(inst: Union[str, Instruction]): try: return inst.name except AttributeError as ex: - raise PulseError('Input "inst" has no attribute "name".' - 'This should be a circuit "Instruction".') from ex + raise PulseError( + 'Input "inst" has no attribute "name".' 'This should be a circuit "Instruction".' + ) from ex diff --git a/qiskit/pulse/instructions/acquire.py b/qiskit/pulse/instructions/acquire.py index f89658c70cd5..f00e94172ff0 100644 --- a/qiskit/pulse/instructions/acquire.py +++ b/qiskit/pulse/instructions/acquire.py @@ -39,14 +39,16 @@ class Acquire(Instruction): * the discriminator to classify kerneled IQ points. """ - def __init__(self, - duration: Union[int, ParameterExpression], - channel: AcquireChannel, - mem_slot: Optional[MemorySlot] = None, - reg_slot: Optional[RegisterSlot] = None, - kernel: Optional[Kernel] = None, - discriminator: Optional[Discriminator] = None, - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + channel: AcquireChannel, + mem_slot: Optional[MemorySlot] = None, + reg_slot: Optional[RegisterSlot] = None, + kernel: Optional[Kernel] = None, + discriminator: Optional[Discriminator] = None, + name: Optional[str] = None, + ): """Create a new Acquire instruction. Args: @@ -65,11 +67,13 @@ def __init__(self, does not equal the number of channels. """ if isinstance(channel, list) or isinstance(mem_slot, list) or isinstance(reg_slot, list): - raise PulseError("The Acquire instruction takes only one AcquireChannel and one " - "classical memory destination for the measurement result.") + raise PulseError( + "The Acquire instruction takes only one AcquireChannel and one " + "classical memory destination for the measurement result." + ) if not (mem_slot or reg_slot): - raise PulseError('Neither MemorySlots nor RegisterSlots were supplied.') + raise PulseError("Neither MemorySlots nor RegisterSlots were supplied.") self._kernel = kernel self._discriminator = discriminator @@ -130,8 +134,9 @@ def __repr__(self) -> str: return "{}({}{}{}{}{}{})".format( self.__class__.__name__, self.duration, - ', ' + str(self.channel), - ', ' + str(self.mem_slot) if self.mem_slot else '', - ', ' + str(self.reg_slot) if self.reg_slot else '', - ', ' + str(self.kernel) if self.kernel else '', - ', ' + str(self.discriminator) if self.discriminator else '') + ", " + str(self.channel), + ", " + str(self.mem_slot) if self.mem_slot else "", + ", " + str(self.reg_slot) if self.reg_slot else "", + ", " + str(self.kernel) if self.kernel else "", + ", " + str(self.discriminator) if self.discriminator else "", + ) diff --git a/qiskit/pulse/instructions/call.py b/qiskit/pulse/instructions/call.py index c993b8b48cbd..4ddc28f8f393 100644 --- a/qiskit/pulse/instructions/call.py +++ b/qiskit/pulse/instructions/call.py @@ -27,12 +27,16 @@ class Call(instruction.Instruction): The ``Call`` instruction represents the calling of a referenced subroutine (schedule). It enables code reuse both within the pulse representation and hardware (if supported). """ - # Prefix to use for auto naming. - prefix = 'call' - def __init__(self, subroutine, - value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, - name: Optional[str] = None): + # Prefix to use for auto naming. + prefix = "call" + + def __init__( + self, + subroutine, + value_dict: Optional[Dict[ParameterExpression, ParameterValueType]] = None, + name: Optional[str] = None, + ): """Define new subroutine. .. note:: Inline subroutine is mutable. This requires special care for modification. @@ -49,7 +53,7 @@ def __init__(self, subroutine, from qiskit.pulse.schedule import ScheduleBlock, Schedule if not isinstance(subroutine, (ScheduleBlock, Schedule)): - raise PulseError(f'Subroutine type {subroutine.__class__.__name__} cannot be called.') + raise PulseError(f"Subroutine type {subroutine.__class__.__name__} cannot be called.") value_dict = value_dict or dict() @@ -58,8 +62,7 @@ def __init__(self, subroutine, if subroutine.is_parameterized(): self._arguments = {par: value_dict.get(par, par) for par in subroutine.parameters} assigned_subroutine = subroutine.assign_parameters( - value_dict=self.arguments, - inplace=False + value_dict=self.arguments, inplace=False ) else: self._arguments = dict() @@ -68,7 +71,7 @@ def __init__(self, subroutine, # create cache data of parameter-assigned subroutine self._assigned_cache = tuple((self._get_arg_hash(), assigned_subroutine)) - super().__init__(operands=(subroutine, ), name=name or f"{self.prefix}_{subroutine.name}") + super().__init__(operands=(subroutine,), name=name or f"{self.prefix}_{subroutine.name}") @property def duration(self) -> Union[int, ParameterExpression]: @@ -103,10 +106,7 @@ def assigned_subroutine(self): program (Union[Schedule, ScheduleBlock]): Attached program. """ if self._get_arg_hash() != self._assigned_cache[0]: - subroutine = self.subroutine.assign_parameters( - value_dict=self.arguments, - inplace=False - ) + subroutine = self.subroutine.assign_parameters(value_dict=self.arguments, inplace=False) # update cache data self._assigned_cache = tuple((self._get_arg_hash(), subroutine)) else: @@ -114,8 +114,7 @@ def assigned_subroutine(self): return subroutine - def _initialize_parameter_table(self, - operands: Tuple[Any]): + def _initialize_parameter_table(self, operands: Tuple[Any]): """A helper method to initialize parameter table. The behavior of the parameter table of the ``Call`` instruction is slightly different from @@ -137,9 +136,9 @@ def _initialize_parameter_table(self, self._parameter_table[value] = value @deprecated_functionality - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'Call': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "Call": """Store parameters which will be later assigned to the subroutine. Parameter values are not immediately assigned. The subroutine with parameters @@ -196,10 +195,12 @@ def arguments(self, new_arguments: Dict[ParameterExpression, ParameterValueType] """ # validation if new_arguments.keys() != self._arguments.keys(): - new_arg_names = ', '.join(map(repr, new_arguments.keys())) - old_arg_names = ', '.join(map(repr, self.arguments.keys())) - raise PulseError('Key mismatch between new arguments and existing arguments. ' - f'{new_arg_names} != {old_arg_names}') + new_arg_names = ", ".join(map(repr, new_arguments.keys())) + old_arg_names = ", ".join(map(repr, self.arguments.keys())) + raise PulseError( + "Key mismatch between new arguments and existing arguments. " + f"{new_arg_names} != {old_arg_names}" + ) self._arguments = new_arguments @@ -207,7 +208,7 @@ def _get_arg_hash(self): """A helper function to generate hash of parameters.""" return hash(tuple(self.arguments.items())) - def __eq__(self, other: 'Instruction') -> bool: + def __eq__(self, other: "Instruction") -> bool: """Check if this instruction is equal to the `other` instruction. Instructions are equal if they share the same type, operands, and channels. diff --git a/qiskit/pulse/instructions/delay.py b/qiskit/pulse/instructions/delay.py index 8340e8e017d3..f045a4880ff3 100644 --- a/qiskit/pulse/instructions/delay.py +++ b/qiskit/pulse/instructions/delay.py @@ -34,9 +34,12 @@ class Delay(Instruction): The ``channel`` will output no signal from time=0 up until time=10. """ - def __init__(self, duration: Union[int, ParameterExpression], - channel: Channel, - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + channel: Channel, + name: Optional[str] = None, + ): """Create a new delay instruction. No other instruction may be scheduled within a ``Delay``. @@ -58,7 +61,7 @@ def channel(self) -> Channel: @property def channels(self) -> Tuple[Channel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> Union[int, ParameterExpression]: diff --git a/qiskit/pulse/instructions/directives.py b/qiskit/pulse/instructions/directives.py index 78c11f910115..d09b69823332 100644 --- a/qiskit/pulse/instructions/directives.py +++ b/qiskit/pulse/instructions/directives.py @@ -34,9 +34,7 @@ def duration(self) -> int: class RelativeBarrier(Directive): """Pulse ``RelativeBarrier`` directive.""" - def __init__(self, - *channels: chans.Channel, - name: Optional[str] = None): + def __init__(self, *channels: chans.Channel, name: Optional[str] = None): """Create a relative barrier directive. The barrier directive blocks instructions within the same schedule @@ -56,5 +54,4 @@ def channels(self) -> Tuple[chans.Channel]: def __eq__(self, other): """Verify two barriers are equivalent.""" - return (isinstance(other, type(self)) and - set(self.channels) == set(other.channels)) + return isinstance(other, type(self)) and set(self.channels) == set(other.channels) diff --git a/qiskit/pulse/instructions/frequency.py b/qiskit/pulse/instructions/frequency.py index 0a0146dd8873..09233080b593 100644 --- a/qiskit/pulse/instructions/frequency.py +++ b/qiskit/pulse/instructions/frequency.py @@ -34,9 +34,12 @@ class SetFrequency(Instruction): The duration of SetFrequency is 0. """ - def __init__(self, frequency: Union[float, ParameterExpression], - channel: PulseChannel, - name: Optional[str] = None): + def __init__( + self, + frequency: Union[float, ParameterExpression], + channel: PulseChannel, + name: Optional[str] = None, + ): """Creates a new set channel frequency instruction. Args: @@ -63,7 +66,7 @@ def channel(self) -> PulseChannel: @property def channels(self) -> Tuple[PulseChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> int: @@ -78,10 +81,12 @@ def is_parameterized(self) -> bool: class ShiftFrequency(Instruction): """Shift the channel frequency away from the current frequency.""" - def __init__(self, - frequency: Union[float, ParameterExpression], - channel: PulseChannel, - name: Optional[str] = None): + def __init__( + self, + frequency: Union[float, ParameterExpression], + channel: PulseChannel, + name: Optional[str] = None, + ): """Creates a new shift frequency instruction. Args: @@ -108,7 +113,7 @@ def channel(self) -> PulseChannel: @property def channels(self) -> Tuple[PulseChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> int: diff --git a/qiskit/pulse/instructions/instruction.py b/qiskit/pulse/instructions/instruction.py index f80d85ed9985..3b547e7467dd 100644 --- a/qiskit/pulse/instructions/instruction.py +++ b/qiskit/pulse/instructions/instruction.py @@ -40,11 +40,13 @@ class Instruction(ABC): channels. """ - def __init__(self, - operands: Tuple, - duration: int = None, - channels: Tuple[Channel] = None, - name: Optional[str] = None): + def __init__( + self, + operands: Tuple, + duration: int = None, + channels: Tuple[Channel] = None, + name: Optional[str] = None, + ): """Instruction initializer. Args: @@ -59,16 +61,20 @@ def __init__(self, type :class:`Channel`. """ if duration is not None: - warnings.warn('Specifying duration in the constructor is deprecated. ' - 'Now duration is an abstract property rather than class variable. ' - 'All subclasses should implement ``duration`` accordingly. ' - 'See Qiskit-Terra #5679 for more information.', - DeprecationWarning) + warnings.warn( + "Specifying duration in the constructor is deprecated. " + "Now duration is an abstract property rather than class variable. " + "All subclasses should implement ``duration`` accordingly. " + "See Qiskit-Terra #5679 for more information.", + DeprecationWarning, + ) if channels is not None: - warnings.warn('Specifying ``channels`` in the constructor is deprecated. ' - 'All channels should be stored in ``operands``.', - DeprecationWarning) + warnings.warn( + "Specifying ``channels`` in the constructor is deprecated. " + "All channels should be stored in ``operands``.", + DeprecationWarning, + ) self._operands = operands self._name = name @@ -117,12 +123,12 @@ def duration(self) -> int: raise NotImplementedError @property - def _children(self) -> Tuple['Instruction']: + def _children(self) -> Tuple["Instruction"]: """Instruction has no child nodes.""" return () @property - def instructions(self) -> Tuple[Tuple[int, 'Instruction']]: + def instructions(self) -> Tuple[Tuple[int, "Instruction"]]: """Iterable for getting instructions from Schedule tree.""" return tuple(self._instructions()) @@ -134,10 +140,7 @@ def ch_duration(self, *channels: List[Channel]) -> int: """ return self.ch_stop_time(*channels) - def ch_start_time( - self, - *channels: List[Channel] - ) -> int: + def ch_start_time(self, *channels: List[Channel]) -> int: # pylint: disable=unused-argument """Return minimum start time for supplied channels. @@ -156,7 +159,7 @@ def ch_stop_time(self, *channels: List[Channel]) -> int: return self.duration return 0 - def _instructions(self, time: int = 0) -> Iterable[Tuple[int, 'Instruction']]: + def _instructions(self, time: int = 0) -> Iterable[Tuple[int, "Instruction"]]: """Iterable for flattening Schedule tree. Args: @@ -168,17 +171,18 @@ def _instructions(self, time: int = 0) -> Iterable[Tuple[int, 'Instruction']]: """ yield (time, self) - def flatten(self) -> 'Instruction': + def flatten(self) -> "Instruction": """Return itself as already single instruction.""" - warnings.warn('`This method is being deprecated. Please use ' - '`qiskit.pulse.transforms.flatten` function with this schedule.', - DeprecationWarning) + warnings.warn( + "`This method is being deprecated. Please use " + "`qiskit.pulse.transforms.flatten` function with this schedule.", + DeprecationWarning, + ) return self - def shift(self, - time: int, name: Optional[str] = None): + def shift(self, time: int, name: Optional[str] = None): """Return a new schedule shifted forward by `time`. Args: @@ -194,8 +198,7 @@ def shift(self, name = self.name return Schedule((time, self), name=name) - def insert(self, start_time: int, schedule, - name: Optional[str] = None): + def insert(self, start_time: int, schedule, name: Optional[str] = None): """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted within ``self`` at ``start_time``. @@ -213,8 +216,7 @@ def insert(self, start_time: int, schedule, name = self.name return Schedule(self, (start_time, schedule), name=name) - def append(self, schedule, - name: Optional[str] = None): + def append(self, schedule, name: Optional[str] = None): """Return a new :class:`~qiskit.pulse.Schedule` with ``schedule`` inserted at the maximum time over all channels shared between ``self`` and ``schedule``. @@ -234,7 +236,7 @@ def parameters(self) -> Set: """Parameters which determine the instruction behavior.""" parameters = set() for op in self.operands: - if hasattr(op, 'parameters'): + if hasattr(op, "parameters"): for op_param in op.parameters: parameters.add(op_param) return parameters @@ -243,8 +245,7 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return any(chan.is_parameterized() for chan in self.channels) - def _initialize_parameter_table(self, - operands: Tuple[Any]): + def _initialize_parameter_table(self, operands: Tuple[Any]): """A helper method to initialize parameter table. Args: @@ -259,9 +260,9 @@ def _initialize_parameter_table(self, self._parameter_table[param].append(idx) @deprecated_functionality - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'Instruction': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "Instruction": """Modify and return self with parameters assigned according to the input. Args: @@ -297,13 +298,21 @@ def assign_parameters(self, return self - def draw(self, dt: float = 1, style=None, - filename: Optional[str] = None, interp_method: Optional[Callable] = None, - scale: float = 1, plot_all: bool = False, - plot_range: Optional[Tuple[float]] = None, - interactive: bool = False, table: bool = True, - label: bool = False, framechange: bool = True, - channels: Optional[List[Channel]] = None): + def draw( + self, + dt: float = 1, + style=None, + filename: Optional[str] = None, + interp_method: Optional[Callable] = None, + scale: float = 1, + plot_all: bool = False, + plot_range: Optional[Tuple[float]] = None, + interactive: bool = False, + table: bool = True, + label: bool = False, + framechange: bool = True, + channels: Optional[List[Channel]] = None, + ): """Plot the instruction. Args: @@ -327,15 +336,23 @@ def draw(self, dt: float = 1, style=None, # pylint: disable=cyclic-import from qiskit import visualization - return visualization.pulse_drawer(self, dt=dt, style=style, - filename=filename, interp_method=interp_method, - scale=scale, - plot_all=plot_all, plot_range=plot_range, - interactive=interactive, table=table, - label=label, framechange=framechange, - channels=channels) - - def __eq__(self, other: 'Instruction') -> bool: + return visualization.pulse_drawer( + self, + dt=dt, + style=style, + filename=filename, + interp_method=interp_method, + scale=scale, + plot_all=plot_all, + plot_range=plot_range, + interactive=interactive, + table=table, + label=label, + framechange=framechange, + channels=channels, + ) + + def __eq__(self, other: "Instruction") -> bool: """Check if this Instruction is equal to the `other` instruction. Equality is determined by the instruction sharing the same operands and channels. @@ -378,6 +395,7 @@ def __lshift__(self, time: int): return self.shift(time) def __repr__(self) -> str: - operands = ', '.join(str(op) for op in self.operands) - return "{}({}{})".format(self.__class__.__name__, operands, - ", name='{}'".format(self.name) if self.name else "") + operands = ", ".join(str(op) for op in self.operands) + return "{}({}{})".format( + self.__class__.__name__, operands, ", name='{}'".format(self.name) if self.name else "" + ) diff --git a/qiskit/pulse/instructions/phase.py b/qiskit/pulse/instructions/phase.py index ad8482c55452..b9067486d923 100644 --- a/qiskit/pulse/instructions/phase.py +++ b/qiskit/pulse/instructions/phase.py @@ -39,9 +39,12 @@ class ShiftPhase(Instruction): by using a ShiftPhase to update the frame tracking the qubit state. """ - def __init__(self, phase: Union[complex, ParameterExpression], - channel: PulseChannel, - name: Optional[str] = None): + def __init__( + self, + phase: Union[complex, ParameterExpression], + channel: PulseChannel, + name: Optional[str] = None, + ): """Instantiate a shift phase instruction, increasing the output signal phase on ``channel`` by ``phase`` [radians]. @@ -67,7 +70,7 @@ def channel(self) -> PulseChannel: @property def channels(self) -> Tuple[PulseChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> int: @@ -92,10 +95,12 @@ class SetPhase(Instruction): The ``SetPhase`` instruction sets :math:`\phi` to the instruction's ``phase`` operand. """ - def __init__(self, - phase: Union[complex, ParameterExpression], - channel: PulseChannel, - name: Optional[str] = None): + def __init__( + self, + phase: Union[complex, ParameterExpression], + channel: PulseChannel, + name: Optional[str] = None, + ): """Instantiate a set phase instruction, setting the output signal phase on ``channel`` to ``phase`` [radians]. @@ -121,7 +126,7 @@ def channel(self) -> PulseChannel: @property def channels(self) -> Tuple[PulseChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> int: diff --git a/qiskit/pulse/instructions/play.py b/qiskit/pulse/instructions/play.py index fd11780925bb..bf29e068290a 100644 --- a/qiskit/pulse/instructions/play.py +++ b/qiskit/pulse/instructions/play.py @@ -32,9 +32,7 @@ class Play(Instruction): cycle time, dt, of the backend. """ - def __init__(self, pulse: Pulse, - channel: PulseChannel, - name: Optional[str] = None): + def __init__(self, pulse: Pulse, channel: PulseChannel, name: Optional[str] = None): """Create a new pulse instruction. Args: @@ -49,8 +47,9 @@ def __init__(self, pulse: Pulse, if not isinstance(pulse, Pulse): raise PulseError("The `pulse` argument to `Play` must be of type `library.Pulse`.") if not isinstance(channel, PulseChannel): - raise PulseError("The `channel` argument to `Play` must be of type " - "`channels.PulseChannel`.") + raise PulseError( + "The `channel` argument to `Play` must be of type " "`channels.PulseChannel`." + ) if name is None: name = pulse.name super().__init__(operands=(pulse, channel), name=name) @@ -70,15 +69,14 @@ def channel(self) -> PulseChannel: @property def channels(self) -> Tuple[PulseChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> Union[int, ParameterExpression]: """Duration of this instruction.""" return self.pulse.duration - def _initialize_parameter_table(self, - operands: Tuple[Any]): + def _initialize_parameter_table(self, operands: Tuple[Any]): """A helper method to initialize parameter table. Args: @@ -108,9 +106,9 @@ def parameters(self) -> Set: return parameters @deprecated_functionality - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'Play': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "Play": super().assign_parameters(value_dict) pulse = self.pulse.assign_parameters(value_dict) self._operands = (pulse, self.channel) diff --git a/qiskit/pulse/instructions/snapshot.py b/qiskit/pulse/instructions/snapshot.py index 91d0aa349e6d..a4b09f7e2155 100644 --- a/qiskit/pulse/instructions/snapshot.py +++ b/qiskit/pulse/instructions/snapshot.py @@ -23,7 +23,7 @@ class Snapshot(Instruction): """An instruction targeted for simulators, to capture a moment in the simulation.""" - def __init__(self, label: str, snapshot_type: str = 'statevector', name: Optional[str] = None): + def __init__(self, label: str, snapshot_type: str = "statevector", name: Optional[str] = None): """Create new snapshot. Args: @@ -37,7 +37,7 @@ def __init__(self, label: str, snapshot_type: str = 'statevector', name: Optiona PulseError: If snapshot label is invalid. """ if not isinstance(label, str): - raise PulseError('Snapshot label must be a string.') + raise PulseError("Snapshot label must be a string.") self._channel = SnapshotChannel() if name is None: name = label @@ -63,7 +63,7 @@ def channel(self) -> SnapshotChannel: @property def channels(self) -> Tuple[SnapshotChannel]: """Returns the channels that this schedule uses.""" - return (self.channel, ) + return (self.channel,) @property def duration(self) -> int: diff --git a/qiskit/pulse/interfaces.py b/qiskit/pulse/interfaces.py index 17bb74780b91..75de524ab3e4 100644 --- a/qiskit/pulse/interfaces.py +++ b/qiskit/pulse/interfaces.py @@ -32,18 +32,24 @@ class ScheduleComponent(metaclass=ABCMeta): @abstractmethod def name(self) -> str: """Name of ScheduleComponent.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @property @abstractmethod def channels(self) -> List[Channel]: """Return channels used by schedule.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @@ -51,9 +57,12 @@ def channels(self) -> List[Channel]: @abstractmethod def duration(self) -> int: """Duration of this schedule component.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @@ -61,9 +70,12 @@ def duration(self) -> int: @abstractmethod def start_time(self) -> int: """Starting time of this schedule component.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @@ -71,78 +83,104 @@ def start_time(self) -> int: @abstractmethod def stop_time(self) -> int: """Stopping time of this schedule component.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod def ch_duration(self, *channels: List[Channel]) -> int: """Duration of the `channels` in schedule component.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod def ch_start_time(self, *channels: List[Channel]) -> int: - """Starting time of the `channels` in schedule component. """ - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + """Starting time of the `channels` in schedule component.""" + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod def ch_stop_time(self, *channels: List[Channel]) -> int: """Stopping of the `channels` in schedule component.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @property @abstractmethod - def _children(self) -> Tuple[Union[int, 'ScheduleComponent']]: - """Child nodes of this schedule component. """ - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + def _children(self) -> Tuple[Union[int, "ScheduleComponent"]]: + """Child nodes of this schedule component.""" + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @property @abstractmethod - def instructions(self) -> Tuple[Tuple[int, 'Instructions']]: + def instructions(self) -> Tuple[Tuple[int, "Instructions"]]: """Return iterable for all `Instruction`s in `Schedule` tree.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def flatten(self) -> 'ScheduleComponent': + def flatten(self) -> "ScheduleComponent": """Return a new schedule which is the flattened schedule contained all `instructions`.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def shift(self: 'ScheduleComponent', time: int, - name: Optional[str] = None) -> 'ScheduleComponent': + def shift( + self: "ScheduleComponent", time: int, name: Optional[str] = None + ) -> "ScheduleComponent": """Return a new schedule shifted forward by `time`. Args: time: Time to shift by name: Name of the new schedule. Defaults to name of parent """ - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def insert(self, start_time: int, schedule: 'ScheduleComponent', - name: Optional[str] = None) -> 'ScheduleComponent': + def insert( + self, start_time: int, schedule: "ScheduleComponent", name: Optional[str] = None + ) -> "ScheduleComponent": """Return a new schedule with `schedule` inserted at `start_time` of `self`. Args: @@ -150,14 +188,18 @@ def insert(self, start_time: int, schedule: 'ScheduleComponent', schedule: schedule to be inserted name: Name of the new schedule. Defaults to name of parent """ - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def append(self, schedule: 'ScheduleComponent', - name: Optional[str] = None) -> 'ScheduleComponent': + def append( + self, schedule: "ScheduleComponent", name: Optional[str] = None + ) -> "ScheduleComponent": """Return a new schedule with `schedule` inserted at the maximum time over all channels shared between `self` and `schedule`. @@ -165,31 +207,43 @@ def append(self, schedule: 'ScheduleComponent', schedule: schedule to be appended name: Name of the new schedule. Defaults to name of parent """ - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def __add__(self, schedule: 'ScheduleComponent') -> 'ScheduleComponent': + def __add__(self, schedule: "ScheduleComponent") -> "ScheduleComponent": """Return a new schedule with `schedule` inserted within `self` at `start_time`.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def __or__(self, schedule: 'ScheduleComponent') -> 'ScheduleComponent': + def __or__(self, schedule: "ScheduleComponent") -> "ScheduleComponent": """Return a new schedule which is the union of `self` and `schedule`.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass @abstractmethod - def __lshift__(self, time: int) -> 'ScheduleComponent': + def __lshift__(self, time: int) -> "ScheduleComponent": """Return a new schedule which is shifted forward by `time`.""" - warnings.warn("ScheduleComponent is deprecated and will be removed in a future release. " - "Anywhere that currently accepts a ``ScheduleComponent`` should instead " - "accept a ``Union[Schedule, Instruction]`` ", DeprecationWarning) + warnings.warn( + "ScheduleComponent is deprecated and will be removed in a future release. " + "Anywhere that currently accepts a ``ScheduleComponent`` should instead " + "accept a ``Union[Schedule, Instruction]`` ", + DeprecationWarning, + ) pass diff --git a/qiskit/pulse/library/__init__.py b/qiskit/pulse/library/__init__.py index 21b3944dff79..7e20c4c39431 100644 --- a/qiskit/pulse/library/__init__.py +++ b/qiskit/pulse/library/__init__.py @@ -34,7 +34,6 @@ """ from .discrete import * -from .parametric_pulses import (ParametricPulse, Gaussian, GaussianSquare, - Drag, Constant) +from .parametric_pulses import ParametricPulse, Gaussian, GaussianSquare, Drag, Constant from .pulse import Pulse from .waveform import Waveform diff --git a/qiskit/pulse/library/continuous.py b/qiskit/pulse/library/continuous.py index 2b9d775227f9..f7123504e5f1 100644 --- a/qiskit/pulse/library/continuous.py +++ b/qiskit/pulse/library/continuous.py @@ -49,8 +49,8 @@ def square(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np freq: Pulse frequency. units of 1/dt. phase: Pulse phase. """ - x = times*freq+phase/np.pi - return amp*(2*(2*np.floor(x) - np.floor(2*x)) + 1).astype(np.complex_) + x = times * freq + phase / np.pi + return amp * (2 * (2 * np.floor(x) - np.floor(2 * x)) + 1).astype(np.complex_) def sawtooth(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray: @@ -62,8 +62,8 @@ def sawtooth(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> freq: Pulse frequency. units of 1/dt. phase: Pulse phase. """ - x = times*freq+phase/np.pi - return amp*2*(x-np.floor(1/2+x)).astype(np.complex_) + x = times * freq + phase / np.pi + return amp * 2 * (x - np.floor(1 / 2 + x)).astype(np.complex_) def triangle(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray: @@ -75,8 +75,9 @@ def triangle(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> freq: Pulse frequency. units of 1/dt. phase: Pulse phase. """ - return amp*(-2*np.abs( - sawtooth(times, 1, freq, phase=(phase-np.pi/2)/2)) + 1).astype(np.complex_) + return amp * (-2 * np.abs(sawtooth(times, 1, freq, phase=(phase - np.pi / 2) / 2)) + 1).astype( + np.complex_ + ) def cos(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray: @@ -88,7 +89,7 @@ def cos(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.nd freq: Pulse frequency, units of 1/dt. phase: Pulse phase. """ - return amp*np.cos(2*np.pi*freq*times+phase).astype(np.complex_) + return amp * np.cos(2 * np.pi * freq * times + phase).astype(np.complex_) def sin(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.ndarray: @@ -100,12 +101,18 @@ def sin(times: np.ndarray, amp: complex, freq: float, phase: float = 0) -> np.nd freq: Pulse frequency, units of 1/dt. phase: Pulse phase. """ - return amp*np.sin(2*np.pi*freq*times+phase).astype(np.complex_) - - -def _fix_gaussian_width(gaussian_samples, amp: float, center: float, sigma: float, - zeroed_width: Optional[float] = None, rescale_amp: bool = False, - ret_scale_factor: bool = False) -> np.ndarray: + return amp * np.sin(2 * np.pi * freq * times + phase).astype(np.complex_) + + +def _fix_gaussian_width( + gaussian_samples, + amp: float, + center: float, + sigma: float, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, + ret_scale_factor: bool = False, +) -> np.ndarray: r"""Enforce that the supplied gaussian pulse is zeroed at a specific width. This is achieved by subtracting $\Omega_g(center \pm zeroed_width/2)$ from all samples. @@ -121,13 +128,13 @@ def _fix_gaussian_width(gaussian_samples, amp: float, center: float, sigma: floa ret_scale_factor: Return amplitude scale factor. """ if zeroed_width is None: - zeroed_width = 2*(center + 1) + zeroed_width = 2 * (center + 1) - zero_offset = gaussian(np.array([zeroed_width/2]), amp, 0, sigma) + zero_offset = gaussian(np.array([zeroed_width / 2]), amp, 0, sigma) gaussian_samples -= zero_offset - amp_scale_factor = 1. + amp_scale_factor = 1.0 if rescale_amp: - amp_scale_factor = amp/(amp-zero_offset) if amp-zero_offset != 0 else 1. + amp_scale_factor = amp / (amp - zero_offset) if amp - zero_offset != 0 else 1.0 gaussian_samples *= amp_scale_factor if ret_scale_factor: @@ -135,9 +142,15 @@ def _fix_gaussian_width(gaussian_samples, amp: float, center: float, sigma: floa return gaussian_samples -def gaussian(times: np.ndarray, amp: complex, center: float, sigma: float, - zeroed_width: Optional[float] = None, rescale_amp: bool = False, - ret_x: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: +def gaussian( + times: np.ndarray, + amp: complex, + center: float, + sigma: float, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, + ret_x: bool = False, +) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: r"""Continuous unnormalized gaussian pulse. Integrated area under curve is $\Omega_g(amp, sigma) = amp \times np.sqrt(2\pi \sigma^2)$ @@ -159,21 +172,33 @@ def gaussian(times: np.ndarray, amp: complex, center: float, sigma: float, $x=(times-center)/sigma. """ times = np.asarray(times, dtype=np.complex_) - x = (times-center)/sigma - gauss = amp*np.exp(-x**2/2).astype(np.complex_) + x = (times - center) / sigma + gauss = amp * np.exp(-(x ** 2) / 2).astype(np.complex_) if zeroed_width is not None: - gauss = _fix_gaussian_width(gauss, amp=amp, center=center, sigma=sigma, - zeroed_width=zeroed_width, rescale_amp=rescale_amp) + gauss = _fix_gaussian_width( + gauss, + amp=amp, + center=center, + sigma=sigma, + zeroed_width=zeroed_width, + rescale_amp=rescale_amp, + ) if ret_x: return gauss, x return gauss -def gaussian_deriv(times: np.ndarray, amp: complex, center: float, sigma: float, - ret_gaussian: bool = False, zeroed_width: Optional[float] = None, - rescale_amp: bool = False) -> np.ndarray: +def gaussian_deriv( + times: np.ndarray, + amp: complex, + center: float, + sigma: float, + ret_gaussian: bool = False, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, +) -> np.ndarray: r"""Continuous unnormalized gaussian derivative pulse. Args: @@ -188,17 +213,30 @@ def gaussian_deriv(times: np.ndarray, amp: complex, center: float, sigma: float, rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will be rescaled so that $\Omega_g(center)=amp$. """ - gauss, x = gaussian(times, amp=amp, center=center, sigma=sigma, zeroed_width=zeroed_width, - rescale_amp=rescale_amp, ret_x=True) + gauss, x = gaussian( + times, + amp=amp, + center=center, + sigma=sigma, + zeroed_width=zeroed_width, + rescale_amp=rescale_amp, + ret_x=True, + ) gauss_deriv = -x / sigma * gauss if ret_gaussian: return gauss_deriv, gauss return gauss_deriv -def _fix_sech_width(sech_samples, amp: float, center: float, sigma: float, - zeroed_width: Optional[float] = None, rescale_amp: bool = False, - ret_scale_factor: bool = False) -> np.ndarray: +def _fix_sech_width( + sech_samples, + amp: float, + center: float, + sigma: float, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, + ret_scale_factor: bool = False, +) -> np.ndarray: r"""Enforce that the supplied sech pulse is zeroed at a specific width. This is achieved by subtracting $\Omega_g(center \pm zeroed_width/2)$ from all samples. @@ -214,13 +252,13 @@ def _fix_sech_width(sech_samples, amp: float, center: float, sigma: float, ret_scale_factor: Return amplitude scale factor. """ if zeroed_width is None: - zeroed_width = 2*(center + 1) + zeroed_width = 2 * (center + 1) - zero_offset = sech(np.array([zeroed_width/2]), amp, 0, sigma) + zero_offset = sech(np.array([zeroed_width / 2]), amp, 0, sigma) sech_samples -= zero_offset - amp_scale_factor = 1. + amp_scale_factor = 1.0 if rescale_amp: - amp_scale_factor = amp/(amp-zero_offset) if amp-zero_offset != 0 else 1. + amp_scale_factor = amp / (amp - zero_offset) if amp - zero_offset != 0 else 1.0 sech_samples *= amp_scale_factor if ret_scale_factor: @@ -233,9 +271,15 @@ def sech_fn(x, *args, **kwargs): return 1.0 / np.cosh(x, *args, **kwargs) -def sech(times: np.ndarray, amp: complex, center: float, sigma: float, - zeroed_width: Optional[float] = None, rescale_amp: bool = False, - ret_x: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: +def sech( + times: np.ndarray, + amp: complex, + center: float, + sigma: float, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, + ret_x: bool = False, +) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: r"""Continuous unnormalized sech pulse. Args: @@ -252,20 +296,27 @@ def sech(times: np.ndarray, amp: complex, center: float, sigma: float, $x=(times-center)/sigma$. """ times = np.asarray(times, dtype=np.complex_) - x = (times-center)/sigma - sech_out = amp*sech_fn(x).astype(np.complex_) + x = (times - center) / sigma + sech_out = amp * sech_fn(x).astype(np.complex_) if zeroed_width is not None: - sech_out = _fix_sech_width(sech_out, amp=amp, center=center, sigma=sigma, - zeroed_width=zeroed_width, rescale_amp=rescale_amp) + sech_out = _fix_sech_width( + sech_out, + amp=amp, + center=center, + sigma=sigma, + zeroed_width=zeroed_width, + rescale_amp=rescale_amp, + ) if ret_x: return sech_out, x return sech_out -def sech_deriv(times: np.ndarray, amp: complex, center: float, sigma: float, - ret_sech: bool = False) -> np.ndarray: +def sech_deriv( + times: np.ndarray, amp: complex, center: float, sigma: float, ret_sech: bool = False +) -> np.ndarray: """Continuous unnormalized sech derivative pulse. Args: @@ -276,14 +327,20 @@ def sech_deriv(times: np.ndarray, amp: complex, center: float, sigma: float, ret_sech: Return sech with which derivative was taken with. """ sech_out, x = sech(times, amp=amp, center=center, sigma=sigma, ret_x=True) - sech_out_deriv = - sech_out * np.tanh(x) / sigma + sech_out_deriv = -sech_out * np.tanh(x) / sigma if ret_sech: return sech_out_deriv, sech_out return sech_out_deriv -def gaussian_square(times: np.ndarray, amp: complex, center: float, square_width: float, - sigma: float, zeroed_width: Optional[float] = None) -> np.ndarray: +def gaussian_square( + times: np.ndarray, + amp: complex, + center: float, + square_width: float, + sigma: float, + zeroed_width: Optional[float] = None, +) -> np.ndarray: r"""Continuous gaussian square pulse. Args: @@ -298,26 +355,47 @@ def gaussian_square(times: np.ndarray, amp: complex, center: float, square_width Raises: PulseError: if zeroed_width is not compatible with square_width. """ - square_start = center-square_width/2 - square_stop = center+square_width/2 + square_start = center - square_width / 2 + square_stop = center + square_width / 2 if zeroed_width: if zeroed_width < square_width: raise PulseError("zeroed_width cannot be smaller than square_width.") - gaussian_zeroed_width = zeroed_width-square_width + gaussian_zeroed_width = zeroed_width - square_width else: gaussian_zeroed_width = None - funclist = [functools.partial(gaussian, amp=amp, center=square_start, sigma=sigma, - zeroed_width=gaussian_zeroed_width, rescale_amp=True), - functools.partial(gaussian, amp=amp, center=square_stop, sigma=sigma, - zeroed_width=gaussian_zeroed_width, rescale_amp=True), - functools.partial(constant, amp=amp)] + funclist = [ + functools.partial( + gaussian, + amp=amp, + center=square_start, + sigma=sigma, + zeroed_width=gaussian_zeroed_width, + rescale_amp=True, + ), + functools.partial( + gaussian, + amp=amp, + center=square_stop, + sigma=sigma, + zeroed_width=gaussian_zeroed_width, + rescale_amp=True, + ), + functools.partial(constant, amp=amp), + ] condlist = [times <= square_start, times >= square_stop] return np.piecewise(times.astype(np.complex_), condlist, funclist) -def drag(times: np.ndarray, amp: complex, center: float, sigma: float, beta: float, - zeroed_width: Optional[float] = None, rescale_amp: bool = False) -> np.ndarray: +def drag( + times: np.ndarray, + amp: complex, + center: float, + sigma: float, + beta: float, + zeroed_width: Optional[float] = None, + rescale_amp: bool = False, +) -> np.ndarray: r"""Continuous Y-only correction DRAG pulse for standard nonlinear oscillator (SNO) [1]. [1] Gambetta, J. M., Motzoi, F., Merkel, S. T. & Wilhelm, F. K. @@ -339,8 +417,14 @@ def drag(times: np.ndarray, amp: complex, center: float, sigma: float, beta: flo be rescaled so that $\Omega_g(center)=amp$. """ - gauss_deriv, gauss = gaussian_deriv(times, amp=amp, center=center, sigma=sigma, - ret_gaussian=True, zeroed_width=zeroed_width, - rescale_amp=rescale_amp) - - return gauss + 1j*beta*gauss_deriv + gauss_deriv, gauss = gaussian_deriv( + times, + amp=amp, + center=center, + sigma=sigma, + ret_gaussian=True, + zeroed_width=zeroed_width, + rescale_amp=rescale_amp, + ) + + return gauss + 1j * beta * gauss_deriv diff --git a/qiskit/pulse/library/discrete.py b/qiskit/pulse/library/discrete.py index 8ac1332e41fb..53609ec6af34 100644 --- a/qiskit/pulse/library/discrete.py +++ b/qiskit/pulse/library/discrete.py @@ -66,8 +66,9 @@ def zero(duration: int, name: Optional[str] = None) -> Waveform: _sampled_square_pulse = samplers.midpoint(continuous.square) -def square(duration: int, amp: complex, freq: float = None, - phase: float = 0, name: Optional[str] = None) -> Waveform: +def square( + duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None +) -> Waveform: r"""Generates square wave :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, @@ -89,7 +90,7 @@ def square(duration: int, amp: complex, freq: float = None, name: Name of pulse. """ if freq is None: - freq = 1./duration + freq = 1.0 / duration return _sampled_square_pulse(duration, amp, freq, phase=phase, name=name) @@ -97,8 +98,9 @@ def square(duration: int, amp: complex, freq: float = None, _sampled_sawtooth_pulse = samplers.midpoint(continuous.sawtooth) -def sawtooth(duration: int, amp: complex, freq: float = None, - phase: float = 0, name: Optional[str] = None) -> Waveform: +def sawtooth( + duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None +) -> Waveform: r"""Generates sawtooth wave :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, @@ -132,7 +134,7 @@ def sawtooth(duration: int, amp: complex, freq: float = None, plt.plot(range(duration), sawtooth_wave) """ if freq is None: - freq = 1./duration + freq = 1.0 / duration return _sampled_sawtooth_pulse(duration, amp, freq, phase=phase, name=name) @@ -140,8 +142,9 @@ def sawtooth(duration: int, amp: complex, freq: float = None, _sampled_triangle_pulse = samplers.midpoint(continuous.triangle) -def triangle(duration: int, amp: complex, freq: float = None, - phase: float = 0, name: Optional[str] = None) -> Waveform: +def triangle( + duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None +) -> Waveform: r"""Generates triangle wave :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp``, :math:`T=` ``period``, and :math:`\phi=` ``phase``, @@ -175,7 +178,7 @@ def triangle(duration: int, amp: complex, freq: float = None, plt.plot(range(duration), triangle_wave) """ if freq is None: - freq = 1./duration + freq = 1.0 / duration return _sampled_triangle_pulse(duration, amp, freq, phase=phase, name=name) @@ -183,8 +186,9 @@ def triangle(duration: int, amp: complex, freq: float = None, _sampled_cos_pulse = samplers.midpoint(continuous.cos) -def cos(duration: int, amp: complex, freq: float = None, - phase: float = 0, name: Optional[str] = None) -> Waveform: +def cos( + duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None +) -> Waveform: r"""Generates cosine wave :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp``, :math:`\omega=` ``freq``, and :math:`\phi=` ``phase``, @@ -203,7 +207,7 @@ def cos(duration: int, amp: complex, freq: float = None, name: Name of pulse. """ if freq is None: - freq = 1/duration + freq = 1 / duration return _sampled_cos_pulse(duration, amp, freq, phase=phase, name=name) @@ -211,8 +215,9 @@ def cos(duration: int, amp: complex, freq: float = None, _sampled_sin_pulse = samplers.midpoint(continuous.sin) -def sin(duration: int, amp: complex, freq: float = None, - phase: float = 0, name: Optional[str] = None) -> Waveform: +def sin( + duration: int, amp: complex, freq: float = None, phase: float = 0, name: Optional[str] = None +) -> Waveform: r"""Generates sine wave :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp``, :math:`\omega=` ``freq``, and :math:`\phi=` ``phase``, @@ -231,7 +236,7 @@ def sin(duration: int, amp: complex, freq: float = None, name: Name of pulse. """ if freq is None: - freq = 1/duration + freq = 1 / duration return _sampled_sin_pulse(duration, amp, freq, phase=phase, name=name) @@ -239,8 +244,9 @@ def sin(duration: int, amp: complex, freq: float = None, _sampled_gaussian_pulse = samplers.midpoint(continuous.gaussian) -def gaussian(duration: int, amp: complex, sigma: float, name: Optional[str] = None, - zero_ends: bool = True) -> Waveform: +def gaussian( + duration: int, amp: complex, sigma: float, name: Optional[str] = None, zero_ends: bool = True +) -> Waveform: r"""Generates unnormalized gaussian :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma``, applies the ``midpoint`` sampling strategy @@ -271,19 +277,20 @@ def gaussian(duration: int, amp: complex, sigma: float, name: Optional[str] = No name: Name of pulse. zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. """ - center = duration/2 + center = duration / 2 zeroed_width = duration + 2 if zero_ends else None rescale_amp = bool(zero_ends) - return _sampled_gaussian_pulse(duration, amp, center, sigma, - zeroed_width=zeroed_width, rescale_amp=rescale_amp, - name=name) + return _sampled_gaussian_pulse( + duration, amp, center, sigma, zeroed_width=zeroed_width, rescale_amp=rescale_amp, name=name + ) _sampled_gaussian_deriv_pulse = samplers.midpoint(continuous.gaussian_deriv) -def gaussian_deriv(duration: int, amp: complex, sigma: float, - name: Optional[str] = None) -> Waveform: +def gaussian_deriv( + duration: int, amp: complex, sigma: float, name: Optional[str] = None +) -> Waveform: r"""Generates unnormalized gaussian derivative :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma`` applies the `midpoint` sampling strategy @@ -301,15 +308,16 @@ def gaussian_deriv(duration: int, amp: complex, sigma: float, sigma: Width (standard deviation) of pulse. name: Name of pulse. """ - center = duration/2 + center = duration / 2 return _sampled_gaussian_deriv_pulse(duration, amp, center, sigma, name=name) _sampled_sech_pulse = samplers.midpoint(continuous.sech) -def sech(duration: int, amp: complex, sigma: float, name: str = None, - zero_ends: bool = True) -> Waveform: +def sech( + duration: int, amp: complex, sigma: float, name: str = None, zero_ends: bool = True +) -> Waveform: r"""Generates unnormalized sech :class:`~qiskit.pulse.Waveform`. For :math:`A=` ``amp`` and :math:`\sigma=` ``sigma``, applies the ``midpoint`` sampling strategy @@ -338,12 +346,12 @@ def sech(duration: int, amp: complex, sigma: float, name: str = None, name: Name of pulse. zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. """ - center = duration/2 + center = duration / 2 zeroed_width = duration + 2 if zero_ends else None rescale_amp = bool(zero_ends) - return _sampled_sech_pulse(duration, amp, center, sigma, - zeroed_width=zeroed_width, rescale_amp=rescale_amp, - name=name) + return _sampled_sech_pulse( + duration, amp, center, sigma, zeroed_width=zeroed_width, rescale_amp=rescale_amp, name=name + ) _sampled_sech_deriv_pulse = samplers.midpoint(continuous.sech_deriv) @@ -367,16 +375,22 @@ def sech_deriv(duration: int, amp: complex, sigma: float, name: str = None) -> W sigma: Width (standard deviation) of pulse. name: Name of pulse. """ - center = duration/2 + center = duration / 2 return _sampled_sech_deriv_pulse(duration, amp, center, sigma, name=name) _sampled_gaussian_square_pulse = samplers.midpoint(continuous.gaussian_square) -def gaussian_square(duration: int, amp: complex, sigma: float, - risefall: Optional[float] = None, width: Optional[float] = None, - name: Optional[str] = None, zero_ends: bool = True) -> Waveform: +def gaussian_square( + duration: int, + amp: complex, + sigma: float, + risefall: Optional[float] = None, + width: Optional[float] = None, + name: Optional[str] = None, + zero_ends: bool = True, +) -> Waveform: r"""Generates gaussian square :class:`~qiskit.pulse.Waveform`. For :math:`d=` ``duration``, :math:`A=` ``amp``, :math:`\sigma=` ``sigma``, @@ -419,20 +433,29 @@ def gaussian_square(duration: int, amp: complex, sigma: float, if width is None: width = duration - 2 * risefall elif 2 * risefall + width != duration: - raise PulseError("Both width and risefall were specified, and they are " - "inconsistent: 2 * risefall + width == {} != " - "duration == {}.".format(2 * risefall + width, duration)) + raise PulseError( + "Both width and risefall were specified, and they are " + "inconsistent: 2 * risefall + width == {} != " + "duration == {}.".format(2 * risefall + width, duration) + ) center = duration / 2 zeroed_width = duration + 2 if zero_ends else None - return _sampled_gaussian_square_pulse(duration, amp, center, width, sigma, - zeroed_width=zeroed_width, name=name) + return _sampled_gaussian_square_pulse( + duration, amp, center, width, sigma, zeroed_width=zeroed_width, name=name + ) _sampled_drag_pulse = samplers.midpoint(continuous.drag) -def drag(duration: int, amp: complex, sigma: float, beta: float, - name: Optional[str] = None, zero_ends: bool = True) -> Waveform: +def drag( + duration: int, + amp: complex, + sigma: float, + beta: float, + name: Optional[str] = None, + zero_ends: bool = True, +) -> Waveform: r"""Generates Y-only correction DRAG :class:`~qiskit.pulse.Waveform` for standard nonlinear oscillator (SNO) [1]. @@ -469,9 +492,16 @@ def drag(duration: int, amp: complex, sigma: float, beta: float, name: Name of pulse. zero_ends: If True, zero ends at ``x = -1, x = duration + 1``, but rescale to preserve amp. """ - center = duration/2 + center = duration / 2 zeroed_width = duration + 2 if zero_ends else None rescale_amp = bool(zero_ends) - return _sampled_drag_pulse(duration, amp, center, sigma, beta, - zeroed_width=zeroed_width, rescale_amp=rescale_amp, - name=name) + return _sampled_drag_pulse( + duration, + amp, + center, + sigma, + beta, + zeroed_width=zeroed_width, + rescale_amp=rescale_amp, + name=name, + ) diff --git a/qiskit/pulse/library/parametric_pulses.py b/qiskit/pulse/library/parametric_pulses.py index ee6997eba391..898b0ee28e08 100644 --- a/qiskit/pulse/library/parametric_pulses.py +++ b/qiskit/pulse/library/parametric_pulses.py @@ -55,9 +55,7 @@ class ParametricPulse(Pulse): """The abstract superclass for parametric pulses.""" @abstractmethod - def __init__(self, - duration: Union[int, ParameterExpression], - name: Optional[str] = None): + def __init__(self, duration: Union[int, ParameterExpression], name: Optional[str] = None): """Create a parametric pulse and validate the input parameters. Args: @@ -89,17 +87,18 @@ def is_parameterized(self) -> bool: return any(_is_parameterized(val) for val in self.parameters.values()) @deprecated_functionality - def assign(self, parameter: ParameterExpression, - value: ParameterValueType) -> 'ParametricPulse': + def assign( + self, parameter: ParameterExpression, value: ParameterValueType + ) -> "ParametricPulse": """Assign one parameter to a value, which can either be numeric or another parameter expression. """ return self.assign_parameters({parameter: value}) @deprecated_functionality - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'ParametricPulse': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "ParametricPulse": """Return a new ParametricPulse with parameters assigned. Args: @@ -136,11 +135,13 @@ class Gaussian(ParametricPulse): f(x) = amp * exp( -(1/2) * (x - duration/2)^2 / sigma^2) ) , 0 <= x < duration """ - def __init__(self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + amp: Union[complex, ParameterExpression], + sigma: Union[float, ParameterExpression], + name: Optional[str] = None, + ): """Initialize the gaussian pulse. Args: @@ -167,13 +168,11 @@ def sigma(self) -> Union[float, ParameterExpression]: return self._sigma def get_waveform(self) -> Waveform: - return gaussian(duration=self.duration, amp=self.amp, - sigma=self.sigma, zero_ends=True) + return gaussian(duration=self.duration, amp=self.amp, sigma=self.sigma, zero_ends=True) def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.: - raise PulseError("The amplitude norm must be <= 1, " - "found: {}".format(abs(self.amp))) + if not _is_parameterized(self.amp) and abs(self.amp) > 1.0: + raise PulseError("The amplitude norm must be <= 1, " "found: {}".format(abs(self.amp))) if not _is_parameterized(self.sigma) and self.sigma <= 0: raise PulseError("Sigma must be greater than 0.") @@ -182,9 +181,13 @@ def parameters(self) -> Dict[str, Any]: return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma} def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}{})" \ - "".format(self.__class__.__name__, self.duration, self.amp, self.sigma, - ", name='{}'".format(self.name) if self.name is not None else "") + return "{}(duration={}, amp={}, sigma={}{})" "".format( + self.__class__.__name__, + self.duration, + self.amp, + self.sigma, + ", name='{}'".format(self.name) if self.name is not None else "", + ) class GaussianSquare(ParametricPulse): @@ -207,12 +210,14 @@ class GaussianSquare(ParametricPulse): f(x) = amp * exp( -(1/2) * (x - (risefall + width)/2)^2 / sigma^2) ) """ - def __init__(self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - width: Union[float, ParameterExpression], - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + amp: Union[complex, ParameterExpression], + sigma: Union[float, ParameterExpression], + width: Union[float, ParameterExpression], + name: Optional[str] = None, + ): """Initialize the gaussian square pulse. Args: @@ -246,14 +251,13 @@ def width(self) -> Union[float, ParameterExpression]: return self._width def get_waveform(self) -> Waveform: - return gaussian_square(duration=self.duration, amp=self.amp, - width=self.width, sigma=self.sigma, - zero_ends=True) + return gaussian_square( + duration=self.duration, amp=self.amp, width=self.width, sigma=self.sigma, zero_ends=True + ) def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.: - raise PulseError("The amplitude norm must be <= 1, " - "found: {}".format(abs(self.amp))) + if not _is_parameterized(self.amp) and abs(self.amp) > 1.0: + raise PulseError("The amplitude norm must be <= 1, " "found: {}".format(abs(self.amp))) if not _is_parameterized(self.sigma) and self.sigma <= 0: raise PulseError("Sigma must be greater than 0.") if not _is_parameterized(self.width) and (self.width < 0 or self.width >= self.duration): @@ -261,13 +265,22 @@ def validate_parameters(self) -> None: @property def parameters(self) -> Dict[str, Any]: - return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma, - "width": self.width} + return { + "duration": self.duration, + "amp": self.amp, + "sigma": self.sigma, + "width": self.width, + } def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}, width={}{})" \ - "".format(self.__class__.__name__, self.duration, self.amp, self.sigma, self.width, - ", name='{}'".format(self.name) if self.name is not None else "") + return "{}(duration={}, amp={}, sigma={}, width={}{})" "".format( + self.__class__.__name__, + self.duration, + self.amp, + self.sigma, + self.width, + ", name='{}'".format(self.name) if self.name is not None else "", + ) class Drag(ParametricPulse): @@ -304,12 +317,14 @@ class Drag(ParametricPulse): Phys. Rev. Lett. 103, 110501 – Published 8 September 2009.* """ - def __init__(self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - sigma: Union[float, ParameterExpression], - beta: Union[float, ParameterExpression], - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + amp: Union[complex, ParameterExpression], + sigma: Union[float, ParameterExpression], + beta: Union[float, ParameterExpression], + name: Optional[str] = None, + ): """Initialize the drag pulse. Args: @@ -343,20 +358,23 @@ def beta(self) -> Union[float, ParameterExpression]: return self._beta def get_waveform(self) -> Waveform: - return drag(duration=self.duration, amp=self.amp, sigma=self.sigma, - beta=self.beta, zero_ends=True) + return drag( + duration=self.duration, amp=self.amp, sigma=self.sigma, beta=self.beta, zero_ends=True + ) def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.: - raise PulseError("The amplitude norm must be <= 1, " - "found: {}".format(abs(self.amp))) + if not _is_parameterized(self.amp) and abs(self.amp) > 1.0: + raise PulseError("The amplitude norm must be <= 1, " "found: {}".format(abs(self.amp))) if not _is_parameterized(self.sigma) and self.sigma <= 0: raise PulseError("Sigma must be greater than 0.") if not _is_parameterized(self.beta) and isinstance(self.beta, complex): raise PulseError("Beta must be real.") # Check if beta is too large: the amplitude norm must be <=1 for all points - if (not _is_parameterized(self.beta) and not _is_parameterized(self.sigma) - and self.beta > self.sigma): + if ( + not _is_parameterized(self.beta) + and not _is_parameterized(self.sigma) + and self.beta > self.sigma + ): # If beta <= sigma, then the maximum amplitude is at duration / 2, which is # already constrainted by self.amp <= 1 @@ -364,27 +382,37 @@ def validate_parameters(self) -> None: # This eq is derived from solving for the roots of the norm of the drag function. # There is a second maxima mirrored around the center of the pulse with the same # norm as the first, so checking the value at the first x maxima is sufficient. - argmax_x = (self.duration / 2 - - (self.sigma / self.beta) * math.sqrt(self.beta ** 2 - self.sigma ** 2)) + argmax_x = self.duration / 2 - (self.sigma / self.beta) * math.sqrt( + self.beta ** 2 - self.sigma ** 2 + ) if argmax_x < 0: # If the max point is out of range, either end of the pulse will do argmax_x = 0 # 2. Find the value at that maximum - max_val = continuous.drag(np.array(argmax_x), sigma=self.sigma, - beta=self.beta, amp=self.amp, center=self.duration / 2) - if abs(max_val) > 1.: + max_val = continuous.drag( + np.array(argmax_x), + sigma=self.sigma, + beta=self.beta, + amp=self.amp, + center=self.duration / 2, + ) + if abs(max_val) > 1.0: raise PulseError("Beta is too large; pulse amplitude norm exceeds 1.") @property def parameters(self) -> Dict[str, Any]: - return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma, - "beta": self.beta} + return {"duration": self.duration, "amp": self.amp, "sigma": self.sigma, "beta": self.beta} def __repr__(self) -> str: - return "{}(duration={}, amp={}, sigma={}, beta={}{})" \ - "".format(self.__class__.__name__, self.duration, self.amp, self.sigma, self.beta, - ", name='{}'".format(self.name) if self.name is not None else "") + return "{}(duration={}, amp={}, sigma={}, beta={}{})" "".format( + self.__class__.__name__, + self.duration, + self.amp, + self.sigma, + self.beta, + ", name='{}'".format(self.name) if self.name is not None else "", + ) class Constant(ParametricPulse): @@ -397,10 +425,12 @@ class Constant(ParametricPulse): f(x) = 0 , elsewhere """ - def __init__(self, - duration: Union[int, ParameterExpression], - amp: Union[complex, ParameterExpression], - name: Optional[str] = None): + def __init__( + self, + duration: Union[int, ParameterExpression], + amp: Union[complex, ParameterExpression], + name: Optional[str] = None, + ): """ Initialize the constant-valued pulse. @@ -423,18 +453,20 @@ def get_waveform(self) -> Waveform: return constant(duration=self.duration, amp=self.amp) def validate_parameters(self) -> None: - if not _is_parameterized(self.amp) and abs(self.amp) > 1.: - raise PulseError("The amplitude norm must be <= 1, " - "found: {}".format(abs(self.amp))) + if not _is_parameterized(self.amp) and abs(self.amp) > 1.0: + raise PulseError("The amplitude norm must be <= 1, " "found: {}".format(abs(self.amp))) @property def parameters(self) -> Dict[str, Any]: return {"duration": self.duration, "amp": self.amp} def __repr__(self) -> str: - return "{}(duration={}, amp={}{})" \ - "".format(self.__class__.__name__, self.duration, self.amp, - ", name='{}'".format(self.name) if self.name is not None else "") + return "{}(duration={}, amp={}{})" "".format( + self.__class__.__name__, + self.duration, + self.amp, + ", name='{}'".format(self.name) if self.name is not None else "", + ) def _is_parameterized(value: Any) -> bool: diff --git a/qiskit/pulse/library/pulse.py b/qiskit/pulse/library/pulse.py index cb87addaf28d..4dd1b733f025 100644 --- a/qiskit/pulse/library/pulse.py +++ b/qiskit/pulse/library/pulse.py @@ -27,9 +27,7 @@ class Pulse(ABC): """ @abstractmethod - def __init__(self, - duration: Union[int, ParameterExpression], - name: Optional[str] = None): + def __init__(self, duration: Union[int, ParameterExpression], name: Optional[str] = None): self.duration = duration self.name = name @@ -50,9 +48,9 @@ def is_parameterized(self) -> bool: @deprecated_functionality @abstractmethod - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'Pulse': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "Pulse": """Return a new pulse with parameters assigned. Args: @@ -64,20 +62,22 @@ def assign_parameters(self, """ raise NotImplementedError - def draw(self, - dt: Any = None, # deprecated - style: Optional[Dict[str, Any]] = None, - filename: Any = None, # deprecated - interp_method: Any = None, # deprecated - scale: Any = None, # deprecated - interactive: Any = None, # deprecated - draw_title: Any = None, # deprecated - backend=None, # importing backend causes cyclic import - time_range: Optional[Tuple[int, int]] = None, - time_unit: str = 'dt', - show_waveform_info: bool = True, - plotter: str = 'mpl2d', - axis: Optional[Any] = None): + def draw( + self, + dt: Any = None, # deprecated + style: Optional[Dict[str, Any]] = None, + filename: Any = None, # deprecated + interp_method: Any = None, # deprecated + scale: Any = None, # deprecated + interactive: Any = None, # deprecated + draw_title: Any = None, # deprecated + backend=None, # importing backend causes cyclic import + time_range: Optional[Tuple[int, int]] = None, + time_unit: str = "dt", + show_waveform_info: bool = True, + plotter: str = "mpl2d", + axis: Optional[Any] = None, + ): """Plot the interpolated envelope of pulse. Args: @@ -122,12 +122,14 @@ def draw(self, # pylint: disable=cyclic-import, missing-return-type-doc from qiskit.visualization import pulse_drawer_v2, PulseStyle - legacy_args = {'dt': dt, - 'filename': filename, - 'interp_method': interp_method, - 'scale': scale, - 'interactive': interactive, - 'draw_title': draw_title} + legacy_args = { + "dt": dt, + "filename": filename, + "interp_method": interp_method, + "scale": scale, + "interactive": interactive, + "draw_title": draw_title, + } active_legacy_args = [] for name, legacy_arg in legacy_args.items(): @@ -135,35 +137,43 @@ def draw(self, active_legacy_args.append(name) if active_legacy_args: - warnings.warn('Legacy pulse drawer is deprecated. ' - 'Specified arguments {dep_args} are deprecated. ' - 'Please check the API document of new pulse drawer ' - '`qiskit.visualization.pulse_drawer_v2`.' - ''.format(dep_args=', '.join(active_legacy_args)), - DeprecationWarning) + warnings.warn( + "Legacy pulse drawer is deprecated. " + "Specified arguments {dep_args} are deprecated. " + "Please check the API document of new pulse drawer " + "`qiskit.visualization.pulse_drawer_v2`." + "".format(dep_args=", ".join(active_legacy_args)), + DeprecationWarning, + ) if filename: - warnings.warn('File saving is delegated to the plotter software in new drawer. ' - 'If you specify matplotlib plotter family to `plotter` argument, ' - 'you can call `savefig` method with the returned Figure object.', - DeprecationWarning) + warnings.warn( + "File saving is delegated to the plotter software in new drawer. " + "If you specify matplotlib plotter family to `plotter` argument, " + "you can call `savefig` method with the returned Figure object.", + DeprecationWarning, + ) if isinstance(style, PulseStyle): style = None - warnings.warn('Legacy stylesheet is specified. This is ignored in the new drawer. ' - 'Please check the API documentation for this method.') - - return pulse_drawer_v2(program=self, - style=style, - backend=backend, - time_range=time_range, - time_unit=time_unit, - show_waveform_info=show_waveform_info, - plotter=plotter, - axis=axis) + warnings.warn( + "Legacy stylesheet is specified. This is ignored in the new drawer. " + "Please check the API documentation for this method." + ) + + return pulse_drawer_v2( + program=self, + style=style, + backend=backend, + time_range=time_range, + time_unit=time_unit, + show_waveform_info=show_waveform_info, + plotter=plotter, + axis=axis, + ) @abstractmethod - def __eq__(self, other: 'Pulse') -> bool: + def __eq__(self, other: "Pulse") -> bool: return isinstance(other, type(self)) @abstractmethod diff --git a/qiskit/pulse/library/samplers/decorators.py b/qiskit/pulse/library/samplers/decorators.py index 7a9f54ce2e57..ade7c99b6ec9 100644 --- a/qiskit/pulse/library/samplers/decorators.py +++ b/qiskit/pulse/library/samplers/decorators.py @@ -147,6 +147,7 @@ def functional_pulse(func: Callable) -> Callable: Raises: PulseError: when invalid function is specified. """ + @functools.wraps(func) def to_pulse(duration, *args, name=None, **kwargs): """Return Waveform.""" @@ -154,7 +155,7 @@ def to_pulse(duration, *args, name=None, **kwargs): samples = func(duration, *args, **kwargs) samples = np.asarray(samples, dtype=np.complex128) return Waveform(samples=samples, name=name) - raise PulseError('The first argument must be an integer value representing duration.') + raise PulseError("The first argument must be an integer value representing duration.") return to_pulse @@ -167,7 +168,7 @@ def _update_annotations(discretized_pulse: Callable) -> Callable: """ undecorated_annotations = list(discretized_pulse.__annotations__.items()) decorated_annotations = undecorated_annotations[1:] - decorated_annotations.insert(0, ('duration', int)) + decorated_annotations.insert(0, ("duration", int)) discretized_pulse.__annotations__ = dict(decorated_annotations) return discretized_pulse @@ -179,10 +180,10 @@ def _update_docstring(discretized_pulse: Callable, sampler_inst: Callable) -> Ca discretized_pulse: Discretized decorated continuous pulse. sampler_inst: Applied sampler. """ - wrapped_docstring = pydoc.render_doc(discretized_pulse, '%s') - header, body = wrapped_docstring.split('\n', 1) - body = textwrap.indent(body, ' ') - wrapped_docstring = header+body + wrapped_docstring = pydoc.render_doc(discretized_pulse, "%s") + header, body = wrapped_docstring.split("\n", 1) + body = textwrap.indent(body, " ") + wrapped_docstring = header + body updated_ds = """ Discretized continuous pulse function: `{continuous_name}` using sampler: `{sampler_name}`. @@ -200,9 +201,11 @@ def _update_docstring(discretized_pulse: Callable, sampler_inst: Callable) -> Ca Sampled continuous function: {continuous_doc} - """.format(continuous_name=discretized_pulse.__name__, - sampler_name=sampler_inst.__name__, - continuous_doc=wrapped_docstring) + """.format( + continuous_name=discretized_pulse.__name__, + sampler_name=sampler_inst.__name__, + continuous_doc=wrapped_docstring, + ) discretized_pulse.__doc__ = updated_ds return discretized_pulse @@ -247,7 +250,7 @@ def call_sampler(duration: int, *args, **kwargs) -> Waveform: # Unset wrapped to return base sampler signature # but still get rest of benefits of wraps # such as __name__, __qualname__ - call_sampler.__dict__.pop('__wrapped__') + call_sampler.__dict__.pop("__wrapped__") # wrap with functional pulse return functional_pulse(call_sampler) diff --git a/qiskit/pulse/library/samplers/strategies.py b/qiskit/pulse/library/samplers/strategies.py index fe4dfa6770e1..14c252d3bcf2 100644 --- a/qiskit/pulse/library/samplers/strategies.py +++ b/qiskit/pulse/library/samplers/strategies.py @@ -55,7 +55,7 @@ def right_sample(continuous_pulse: Callable, duration: int, *args, **kwargs) -> *args: Continuous pulse function args. **kwargs: Continuous pulse function kwargs. """ - times = np.arange(1, duration+1) + times = np.arange(1, duration + 1) return continuous_pulse(times, *args, **kwargs) @@ -68,5 +68,5 @@ def midpoint_sample(continuous_pulse: Callable, duration: int, *args, **kwargs) *args: Continuous pulse function args. **kwargs: Continuous pulse function kwargs. """ - times = np.arange(1/2, duration + 1/2) + times = np.arange(1 / 2, duration + 1 / 2) return continuous_pulse(times, *args, **kwargs) diff --git a/qiskit/pulse/library/waveform.py b/qiskit/pulse/library/waveform.py index a53b4ff2f467..1ce787d937ec 100644 --- a/qiskit/pulse/library/waveform.py +++ b/qiskit/pulse/library/waveform.py @@ -25,9 +25,13 @@ class Waveform(Pulse): """A pulse specified completely by complex-valued samples; each sample is played for the duration of the backend cycle-time, dt. """ - def __init__(self, samples: Union[np.ndarray, List[complex]], - name: Optional[str] = None, - epsilon: float = 1e-7): + + def __init__( + self, + samples: Union[np.ndarray, List[complex]], + name: Optional[str] = None, + epsilon: float = 1e-7, + ): """Create new sample pulse command. Args: @@ -68,30 +72,31 @@ def _clip(self, samples: np.ndarray, epsilon: float = 1e-7) -> np.ndarray: PulseError: If there exists a pulse sample with a norm greater than 1+epsilon. """ samples_norm = np.abs(samples) - to_clip = (samples_norm > 1.) & (samples_norm <= 1. + epsilon) + to_clip = (samples_norm > 1.0) & (samples_norm <= 1.0 + epsilon) if np.any(to_clip): # first try normalizing by the abs value clip_where = np.argwhere(to_clip) clip_angle = np.angle(samples[clip_where]) - clipped_samples = np.exp(1j*clip_angle, dtype=np.complex_) + clipped_samples = np.exp(1j * clip_angle, dtype=np.complex_) # if norm still exceed one subtract epsilon # required for some platforms clipped_sample_norms = np.abs(clipped_samples) - to_clip_epsilon = clipped_sample_norms > 1. + to_clip_epsilon = clipped_sample_norms > 1.0 if np.any(to_clip_epsilon): clip_where_epsilon = np.argwhere(to_clip_epsilon) - clipped_samples_epsilon = (1-epsilon)*np.exp( - 1j*clip_angle[clip_where_epsilon], dtype=np.complex_) + clipped_samples_epsilon = (1 - epsilon) * np.exp( + 1j * clip_angle[clip_where_epsilon], dtype=np.complex_ + ) clipped_samples[clip_where_epsilon] = clipped_samples_epsilon # update samples with clipped values samples[clip_where] = clipped_samples samples_norm[clip_where] = np.abs(clipped_samples) - if np.any(samples_norm > 1.): - raise PulseError('Pulse contains sample with norm greater than 1+epsilon.') + if np.any(samples_norm > 1.0): + raise PulseError("Pulse contains sample with norm greater than 1+epsilon.") return samples @@ -105,15 +110,18 @@ def parameters(self) -> Dict[str, Any]: return dict() @deprecated_functionality - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType] - ) -> 'Waveform': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType] + ) -> "Waveform": # Waveforms don't accept parameters return self def __eq__(self, other: Pulse) -> bool: - return super().__eq__(other) and self.samples.shape == other.samples.shape and \ - np.allclose(self.samples, other.samples, rtol=0, atol=self.epsilon) + return ( + super().__eq__(other) + and self.samples.shape == other.samples.shape + and np.allclose(self.samples, other.samples, rtol=0, atol=self.epsilon) + ) def __hash__(self) -> int: return hash(self.samples.tostring()) @@ -122,5 +130,8 @@ def __repr__(self) -> str: opt = np.get_printoptions() np.set_printoptions(threshold=50) np.set_printoptions(**opt) - return "{}({}{})".format(self.__class__.__name__, repr(self.samples), - ", name='{}'".format(self.name) if self.name is not None else "") + return "{}({}{})".format( + self.__class__.__name__, + repr(self.samples), + ", name='{}'".format(self.name) if self.name is not None else "", + ) diff --git a/qiskit/pulse/macros.py b/qiskit/pulse/macros.py index 2cd3cf5876db..2423afa57059 100644 --- a/qiskit/pulse/macros.py +++ b/qiskit/pulse/macros.py @@ -19,12 +19,14 @@ from qiskit.pulse.schedule import Schedule -def measure(qubits: List[int], - backend=None, - inst_map: Optional[InstructionScheduleMap] = None, - meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, - qubit_mem_slots: Optional[Dict[int, int]] = None, - measure_name: str = 'measure') -> Schedule: +def measure( + qubits: List[int], + backend=None, + inst_map: Optional[InstructionScheduleMap] = None, + meas_map: Optional[Union[List[List[int]], Dict[int, List[int]]]] = None, + qubit_mem_slots: Optional[Dict[int, int]] = None, + measure_name: str = "measure", +) -> Schedule: """Return a schedule which measures the requested qubits according to the given instruction mapping and measure map, or by using the defaults provided by the backend. @@ -55,7 +57,8 @@ def measure(qubits: List[int], meas_map = meas_map or backend.configuration().meas_map except AttributeError as ex: raise exceptions.PulseError( - 'inst_map or meas_map, and backend cannot be None simultaneously') from ex + "inst_map or meas_map, and backend cannot be None simultaneously" + ) from ex if isinstance(meas_map, list): meas_map = utils.format_meas_map(meas_map) @@ -72,7 +75,8 @@ def measure(qubits: List[int], "We could not find a default measurement schedule called '{}'. " "Please provide another name using the 'measure_name' keyword " "argument. For assistance, the instructions which are defined are: " - "{}".format(measure_name, inst_map.instructions)) from ex + "{}".format(measure_name, inst_map.instructions) + ) from ex for time, inst in default_sched.instructions: if inst.channel.index not in qubits: continue @@ -99,5 +103,4 @@ def measure_all(backend) -> Schedule: Returns: A schedule corresponding to the inputs provided. """ - return measure(qubits=list(range(backend.configuration().n_qubits)), - backend=backend) + return measure(qubits=list(range(backend.configuration().n_qubits)), backend=backend) diff --git a/qiskit/pulse/parameter_manager.py b/qiskit/pulse/parameter_manager.py index 6b6435a4191b..b7bd667d27a8 100644 --- a/qiskit/pulse/parameter_manager.py +++ b/qiskit/pulse/parameter_manager.py @@ -85,6 +85,7 @@ class NodeVisitor: method is called. Usually, this method is provided for operating on object defined outside of the Qiskit Pulse module. """ + def visit(self, node: Any): """Visit a node.""" visitor = self._get_visitor(type(node)) @@ -96,7 +97,7 @@ def _get_visitor(self, node_class): return self.generic_visit try: - return getattr(self, f'visit_{node_class.__name__}') + return getattr(self, f"visit_{node_class.__name__}") except AttributeError: # check super class return self._get_visitor(node_class.__base__) @@ -123,6 +124,7 @@ class ParameterSetter(NodeVisitor): This visitor is initialized with a dictionary of parameters to be assigned, and assign values to operands of nodes found. """ + def __init__(self, param_map: Dict[ParameterExpression, ParameterValueType]): self._param_map = param_map @@ -198,7 +200,7 @@ def visit_Channel(self, node: channels.Channel): # validate if not isinstance(new_index, ParameterExpression): if not isinstance(new_index, int) or new_index < 0: - raise PulseError('Channel index must be a nonnegative integer') + raise PulseError("Channel index must be a nonnegative integer") # return new instance to prevent accidentally override timeslots without evaluation return node.__class__(index=new_index) @@ -254,10 +256,10 @@ def _update_parameter_manager(self, node: Union[Schedule, ScheduleBlock]): else: new_parameters.add(parameter) - if hasattr(node, '_parameter_manager'): + if hasattr(node, "_parameter_manager"): node._parameter_manager._parameters = new_parameters else: - raise PulseError(f'Node type {node.__class__.__name__} has no parameter manager.') + raise PulseError(f"Node type {node.__class__.__name__} has no parameter manager.") class ParameterGetter(NodeVisitor): @@ -266,6 +268,7 @@ class ParameterGetter(NodeVisitor): This visitor initializes empty parameter array, and recursively visits nodes and add parameters found to the array. """ + def __init__(self): self.parameters = set() @@ -349,6 +352,7 @@ class ParameterManager: Parameter assignment logic is implemented based on the visitor pattern. Instruction data and its location are not directly associated with this object. """ + def __init__(self): """Create new parameter table for pulse programs.""" self._parameters = set() @@ -376,11 +380,12 @@ def get_parameters(self, parameter_name: str) -> List[Parameter]: """ return [param for param in self.parameters if param.name == parameter_name] - def assign_parameters(self, - pulse_program: Any, - value_dict: Dict[ParameterExpression, ParameterValueType], - inplace: bool = True - ) -> Any: + def assign_parameters( + self, + pulse_program: Any, + value_dict: Dict[ParameterExpression, ParameterValueType], + inplace: bool = True, + ) -> Any: """Modify and return program data with parameters assigned according to the input. Args: diff --git a/qiskit/pulse/parser.py b/qiskit/pulse/parser.py index 65d53901cdb7..562f7215c848 100644 --- a/qiskit/pulse/parser.py +++ b/qiskit/pulse/parser.py @@ -28,24 +28,24 @@ class PulseExpression(ast.NodeTransformer): """Expression parser to evaluate parameter values.""" _math_ops = { - 'acos': cmath.acos, - 'acosh': cmath.acosh, - 'asin': cmath.asin, - 'asinh': cmath.asinh, - 'atan': cmath.atan, - 'atanh': cmath.atanh, - 'cos': cmath.cos, - 'cosh': cmath.cosh, - 'exp': cmath.exp, - 'log': cmath.log, - 'log10': cmath.log10, - 'sin': cmath.sin, - 'sinh': cmath.sinh, - 'sqrt': cmath.sqrt, - 'tan': cmath.tan, - 'tanh': cmath.tanh, - 'pi': cmath.pi, - 'e': cmath.e + "acos": cmath.acos, + "acosh": cmath.acosh, + "asin": cmath.asin, + "asinh": cmath.asinh, + "atan": cmath.atan, + "atanh": cmath.atanh, + "cos": cmath.cos, + "cosh": cmath.cosh, + "exp": cmath.exp, + "log": cmath.log, + "log10": cmath.log10, + "sin": cmath.sin, + "sinh": cmath.sinh, + "sqrt": cmath.sqrt, + "tan": cmath.tan, + "tanh": cmath.tanh, + "pi": cmath.pi, + "e": cmath.e, } """Valid math functions.""" @@ -54,14 +54,11 @@ class PulseExpression(ast.NodeTransformer): ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, - ast.Pow: operator.pow + ast.Pow: operator.pow, } """Valid binary operations.""" - _unary_ops = { - ast.UAdd: operator.pos, - ast.USub: operator.neg - } + _unary_ops = {ast.UAdd: operator.pos, ast.USub: operator.neg} """Valid unary operations.""" def __init__(self, source: Union[str, ast.Expression], partial_binding: bool = False): @@ -82,9 +79,9 @@ def __init__(self, source: Union[str, ast.Expression], partial_binding: bool = F self._tree = source else: try: - self._tree = ast.parse(source, mode='eval') + self._tree = ast.parse(source, mode="eval") except SyntaxError as ex: - raise PulseError(f'{source} is invalid expression.') from ex + raise PulseError(f"{source} is invalid expression.") from ex # parse parameters self.visit(self._tree) @@ -124,11 +121,15 @@ def __call__(self, *args, **kwargs) -> Union[float, complex, ast.Expression]: if key not in self._locals_dict.keys(): self._locals_dict[key] = val else: - raise PulseError("%s got multiple values for argument '%s'" - % (self.__class__.__name__, key)) + raise PulseError( + "%s got multiple values for argument '%s'" + % (self.__class__.__name__, key) + ) else: - raise PulseError("%s got an unexpected keyword argument '%s'" - % (self.__class__.__name__, key)) + raise PulseError( + "%s got an unexpected keyword argument '%s'" + % (self.__class__.__name__, key) + ) expr = self.visit(self._tree) @@ -136,7 +137,7 @@ def __call__(self, *args, **kwargs) -> Union[float, complex, ast.Expression]: if self._partial_binding: return PulseExpression(expr, self._partial_binding) else: - raise PulseError('Parameters %s are not all bound.' % self.params) + raise PulseError("Parameters %s are not all bound." % self.params) return expr.body.n @staticmethod @@ -157,7 +158,7 @@ def _match_ops(opr: ast.AST, opr_dict: Dict, *args) -> Union[float, complex]: for op_type, op_func in opr_dict.items(): if isinstance(opr, op_type): return op_func(*args) - raise PulseError('Operator %s is not supported.' % opr.__class__.__name__) + raise PulseError("Operator %s is not supported." % opr.__class__.__name__) def visit_Expression(self, node: ast.Expression) -> ast.Expression: """Evaluate children nodes of expression. @@ -222,8 +223,8 @@ def visit_Name(self, node: ast.Name) -> Union[ast.Name, ast.Constant]: _val = _val.real except ValueError as ex: raise PulseError( - f'Invalid parameter value {node.id} = {self._locals_dict[node.id]} is ' - 'specified.' + f"Invalid parameter value {node.id} = {self._locals_dict[node.id]} is " + "specified." ) from ex val = ast.Constant(n=_val) return ast.copy_location(val, node) @@ -256,10 +257,12 @@ def visit_BinOp(self, node: ast.BinOp) -> Union[ast.BinOp, ast.Constant]: """ node.left = self.visit(node.left) node.right = self.visit(node.right) - if isinstance(node.left, (ast.Constant, ast.Num)) \ - and isinstance(node.right, (ast.Constant, ast.Num)): - val = ast.Constant(n=self._match_ops(node.op, self._binary_ops, - node.left.n, node.right.n)) + if isinstance(node.left, (ast.Constant, ast.Num)) and isinstance( + node.right, (ast.Constant, ast.Num) + ): + val = ast.Constant( + n=self._match_ops(node.op, self._binary_ops, node.left.n, node.right.n) + ) return ast.copy_location(val, node) return node @@ -276,11 +279,11 @@ def visit_Call(self, node: ast.Call) -> Union[ast.Call, ast.Constant]: PulseError: When unsupported or unsafe function is specified. """ if not isinstance(node.func, ast.Name): - raise PulseError('Unsafe expression is detected.') + raise PulseError("Unsafe expression is detected.") node.args = [self.visit(arg) for arg in node.args] if all(isinstance(arg, (ast.Constant, ast.Num)) for arg in node.args): if node.func.id not in self._math_ops.keys(): - raise PulseError('Function %s is not supported.' % node.func.id) + raise PulseError("Function %s is not supported." % node.func.id) _args = [arg.n for arg in node.args] _val = self._math_ops[node.func.id](*_args) if not _val.imag: @@ -290,7 +293,7 @@ def visit_Call(self, node: ast.Call) -> Union[ast.Call, ast.Constant]: return node def generic_visit(self, node): - raise PulseError('Unsupported node: %s' % node.__class__.__name__) + raise PulseError("Unsupported node: %s" % node.__class__.__name__) def parse_string_expr(source: str, partial_binding: bool = False): @@ -316,7 +319,7 @@ def parse_string_expr(source: str, partial_binding: bool = False): value3 = bound_two(P3=5) """ - subs = [('numpy.', ''), ('np.', ''), ('math.', ''), ('cmath.', '')] + subs = [("numpy.", ""), ("np.", ""), ("math.", ""), ("cmath.", "")] for match, sub in subs: source = source.replace(match, sub) diff --git a/qiskit/pulse/reschedule.py b/qiskit/pulse/reschedule.py index 61616c3b457d..2b1f3d4a60d3 100644 --- a/qiskit/pulse/reschedule.py +++ b/qiskit/pulse/reschedule.py @@ -22,5 +22,6 @@ ) -warnings.warn("The reschedule module has been renamed to transforms. This import path " - "is deprecated.") +warnings.warn( + "The reschedule module has been renamed to transforms. This import path " "is deprecated." +) diff --git a/qiskit/pulse/schedule.py b/qiskit/pulse/schedule.py index 65dd48f8f725..0022cfbe7d19 100644 --- a/qiskit/pulse/schedule.py +++ b/qiskit/pulse/schedule.py @@ -43,10 +43,10 @@ TimeSlots = Dict[Channel, List[Interval]] """List of timeslots occupied by instructions for each channel.""" -ScheduleComponent = Union['Schedule', Instruction] +ScheduleComponent = Union["Schedule", Instruction] """An element that composes a pulse schedule.""" -BlockComponent = Union['ScheduleBlock', Instruction] +BlockComponent = Union["ScheduleBlock", Instruction] """An element that composes a pulse schedule block.""" @@ -100,16 +100,19 @@ class Schedule: [1]: https://arxiv.org/abs/2004.06755 """ + # Prefix to use for auto naming. - prefix = 'sched' + prefix = "sched" # Counter to count instance number. instances_counter = itertools.count() - def __init__(self, - *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponent]], - name: Optional[str] = None, - metadata: Optional[dict] = None): + def __init__( + self, + *schedules: Union[ScheduleComponent, Tuple[int, ScheduleComponent]], + name: Optional[str] = None, + metadata: Optional[dict] = None, + ): """Create an empty schedule. Args: @@ -128,7 +131,7 @@ def __init__(self, if name is None: name = self.prefix + str(next(self.instances_counter)) if sys.platform != "win32" and not is_main_process(): - name += '-{}'.format(mp.current_process().pid) + name += "-{}".format(mp.current_process().pid) self._name = name self._parameter_manager = ParameterManager() @@ -214,9 +217,11 @@ def _children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: scheduled time of each ``NamedValue`` and the component itself. """ - warnings.warn('Schedule._children is now available as the public property ' - 'Schedule.children. Access to this private property is being deprecated.', - DeprecationWarning) + warnings.warn( + "Schedule._children is now available as the public property " + "Schedule.children. Access to this private property is being deprecated.", + DeprecationWarning, + ) return tuple(self.__children) @property @@ -238,10 +243,10 @@ def children(self) -> Tuple[Tuple[int, ScheduleComponent], ...]: @property def instructions(self) -> Tuple[Tuple[int, Instruction]]: """Get the time-ordered instructions from self.""" + def key(time_inst_pair): inst = time_inst_pair[1] - return (time_inst_pair[0], inst.duration, - sorted(chan.name for chan in inst.channels)) + return (time_inst_pair[0], inst.duration, sorted(chan.name for chan in inst.channels)) return tuple(sorted(self._instructions(), key=key)) @@ -298,11 +303,7 @@ def _instructions(self, time: int = 0): for insert_time, child_sched in self.children: yield from child_sched._instructions(time + insert_time) - def shift(self, - time: int, - name: Optional[str] = None, - inplace: bool = False - ) -> 'Schedule': + def shift(self, time: int, name: Optional[str] = None, inplace: bool = False) -> "Schedule": """Return a schedule shifted forward by ``time``. Args: @@ -315,10 +316,7 @@ def shift(self, return self._mutable_shift(time) return self._immutable_shift(time, name=name) - def _immutable_shift(self, - time: int, - name: Optional[str] = None - ) -> 'Schedule': + def _immutable_shift(self, time: int, name: Optional[str] = None) -> "Schedule": """Return a new schedule shifted forward by `time`. Args: @@ -329,9 +327,7 @@ def _immutable_shift(self, name = self.name return Schedule((time, self), name=name, metadata=self.metadata.copy()) - def _mutable_shift(self, - time: int - ) -> 'Schedule': + def _mutable_shift(self, time: int) -> "Schedule": """Return this schedule shifted forward by `time`. Args: @@ -341,13 +337,11 @@ def _mutable_shift(self, PulseError: if ``time`` is not an integer. """ if not isinstance(time, int): - raise PulseError( - "Schedule start time must be an integer.") + raise PulseError("Schedule start time must be an integer.") timeslots = {} for chan, ch_timeslots in self._timeslots.items(): - timeslots[chan] = [(ts[0] + time, ts[1] + time) for - ts in ch_timeslots] + timeslots[chan] = [(ts[0] + time, ts[1] + time) for ts in ch_timeslots] _check_nonnegative_timeslot(timeslots) @@ -356,12 +350,13 @@ def _mutable_shift(self, self.__children = [(orig_time + time, child) for orig_time, child in self.children] return self - def insert(self, - start_time: int, - schedule: ScheduleComponent, - name: Optional[str] = None, - inplace: bool = False - ) -> 'Schedule': + def insert( + self, + start_time: int, + schedule: ScheduleComponent, + name: Optional[str] = None, + inplace: bool = False, + ) -> "Schedule": """Return a new schedule with ``schedule`` inserted into ``self`` at ``start_time``. Args: @@ -375,10 +370,7 @@ def insert(self, return self._mutable_insert(start_time, schedule) return self._immutable_insert(start_time, schedule, name=name) - def _mutable_insert(self, - start_time: int, - schedule: ScheduleComponent - ) -> 'Schedule': + def _mutable_insert(self, start_time: int, schedule: ScheduleComponent) -> "Schedule": """Mutably insert `schedule` into `self` at `start_time`. Args: @@ -390,11 +382,12 @@ def _mutable_insert(self, self._parameter_manager.update_parameter_table(schedule) return self - def _immutable_insert(self, - start_time: int, - schedule: ScheduleComponent, - name: Optional[str] = None, - ) -> 'Schedule': + def _immutable_insert( + self, + start_time: int, + schedule: ScheduleComponent, + name: Optional[str] = None, + ) -> "Schedule": """Return a new schedule with ``schedule`` inserted into ``self`` at ``start_time``. Args: start_time: Time to insert the schedule. @@ -408,9 +401,9 @@ def _immutable_insert(self, new_sched._mutable_insert(start_time, schedule) return new_sched - def append(self, schedule: ScheduleComponent, - name: Optional[str] = None, - inplace: bool = False) -> 'Schedule': + def append( + self, schedule: ScheduleComponent, name: Optional[str] = None, inplace: bool = False + ) -> "Schedule": r"""Return a new schedule with ``schedule`` inserted at the maximum time over all channels shared between ``self`` and ``schedule``. @@ -429,22 +422,27 @@ def append(self, schedule: ScheduleComponent, time = self.ch_stop_time(*common_channels) return self.insert(time, schedule, name=name, inplace=inplace) - def flatten(self) -> 'Schedule': + def flatten(self) -> "Schedule": """Deprecated.""" from qiskit.pulse.transforms import flatten - warnings.warn('`This method is being deprecated. Please use ' - '`qiskit.pulse.transforms.flatten` function with this schedule.', - DeprecationWarning) + warnings.warn( + "`This method is being deprecated. Please use " + "`qiskit.pulse.transforms.flatten` function with this schedule.", + DeprecationWarning, + ) return flatten(self) - def filter(self, *filter_funcs: Callable, - channels: Optional[Iterable[Channel]] = None, - instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, - time_ranges: Optional[Iterable[Tuple[int, int]]] = None, - intervals: Optional[Iterable[Interval]] = None, - check_subroutine: bool = True) -> 'Schedule': + def filter( + self, + *filter_funcs: Callable, + channels: Optional[Iterable[Channel]] = None, + instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, + time_ranges: Optional[Iterable[Tuple[int, int]]] = None, + intervals: Optional[Iterable[Interval]] = None, + check_subroutine: bool = True, + ) -> "Schedule": """Return a new ``Schedule`` with only the instructions from this ``Schedule`` which pass though the provided filters; i.e. an instruction will be retained iff every function in ``filter_funcs`` returns ``True``, the instruction occurs on a channel type contained in @@ -469,15 +467,19 @@ def filter(self, *filter_funcs: Callable, filters = composite_filter(channels, instruction_types, time_ranges, intervals) filters.extend(filter_funcs) - return filter_instructions(self, filters=filters, negate=False, - recurse_subroutines=check_subroutine) + return filter_instructions( + self, filters=filters, negate=False, recurse_subroutines=check_subroutine + ) - def exclude(self, *filter_funcs: Callable, - channels: Optional[Iterable[Channel]] = None, - instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, - time_ranges: Optional[Iterable[Tuple[int, int]]] = None, - intervals: Optional[Iterable[Interval]] = None, - check_subroutine: bool = True) -> 'Schedule': + def exclude( + self, + *filter_funcs: Callable, + channels: Optional[Iterable[Channel]] = None, + instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, + time_ranges: Optional[Iterable[Tuple[int, int]]] = None, + intervals: Optional[Iterable[Interval]] = None, + check_subroutine: bool = True, + ) -> "Schedule": """Return a ``Schedule`` with only the instructions from this Schedule *failing* at least one of the provided filters. This method is the complement of py:meth:`~self.filter`, so that:: @@ -499,12 +501,11 @@ def exclude(self, *filter_funcs: Callable, filters = composite_filter(channels, instruction_types, time_ranges, intervals) filters.extend(filter_funcs) - return filter_instructions(self, filters=filters, negate=True, - recurse_subroutines=check_subroutine) + return filter_instructions( + self, filters=filters, negate=True, recurse_subroutines=check_subroutine + ) - def _add_timeslots(self, - time: int, - schedule: ScheduleComponent) -> None: + def _add_timeslots(self, time: int, schedule: ScheduleComponent) -> None: """Update all time tracking within this schedule based on the given schedule. Args: @@ -525,16 +526,17 @@ def _add_timeslots(self, if time == 0: self._timeslots[channel] = copy.copy(other_timeslots[channel]) else: - self._timeslots[channel] = [(i[0] + time, i[1] + time) - for i in other_timeslots[channel]] + self._timeslots[channel] = [ + (i[0] + time, i[1] + time) for i in other_timeslots[channel] + ] continue for idx, interval in enumerate(other_timeslots[channel]): if interval[0] + time >= self._timeslots[channel][-1][1]: # Can append the remaining intervals self._timeslots[channel].extend( - [(i[0] + time, i[1] + time) - for i in other_timeslots[channel][idx:]]) + [(i[0] + time, i[1] + time) for i in other_timeslots[channel][idx:]] + ) break try: @@ -546,14 +548,19 @@ def _add_timeslots(self, "Schedule(name='{new}') cannot be inserted into Schedule(name='{old}') at " "time {time} because its instruction on channel {ch} scheduled from time " "{t0} to {tf} overlaps with an existing instruction." - "".format(new=schedule.name or '', old=self.name or '', time=time, - ch=channel, t0=interval[0], tf=interval[1])) from ex + "".format( + new=schedule.name or "", + old=self.name or "", + time=time, + ch=channel, + t0=interval[0], + tf=interval[1], + ) + ) from ex _check_nonnegative_timeslot(self._timeslots) - def _remove_timeslots(self, - time: int, - schedule: ScheduleComponent): + def _remove_timeslots(self, time: int, schedule: ScheduleComponent): """Delete the timeslots if present for the respective schedule component. Args: @@ -569,8 +576,7 @@ def _remove_timeslots(self, for channel in schedule.channels: if channel not in self._timeslots: - raise PulseError( - 'The channel {} is not present in the schedule'.format(channel)) + raise PulseError("The channel {} is not present in the schedule".format(channel)) channel_timeslots = self._timeslots[channel] other_timeslots = _get_timeslots(schedule) @@ -586,15 +592,14 @@ def _remove_timeslots(self, raise PulseError( "Cannot find interval ({t0}, {tf}) to remove from " "channel {ch} in Schedule(name='{name}').".format( - ch=channel, t0=interval[0], tf=interval[1], name=schedule.name)) + ch=channel, t0=interval[0], tf=interval[1], name=schedule.name + ) + ) if not channel_timeslots: self._timeslots.pop(channel) - def _replace_timeslots(self, - time: int, - old: ScheduleComponent, - new: ScheduleComponent): + def _replace_timeslots(self, time: int, old: ScheduleComponent, new: ScheduleComponent): """Replace the timeslots of ``old`` if present with the timeslots of ``new``. Args: @@ -611,11 +616,12 @@ def _renew_timeslots(self): for t0, inst in self.instructions: self._add_timeslots(t0, inst) - def replace(self, - old: ScheduleComponent, - new: ScheduleComponent, - inplace: bool = False, - ) -> 'Schedule': + def replace( + self, + old: ScheduleComponent, + new: ScheduleComponent, + inplace: bool = False, + ) -> "Schedule": """Return a ``Schedule`` with the ``old`` instruction replaced with a ``new`` instruction. @@ -691,18 +697,17 @@ def replace(self, return Schedule(*new_children, name=self.name, metadata=self.metadata.copy()) except PulseError as err: raise PulseError( - 'Replacement of {old} with {new} results in ' - 'overlapping instructions.'.format( - old=old, new=new)) from err + "Replacement of {old} with {new} results in " + "overlapping instructions.".format(old=old, new=new) + ) from err def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return self._parameter_manager.is_parameterized() - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType], - inplace: bool = True - ) -> 'Schedule': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType], inplace: bool = True + ) -> "Schedule": """Assign the parameters in this schedule according to the input. Args: @@ -714,13 +719,10 @@ def assign_parameters(self, Schedule with updated parameters. """ return self._parameter_manager.assign_parameters( - pulse_program=self, - value_dict=value_dict, - inplace=inplace + pulse_program=self, value_dict=value_dict, inplace=inplace ) - def get_parameters(self, - parameter_name: str) -> List[Parameter]: + def get_parameters(self, parameter_name: str) -> List[Parameter]: """Get parameter object bound to this schedule by string name. Because different ``Parameter`` objects can have the same name, @@ -738,15 +740,15 @@ def __len__(self) -> int: """Return number of instructions in the schedule.""" return len(self.instructions) - def __add__(self, other: ScheduleComponent) -> 'Schedule': + def __add__(self, other: ScheduleComponent) -> "Schedule": """Return a new schedule with ``other`` inserted within ``self`` at ``start_time``.""" return self.append(other) - def __or__(self, other: ScheduleComponent) -> 'Schedule': + def __or__(self, other: ScheduleComponent) -> "Schedule": """Return a new schedule which is the union of `self` and `other`.""" return self.insert(0, other) - def __lshift__(self, time: int) -> 'Schedule': + def __lshift__(self, time: int) -> "Schedule": """Return a new schedule which is shifted forward by ``time``.""" return self.shift(time) @@ -779,19 +781,17 @@ def __eq__(self, other: ScheduleComponent) -> bool: return False # 3. instruction check - return all(self_inst == other_inst for self_inst, other_inst - in zip(self.instructions, other.instructions)) + return all( + self_inst == other_inst + for self_inst, other_inst in zip(self.instructions, other.instructions) + ) def __repr__(self) -> str: name = format(self._name) if self._name else "" instructions = ", ".join([repr(instr) for instr in self.instructions[:50]]) if len(self.instructions) > 25: instructions += ", ..." - return '{}({}, name="{}")'.format( - self.__class__.__name__, - instructions, - name - ) + return '{}({}, name="{}")'.format(self.__class__.__name__, instructions, name) def _require_schedule_conversion(function: Callable) -> Callable: @@ -799,10 +799,13 @@ def _require_schedule_conversion(function: Callable) -> Callable: This conversation is performed for backward compatibility only if all durations are assigned. """ + @functools.wraps(function) def wrapper(self, *args, **kwargs): from qiskit.pulse.transforms import block_to_schedule + return function(block_to_schedule(self), *args, **kwargs) + return wrapper @@ -869,16 +872,16 @@ class ScheduleBlock: Apart from these differences, the block representation can provide compatible functionality with ``Schedule`` representation. """ + # Prefix to use for auto naming. - prefix = 'block' + prefix = "block" # Counter to count instance number. instances_counter = itertools.count() - def __init__(self, - name: Optional[str] = None, - metadata: Optional[dict] = None, - alignment_context=None): + def __init__( + self, name: Optional[str] = None, metadata: Optional[dict] = None, alignment_context=None + ): """Create an empty schedule block. Args: @@ -898,7 +901,7 @@ def __init__(self, if name is None: name = self.prefix + str(next(self.instances_counter)) if sys.platform != "win32" and not is_main_process(): - name += '-{}'.format(mp.current_process().pid) + name += "-{}".format(mp.current_process().pid) self._name = name self._parameter_manager = ParameterManager() @@ -1042,10 +1045,7 @@ def ch_stop_time(self, *channels: Channel) -> int: return self.ch_stop_time(*channels) @deprecated_functionality - def shift(self, - time: int, - name: Optional[str] = None, - inplace: bool = True): + def shift(self, time: int, name: Optional[str] = None, inplace: bool = True): """This method will be removed. Temporarily added for backward compatibility. .. note:: This method is not supported and being deprecated. @@ -1059,18 +1059,22 @@ def shift(self, Raises: PulseError: When this method is called. This method is not supported. """ - raise PulseError('Method ``ScheduleBlock.shift`` is not supported as this program ' - 'representation does not have the notion of instruction ' - 'time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to ' - 'this program to obtain the ``Schedule`` representation supporting ' - 'this method.') + raise PulseError( + "Method ``ScheduleBlock.shift`` is not supported as this program " + "representation does not have the notion of instruction " + "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " + "this program to obtain the ``Schedule`` representation supporting " + "this method." + ) @deprecated_functionality - def insert(self, - start_time: int, - block: ScheduleComponent, - name: Optional[str] = None, - inplace: bool = True): + def insert( + self, + start_time: int, + block: ScheduleComponent, + name: Optional[str] = None, + inplace: bool = True, + ): """This method will be removed. Temporarily added for backward compatibility. .. note:: This method is not supported and being deprecated. @@ -1085,15 +1089,17 @@ def insert(self, Raises: PulseError: When this method is called. This method is not supported. """ - raise PulseError('Method ``ScheduleBlock.insert`` is not supported as this program ' - 'representation does not have the notion of instruction ' - 'time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to ' - 'this program to obtain the ``Schedule`` representation supporting ' - 'this method.') - - def append(self, block: BlockComponent, - name: Optional[str] = None, - inplace: bool = True) -> 'ScheduleBlock': + raise PulseError( + "Method ``ScheduleBlock.insert`` is not supported as this program " + "representation does not have the notion of instruction " + "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " + "this program to obtain the ``Schedule`` representation supporting " + "this method." + ) + + def append( + self, block: BlockComponent, name: Optional[str] = None, inplace: bool = True + ) -> "ScheduleBlock": """Return a new schedule block with ``block`` appended to the context block. The execution time is automatically assigned when the block is converted into schedule. @@ -1110,8 +1116,10 @@ def append(self, block: BlockComponent, PulseError: When invalid schedule type is specified. """ if not isinstance(block, (ScheduleBlock, Instruction)): - raise PulseError(f'Appended `schedule` {block.__class__.__name__} is invalid type. ' - 'Only `Instruction` and `ScheduleBlock` can be accepted.') + raise PulseError( + f"Appended `schedule` {block.__class__.__name__} is invalid type. " + "Only `Instruction` and `ScheduleBlock` can be accepted." + ) if not inplace: ret_block = copy.deepcopy(self) @@ -1124,12 +1132,15 @@ def append(self, block: BlockComponent, return self - def filter(self, *filter_funcs: List[Callable], - channels: Optional[Iterable[Channel]] = None, - instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, - time_ranges: Optional[Iterable[Tuple[int, int]]] = None, - intervals: Optional[Iterable[Interval]] = None, - check_subroutine: bool = True): + def filter( + self, + *filter_funcs: List[Callable], + channels: Optional[Iterable[Channel]] = None, + instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, + time_ranges: Optional[Iterable[Tuple[int, int]]] = None, + intervals: Optional[Iterable[Interval]] = None, + check_subroutine: bool = True, + ): """Return a new ``Schedule`` with only the instructions from this ``ScheduleBlock`` which pass though the provided filters; i.e. an instruction will be retained iff every function in ``filter_funcs`` returns ``True``, the instruction occurs on @@ -1158,18 +1169,23 @@ def filter(self, *filter_funcs: List[Callable], Raises: PulseError: When this method is called. This method will be supported soon. """ - raise PulseError('Method ``ScheduleBlock.filter`` is not supported as this program ' - 'representation does not have the notion of an explicit instruction ' - 'time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to ' - 'this program to obtain the ``Schedule`` representation supporting ' - 'this method.') - - def exclude(self, *filter_funcs: List[Callable], - channels: Optional[Iterable[Channel]] = None, - instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, - time_ranges: Optional[Iterable[Tuple[int, int]]] = None, - intervals: Optional[Iterable[Interval]] = None, - check_subroutine: bool = True): + raise PulseError( + "Method ``ScheduleBlock.filter`` is not supported as this program " + "representation does not have the notion of an explicit instruction " + "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " + "this program to obtain the ``Schedule`` representation supporting " + "this method." + ) + + def exclude( + self, + *filter_funcs: List[Callable], + channels: Optional[Iterable[Channel]] = None, + instruction_types: Union[Iterable[abc.ABCMeta], abc.ABCMeta] = None, + time_ranges: Optional[Iterable[Tuple[int, int]]] = None, + intervals: Optional[Iterable[Interval]] = None, + check_subroutine: bool = True, + ): """Return a ``Schedule`` with only the instructions from this Schedule *failing* at least one of the provided filters. This method is the complement of py:meth:`~self.filter`, so that:: @@ -1195,17 +1211,20 @@ def exclude(self, *filter_funcs: List[Callable], Raises: PulseError: When this method is called. This method will be supported soon. """ - raise PulseError('Method ``ScheduleBlock.exclude`` is not supported as this program ' - 'representation does not have the notion of instruction ' - 'time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to ' - 'this program to obtain the ``Schedule`` representation supporting ' - 'this method.') - - def replace(self, - old: BlockComponent, - new: BlockComponent, - inplace: bool = True, - ) -> 'ScheduleBlock': + raise PulseError( + "Method ``ScheduleBlock.exclude`` is not supported as this program " + "representation does not have the notion of instruction " + "time. Apply ``qiskit.pulse.transforms.block_to_schedule`` function to " + "this program to obtain the ``Schedule`` representation supporting " + "this method." + ) + + def replace( + self, + old: BlockComponent, + new: BlockComponent, + inplace: bool = True, + ) -> "ScheduleBlock": """Return a ``ScheduleBlock`` with the ``old`` component replaced with a ``new`` component. @@ -1247,10 +1266,9 @@ def is_parameterized(self) -> bool: """Return True iff the instruction is parameterized.""" return self._parameter_manager.is_parameterized() - def assign_parameters(self, - value_dict: Dict[ParameterExpression, ParameterValueType], - inplace: bool = True - ) -> 'ScheduleBlock': + def assign_parameters( + self, value_dict: Dict[ParameterExpression, ParameterValueType], inplace: bool = True + ) -> "ScheduleBlock": """Assign the parameters in this schedule according to the input. Args: @@ -1262,13 +1280,10 @@ def assign_parameters(self, Schedule with updated parameters. """ return self._parameter_manager.assign_parameters( - pulse_program=self, - value_dict=value_dict, - inplace=inplace + pulse_program=self, value_dict=value_dict, inplace=inplace ) - def get_parameters(self, - parameter_name: str) -> List[Parameter]: + def get_parameters(self, parameter_name: str) -> List[Parameter]: """Get parameter object bound to this schedule by string name. Because different ``Parameter`` objects can have the same name, @@ -1286,7 +1301,7 @@ def __len__(self) -> int: """Return number of instructions in the schedule.""" return len(self.blocks) - def __eq__(self, other: 'ScheduleBlock') -> bool: + def __eq__(self, other: "ScheduleBlock") -> bool: """Test if two ScheduleBlocks are equal. Equality is checked by verifying there is an equal instruction at every time @@ -1325,8 +1340,9 @@ def __eq__(self, other: 'ScheduleBlock') -> bool: import retworkx as rx from qiskit.pulse.transforms import block_to_dag - return rx.is_isomorphic_node_match(block_to_dag(self), block_to_dag(other), - lambda x, y: x == y) + return rx.is_isomorphic_node_match( + block_to_dag(self), block_to_dag(other), lambda x, y: x == y + ) def __repr__(self) -> str: name = format(self._name) if self._name else "" @@ -1334,13 +1350,10 @@ def __repr__(self) -> str: if len(self.blocks) > 25: blocks += ", ..." return '{}({}, name="{}", transform={})'.format( - self.__class__.__name__, - blocks, - name, - repr(self.alignment_context) + self.__class__.__name__, blocks, name, repr(self.alignment_context) ) - def __add__(self, other: BlockComponent) -> 'ScheduleBlock': + def __add__(self, other: BlockComponent) -> "ScheduleBlock": """Return a new schedule with ``other`` inserted within ``self`` at ``start_time``.""" return self.append(other) @@ -1352,16 +1365,22 @@ class ParameterizedSchedule: a set of parameters. Calling ``bind`` on the class will return a ``Schedule``. """ - def __init__(self, *schedules, parameters: Optional[Dict[str, Union[float, complex]]] = None, - name: Optional[str] = None): + def __init__( + self, + *schedules, + parameters: Optional[Dict[str, Union[float, complex]]] = None, + name: Optional[str] = None, + ): - warnings.warn('ParameterizedSchedule is deprecated. Use Schedule with ' - 'circuit.Parameter objects.', DeprecationWarning) + warnings.warn( + "ParameterizedSchedule is deprecated. Use Schedule with " "circuit.Parameter objects.", + DeprecationWarning, + ) full_schedules = [] parameterized = [] parameters = parameters or [] - self.name = name or '' + self.name = name or "" # partition schedules into callable and schedules for schedule in schedules: if isinstance(schedule, ParameterizedSchedule): @@ -1372,7 +1391,7 @@ def __init__(self, *schedules, parameters: Optional[Dict[str, Union[float, compl elif isinstance(schedule, Schedule): full_schedules.append(schedule) else: - raise PulseError('Input type: {} not supported'.format(type(schedule))) + raise PulseError("Input type: {} not supported".format(type(schedule))) self._parameterized = tuple(parameterized) self._schedules = tuple(full_schedules) @@ -1383,9 +1402,11 @@ def parameters(self) -> Tuple[str]: """Schedule parameters.""" return self._parameters - def bind_parameters(self, - *args: Union[int, float, complex, ParameterExpression], - **kwargs: Union[int, float, complex, ParameterExpression]) -> Schedule: + def bind_parameters( + self, + *args: Union[int, float, complex, ParameterExpression], + **kwargs: Union[int, float, complex, ParameterExpression], + ) -> Schedule: """Generate the Schedule from params to evaluate command expressions""" bound_schedule = Schedule(name=self.name) schedules = list(self._schedules) @@ -1400,11 +1421,15 @@ def bind_parameters(self, if key not in named_parameters.keys(): named_parameters[key] = val else: - raise PulseError("%s got multiple values for argument '%s'" - % (self.__class__.__name__, key)) + raise PulseError( + "%s got multiple values for argument '%s'" + % (self.__class__.__name__, key) + ) else: - raise PulseError("%s got an unexpected keyword argument '%s'" - % (self.__class__.__name__, key)) + raise PulseError( + "%s got an unexpected keyword argument '%s'" + % (self.__class__.__name__, key) + ) for param_sched in self._parameterized: # recursively call until based callable is reached @@ -1413,8 +1438,7 @@ def bind_parameters(self, else: # assuming no other parameterized instructions predefined = self.parameters - sub_params = {k: v for k, v in named_parameters.items() - if k in predefined} + sub_params = {k: v for k, v in named_parameters.items() if k in predefined} schedules.append(param_sched(**sub_params)) # construct evaluated schedules @@ -1426,8 +1450,11 @@ def bind_parameters(self, return bound_schedule - def __call__(self, *args: Union[int, float, complex, ParameterExpression], - **kwargs: Union[int, float, complex, ParameterExpression]) -> Schedule: + def __call__( + self, + *args: Union[int, float, complex, ParameterExpression], + **kwargs: Union[int, float, complex, ParameterExpression], + ) -> Schedule: return self.bind_parameters(*args, **kwargs) @@ -1440,43 +1467,48 @@ def _common_method(*classes): By using this decorator wisely, we can reduce code maintenance overhead without losing readability of the codebase. """ + def decorator(method): @functools.wraps(method) def wrapper(*args, **kwargs): return method(*args, **kwargs) + for cls in classes: setattr(cls, method.__name__, wrapper) return method + return decorator @_common_method(Schedule, ScheduleBlock) -def draw(self, - dt: Any = None, # deprecated - style: Optional[Dict[str, Any]] = None, - filename: Any = None, # deprecated - interp_method: Any = None, # deprecated - scale: Any = None, # deprecated - channel_scales: Any = None, # deprecated - plot_all: Any = None, # deprecated - plot_range: Any = None, # deprecated - interactive: Any = None, # deprecated - table: Any = None, # deprecated - label: Any = None, # deprecated - framechange: Any = None, # deprecated - channels: Any = None, # deprecated - show_framechange_channels: Any = None, # deprecated - draw_title: Any = None, # deprecated - backend=None, # importing backend causes cyclic import - time_range: Optional[Tuple[int, int]] = None, - time_unit: str = 'dt', - disable_channels: Optional[List[Channel]] = None, - show_snapshot: bool = True, - show_framechange: bool = True, - show_waveform_info: bool = True, - show_barrier: bool = True, - plotter: str = 'mpl2d', - axis: Optional[Any] = None): +def draw( + self, + dt: Any = None, # deprecated + style: Optional[Dict[str, Any]] = None, + filename: Any = None, # deprecated + interp_method: Any = None, # deprecated + scale: Any = None, # deprecated + channel_scales: Any = None, # deprecated + plot_all: Any = None, # deprecated + plot_range: Any = None, # deprecated + interactive: Any = None, # deprecated + table: Any = None, # deprecated + label: Any = None, # deprecated + framechange: Any = None, # deprecated + channels: Any = None, # deprecated + show_framechange_channels: Any = None, # deprecated + draw_title: Any = None, # deprecated + backend=None, # importing backend causes cyclic import + time_range: Optional[Tuple[int, int]] = None, + time_unit: str = "dt", + disable_channels: Optional[List[Channel]] = None, + show_snapshot: bool = True, + show_framechange: bool = True, + show_waveform_info: bool = True, + show_barrier: bool = True, + plotter: str = "mpl2d", + axis: Optional[Any] = None, +): """Plot the schedule. Args: @@ -1534,20 +1566,22 @@ def draw(self, # pylint: disable=cyclic-import, missing-return-type-doc from qiskit.visualization import pulse_drawer_v2, SchedStyle - legacy_args = {'dt': dt, - 'filename': filename, - 'interp_method': interp_method, - 'scale': scale, - 'channel_scales': channel_scales, - 'plot_all': plot_all, - 'plot_range': plot_range, - 'interactive': interactive, - 'table': table, - 'label': label, - 'framechange': framechange, - 'channels': channels, - 'show_framechange_channels': show_framechange_channels, - 'draw_title': draw_title} + legacy_args = { + "dt": dt, + "filename": filename, + "interp_method": interp_method, + "scale": scale, + "channel_scales": channel_scales, + "plot_all": plot_all, + "plot_range": plot_range, + "interactive": interactive, + "table": table, + "label": label, + "framechange": framechange, + "channels": channels, + "show_framechange_channels": show_framechange_channels, + "draw_title": draw_title, + } active_legacy_args = [] for name, legacy_arg in legacy_args.items(): @@ -1555,36 +1589,44 @@ def draw(self, active_legacy_args.append(name) if active_legacy_args: - warnings.warn('Legacy pulse drawer is deprecated. ' - 'Specified arguments {dep_args} are deprecated. ' - 'Please check the API document of new pulse drawer ' - '`qiskit.visualization.pulse_drawer_v2`.' - ''.format(dep_args=', '.join(active_legacy_args)), - DeprecationWarning) + warnings.warn( + "Legacy pulse drawer is deprecated. " + "Specified arguments {dep_args} are deprecated. " + "Please check the API document of new pulse drawer " + "`qiskit.visualization.pulse_drawer_v2`." + "".format(dep_args=", ".join(active_legacy_args)), + DeprecationWarning, + ) if filename: - warnings.warn('File saving is delegated to the plotter software in new drawer. ' - 'If you specify matplotlib plotter family to `plotter` argument, ' - 'you can call `savefig` method with the returned Figure object.', - DeprecationWarning) + warnings.warn( + "File saving is delegated to the plotter software in new drawer. " + "If you specify matplotlib plotter family to `plotter` argument, " + "you can call `savefig` method with the returned Figure object.", + DeprecationWarning, + ) if isinstance(style, SchedStyle): style = None - warnings.warn('Legacy stylesheet is specified. This is ignored in the new drawer. ' - 'Please check the API documentation for this method.') - - return pulse_drawer_v2(program=self, - style=style, - backend=backend, - time_range=time_range, - time_unit=time_unit, - disable_channels=disable_channels, - show_snapshot=show_snapshot, - show_framechange=show_framechange, - show_waveform_info=show_waveform_info, - show_barrier=show_barrier, - plotter=plotter, - axis=axis) + warnings.warn( + "Legacy stylesheet is specified. This is ignored in the new drawer. " + "Please check the API documentation for this method." + ) + + return pulse_drawer_v2( + program=self, + style=style, + backend=backend, + time_range=time_range, + time_unit=time_unit, + disable_channels=disable_channels, + show_snapshot=show_snapshot, + show_framechange=show_framechange, + show_waveform_info=show_waveform_info, + show_barrier=show_barrier, + plotter=plotter, + axis=axis, + ) def _interval_index(intervals: List[Interval], interval: Interval) -> int: @@ -1603,15 +1645,13 @@ def _interval_index(intervals: List[Interval], interval: Interval) -> int: index = _locate_interval_index(intervals, interval) found_interval = intervals[index] if found_interval != interval: - raise PulseError('The interval: {} does not exist in intervals: {}'.format( - interval, intervals - )) + raise PulseError( + "The interval: {} does not exist in intervals: {}".format(interval, intervals) + ) return index -def _locate_interval_index(intervals: List[Interval], - interval: Interval, - index: int = 0) -> int: +def _locate_interval_index(intervals: List[Interval], interval: Interval, index: int = 0) -> int: """Using binary search on start times, find an interval. Args: @@ -1676,8 +1716,8 @@ def _check_nonnegative_timeslot(timeslots: TimeSlots): if chan_timeslots: if chan_timeslots[0][0] < 0: raise PulseError( - "An instruction on {} has a negative " - " starting time.".format(chan)) + "An instruction on {} has a negative " " starting time.".format(chan) + ) def _get_timeslots(schedule: ScheduleComponent) -> TimeSlots: @@ -1696,6 +1736,6 @@ def _get_timeslots(schedule: ScheduleComponent) -> TimeSlots: elif isinstance(schedule, Schedule): timeslots = schedule.timeslots else: - raise PulseError('Invalid schedule type {} is specified.'.format(type(schedule))) + raise PulseError("Invalid schedule type {} is specified.".format(type(schedule))) return timeslots diff --git a/qiskit/pulse/transforms/__init__.py b/qiskit/pulse/transforms/__init__.py index 369514f3622b..33db6d71e4b3 100644 --- a/qiskit/pulse/transforms/__init__.py +++ b/qiskit/pulse/transforms/__init__.py @@ -87,12 +87,10 @@ align_func, align_left, align_right, - align_sequential + align_sequential, ) -from qiskit.pulse.transforms.base_transforms import ( - target_qobj_transform -) +from qiskit.pulse.transforms.base_transforms import target_qobj_transform from qiskit.pulse.transforms.canonicalization import ( add_implicit_acquires, @@ -106,6 +104,4 @@ remove_trivial_barriers, ) -from qiskit.pulse.transforms.dag import ( - block_to_dag -) +from qiskit.pulse.transforms.dag import block_to_dag diff --git a/qiskit/pulse/transforms/alignments.py b/qiskit/pulse/transforms/alignments.py index 13705a521e39..1b26e4d19501 100644 --- a/qiskit/pulse/transforms/alignments.py +++ b/qiskit/pulse/transforms/alignments.py @@ -24,6 +24,7 @@ class AlignmentKind(abc.ABC): """An abstract class for schedule alignment.""" + is_sequential = None def __init__(self): @@ -47,7 +48,7 @@ def align(self, schedule: Schedule) -> Schedule: def to_dict(self) -> Dict[str, Any]: """Returns dictionary to represent this alignment.""" - return {'alignment': self.__class__.__name__} + return {"alignment": self.__class__.__name__} def __eq__(self, other): """Check equality of two transforms.""" @@ -56,9 +57,9 @@ def __eq__(self, other): def __repr__(self): name = self.__class__.__name__ opts = self.to_dict() - opts.pop('alignment') - opts_str = ', '.join(f'{key}={val}' for key, val in opts.items()) - return f'{name}({opts_str})' + opts.pop("alignment") + opts_str = ", ".join(f"{key}={val}" for key, val in opts.items()) + return f"{name}({opts_str})" class AlignLeft(AlignmentKind): @@ -66,6 +67,7 @@ class AlignLeft(AlignmentKind): Instructions are placed at earliest available timeslots. """ + is_sequential = False def align(self, schedule: Schedule) -> Schedule: @@ -101,8 +103,10 @@ def _push_left_append(this: Schedule, other: ScheduleComponent) -> Schedule: this_channels = set(this.channels) other_channels = set(other.channels) shared_channels = list(this_channels & other_channels) - ch_slacks = [this.stop_time - this.ch_stop_time(channel) + other.ch_start_time(channel) - for channel in shared_channels] + ch_slacks = [ + this.stop_time - this.ch_stop_time(channel) + other.ch_start_time(channel) + for channel in shared_channels + ] if ch_slacks: slack_chan = shared_channels[np.argmin(ch_slacks)] @@ -124,6 +128,7 @@ class AlignRight(AlignmentKind): Instructions are placed at latest available timeslots. """ + is_sequential = False def align(self, schedule: Schedule) -> Schedule: @@ -162,8 +167,9 @@ def _push_right_prepend(this: ScheduleComponent, other: ScheduleComponent) -> Sc this_channels = set(this.channels) other_channels = set(other.channels) shared_channels = list(this_channels & other_channels) - ch_slacks = [this.ch_start_time(channel) - other.ch_stop_time(channel) - for channel in shared_channels] + ch_slacks = [ + this.ch_start_time(channel) - other.ch_stop_time(channel) for channel in shared_channels + ] if ch_slacks: insert_time = min(ch_slacks) + other.start_time @@ -185,6 +191,7 @@ class AlignSequential(AlignmentKind): Instructions played on different channels are also arranged in a sequence. No buffer time is inserted in between instructions. """ + is_sequential = True def align(self, schedule: Schedule) -> Schedule: @@ -212,10 +219,10 @@ class AlignEquispaced(AlignmentKind): Instructions played on different channels are also arranged in a sequence. This alignment is convenient to create dynamical decoupling sequences such as PDD. """ + is_sequential = True - def __init__(self, - duration: Union[int, ParameterExpression]): + def __init__(self, duration: Union[int, ParameterExpression]): """Create new equispaced context. Args: @@ -226,7 +233,7 @@ def __init__(self, """ super().__init__() - self._context_params = (duration, ) + self._context_params = (duration,) @property def duration(self): @@ -276,8 +283,7 @@ def align(self, schedule: Schedule) -> Schedule: def to_dict(self) -> Dict[str, Any]: """Returns dictionary to represent this alignment.""" - return {'alignment': self.__class__.__name__, - 'duration': self.duration} + return {"alignment": self.__class__.__name__, "duration": self.duration} class AlignFunc(AlignmentKind): @@ -296,6 +302,7 @@ class AlignFunc(AlignmentKind): def udd10_pos(j): return np.sin(np.pi*j/(2*10 + 2))**2 """ + is_sequential = True def __init__(self, duration: Union[int, ParameterExpression], func: Callable): @@ -312,7 +319,7 @@ def __init__(self, duration: Union[int, ParameterExpression], func: Callable): """ super().__init__() - self._context_params = (duration, ) + self._context_params = (duration,) self._func = func @property @@ -342,7 +349,7 @@ def align(self, schedule: Schedule) -> Schedule: _t_center = self.duration * self._func(ind + 1) _t0 = int(_t_center - 0.5 * child.duration) if _t0 < 0 or _t0 > self.duration: - PulseError('Invalid schedule position t=%d is specified at index=%d' % (_t0, ind)) + PulseError("Invalid schedule position t=%d is specified at index=%d" % (_t0, ind)) aligned.insert(_t0, child, inplace=True) return aligned @@ -352,9 +359,11 @@ def to_dict(self) -> Dict[str, Any]: .. note:: ``func`` is not presented in this dictionary. Just name. """ - return {'alignment': self.__class__.__name__, - 'duration': self._context_params[0], - 'func': self._func.__name__} + return { + "alignment": self.__class__.__name__, + "duration": self._context_params[0], + "func": self._func.__name__, + } @deprecated_functionality diff --git a/qiskit/pulse/transforms/base_transforms.py b/qiskit/pulse/transforms/base_transforms.py index bce609bb4ccb..011c050f578a 100644 --- a/qiskit/pulse/transforms/base_transforms.py +++ b/qiskit/pulse/transforms/base_transforms.py @@ -22,11 +22,10 @@ InstructionSched = Union[Tuple[int, Instruction], Instruction] -def target_qobj_transform(sched: Union[ScheduleBlock, - Schedule, - InstructionSched, - Iterable[InstructionSched]], - remove_directives: bool = True) -> Schedule: +def target_qobj_transform( + sched: Union[ScheduleBlock, Schedule, InstructionSched, Iterable[InstructionSched]], + remove_directives: bool = True, +) -> Schedule: """A basic pulse program transformation for OpenPulse API execution. Args: diff --git a/qiskit/pulse/transforms/canonicalization.py b/qiskit/pulse/transforms/canonicalization.py index 828dfd3ae958..d3fbe852f5c8 100644 --- a/qiskit/pulse/transforms/canonicalization.py +++ b/qiskit/pulse/transforms/canonicalization.py @@ -42,21 +42,24 @@ def block_to_schedule(block: ScheduleBlock) -> Schedule: """ if not block.is_schedulable(): raise UnassignedDurationError( - 'All instruction durations should be assigned before creating `Schedule`.' - 'Please check `.parameters` to find unassigned parameter objects.') + "All instruction durations should be assigned before creating `Schedule`." + "Please check `.parameters` to find unassigned parameter objects." + ) schedule = Schedule(name=block.name, metadata=block.metadata) for op_data in block.blocks: if isinstance(op_data, ScheduleBlock): context_schedule = block_to_schedule(op_data) - if hasattr(op_data.alignment_context, 'duration'): + if hasattr(op_data.alignment_context, "duration"): # context may have local scope duration, e.g. EquispacedAlignment for 1000 dt post_buffer = op_data.alignment_context.duration - context_schedule.duration if post_buffer < 0: - raise PulseError(f'ScheduleBlock {op_data.name} has longer duration than ' - 'the specified context duration ' - f'{context_schedule.duration} > {op_data.duration}.') + raise PulseError( + f"ScheduleBlock {op_data.name} has longer duration than " + "the specified context duration " + f"{context_schedule.duration} > {op_data.duration}." + ) else: post_buffer = 0 schedule.append(context_schedule, inplace=True) @@ -93,11 +96,11 @@ def compress_pulses(schedules: List[Schedule]) -> List[Schedule]: if inst.pulse in existing_pulses: idx = existing_pulses.index(inst.pulse) identical_pulse = existing_pulses[idx] - new_schedule.insert(time, - instructions.Play(identical_pulse, - inst.channel, - inst.name), - inplace=True) + new_schedule.insert( + time, + instructions.Play(identical_pulse, inst.channel, inst.name), + inplace=True, + ) else: existing_pulses.append(inst.pulse) new_schedule.insert(time, inst, inplace=True) @@ -124,7 +127,7 @@ def flatten(program: Schedule) -> Schedule: if isinstance(program, Schedule): return Schedule(*program.instructions, name=program.name, metadata=program.metadata) else: - raise PulseError(f'Invalid input program {program.__class__.__name__} is specified.') + raise PulseError(f"Invalid input program {program.__class__.__name__} is specified.") def inline_subroutines(program: Union[Schedule, ScheduleBlock]) -> Union[Schedule, ScheduleBlock]: @@ -147,7 +150,7 @@ def inline_subroutines(program: Union[Schedule, ScheduleBlock]) -> Union[Schedul elif isinstance(program, ScheduleBlock): return _inline_block(program) else: - raise PulseError(f'Invalid program {program.__class__.__name__} is specified.') + raise PulseError(f"Invalid program {program.__class__.__name__} is specified.") def _inline_schedule(schedule: Schedule) -> Schedule: @@ -155,8 +158,7 @@ def _inline_schedule(schedule: Schedule) -> Schedule: .. note:: If subroutine is ``ScheduleBlock`` it is converted into Schedule to get ``t0``. """ - ret_schedule = Schedule(name=schedule.name, - metadata=schedule.metadata) + ret_schedule = Schedule(name=schedule.name, metadata=schedule.metadata) for t0, inst in schedule.children: # note that schedule.instructions unintentionally flatten the nested schedule. # this should be performed by another transformer node. @@ -183,17 +185,19 @@ def _inline_block(block: ScheduleBlock) -> ScheduleBlock: .. note:: If subroutine is ``Schedule`` the function raises an error. """ - ret_block = ScheduleBlock(alignment_context=block.alignment_context, - name=block.name, - metadata=block.metadata) + ret_block = ScheduleBlock( + alignment_context=block.alignment_context, name=block.name, metadata=block.metadata + ) for inst in block.blocks: if isinstance(inst, instructions.Call): # bind parameter subroutine = inst.assigned_subroutine() if isinstance(subroutine, Schedule): - raise PulseError(f'A subroutine {subroutine.name} is a pulse Schedule. ' - 'This program cannot be inserted into ScheduleBlock because ' - 't0 associated with instruction will be lost.') + raise PulseError( + f"A subroutine {subroutine.name} is a pulse Schedule. " + "This program cannot be inserted into ScheduleBlock because " + "t0 associated with instruction will be lost." + ) # recursively inline the program inline_block = _inline_block(subroutine) ret_block.append(inline_block, inplace=True) @@ -227,20 +231,21 @@ def remove_trivial_barriers(schedule: Schedule) -> Schedule: Returns: schedule: A schedule without trivial barriers """ + def filter_func(inst): - return (isinstance(inst[1], directives.RelativeBarrier) and - len(inst[1].channels) < 2) + return isinstance(inst[1], directives.RelativeBarrier) and len(inst[1].channels) < 2 return schedule.exclude(filter_func) -def align_measures(schedules: Iterable[ScheduleComponent], - inst_map: Optional[InstructionScheduleMap] = None, - cal_gate: str = 'u3', - max_calibration_duration: Optional[int] = None, - align_time: Optional[int] = None, - align_all: Optional[bool] = True, - ) -> List[Schedule]: +def align_measures( + schedules: Iterable[ScheduleComponent], + inst_map: Optional[InstructionScheduleMap] = None, + cal_gate: str = "u3", + max_calibration_duration: Optional[int] = None, + align_time: Optional[int] = None, + align_all: Optional[bool] = True, +) -> List[Schedule]: """Return new schedules where measurements occur at the same physical time. This transformation will align the first :class:`qiskit.pulse.Acquire` on @@ -310,6 +315,7 @@ def align_measures(schedules: Iterable[ScheduleComponent], Raises: PulseError: If the provided alignment time is negative. """ + def get_first_acquire_times(schedules): """Return a list of first acquire times for each schedule.""" acquire_times = [] @@ -318,8 +324,7 @@ def get_first_acquire_times(schedules): qubit_first_acquire_times = defaultdict(lambda: None) for time, inst in schedule.instructions: - if (isinstance(inst, instructions.Acquire) and - inst.channel not in visited_channels): + if isinstance(inst, instructions.Acquire) and inst.channel not in visited_channels: visited_channels.add(inst.channel) qubit_first_acquire_times[inst.channel.index] = time @@ -366,14 +371,17 @@ def get_max_calibration_duration(inst_map, cal_gate): for time, inst in schedule.instructions: measurement_channels = { - chan.index for chan in inst.channels if - isinstance(chan, (chans.MeasureChannel, chans.AcquireChannel)) + chan.index + for chan in inst.channels + if isinstance(chan, (chans.MeasureChannel, chans.AcquireChannel)) } if measurement_channels: sched_first_acquire_times = first_acquire_times[sched_idx] - max_start_time = max(sched_first_acquire_times[chan] - for chan in measurement_channels if - chan in sched_first_acquire_times) + max_start_time = max( + sched_first_acquire_times[chan] + for chan in measurement_channels + if chan in sched_first_acquire_times + ) shift = align_time - max_start_time if shift < 0: @@ -383,16 +391,14 @@ def get_max_calibration_duration(inst_map, cal_gate): "This may result in an instruction being scheduled before t=0 and " "an error being raised." ) - new_schedule.insert(time+shift, inst, inplace=True) + new_schedule.insert(time + shift, inst, inplace=True) new_schedules.append(new_schedule) return new_schedules -def add_implicit_acquires(schedule: ScheduleComponent, - meas_map: List[List[int]] - ) -> Schedule: +def add_implicit_acquires(schedule: ScheduleComponent, meas_map: List[List[int]]) -> Schedule: """Return a new schedule with implicit acquires from the measurement mapping replaced by explicit ones. @@ -412,8 +418,10 @@ def add_implicit_acquires(schedule: ScheduleComponent, for time, inst in schedule.instructions: if isinstance(inst, instructions.Acquire): if inst.mem_slot and inst.mem_slot.index != inst.channel.index: - warnings.warn("One of your acquires was mapped to a memory slot which didn't match" - " the qubit index. I'm relabeling them to match.") + warnings.warn( + "One of your acquires was mapped to a memory slot which didn't match" + " the qubit index. I'm relabeling them to match." + ) # Get the label of all qubits that are measured with the qubit(s) in this instruction all_qubits = [] @@ -423,11 +431,13 @@ def add_implicit_acquires(schedule: ScheduleComponent, # Replace the old acquire instruction by a new one explicitly acquiring all qubits in # the measurement group. for i in all_qubits: - explicit_inst = instructions.Acquire(inst.duration, - chans.AcquireChannel(i), - mem_slot=chans.MemorySlot(i), - kernel=inst.kernel, - discriminator=inst.discriminator) + explicit_inst = instructions.Acquire( + inst.duration, + chans.AcquireChannel(i), + mem_slot=chans.MemorySlot(i), + kernel=inst.kernel, + discriminator=inst.discriminator, + ) if time not in acquire_map: new_schedule.insert(time, explicit_inst, inplace=True) acquire_map = {time: {i}} @@ -440,11 +450,12 @@ def add_implicit_acquires(schedule: ScheduleComponent, return new_schedule -def pad(schedule: Schedule, - channels: Optional[Iterable[chans.Channel]] = None, - until: Optional[int] = None, - inplace: bool = False - ) -> Schedule: +def pad( + schedule: Schedule, + channels: Optional[Iterable[chans.Channel]] = None, + until: Optional[int] = None, + inplace: bool = False, +) -> Schedule: """Pad the input Schedule with ``Delay``s on all unoccupied timeslots until ``schedule.duration`` or ``until`` if not ``None``. @@ -478,14 +489,12 @@ def pad(schedule: Schedule, if interval[0] != curr_time: end_time = min(interval[0], until) schedule = schedule.insert( - curr_time, - instructions.Delay(end_time - curr_time, channel), - inplace=inplace) + curr_time, instructions.Delay(end_time - curr_time, channel), inplace=inplace + ) curr_time = interval[1] if curr_time < until: schedule = schedule.insert( - curr_time, - instructions.Delay(until - curr_time, channel), - inplace=inplace) + curr_time, instructions.Delay(until - curr_time, channel), inplace=inplace + ) return schedule diff --git a/qiskit/pulse/utils.py b/qiskit/pulse/utils.py index 731adc64683a..123d1ccdd99b 100644 --- a/qiskit/pulse/utils.py +++ b/qiskit/pulse/utils.py @@ -41,8 +41,9 @@ def format_meas_map(meas_map: List[List[int]]) -> Dict[int, List[int]]: @functools.lru_cache(maxsize=None) -def format_parameter_value(operand: Union[ParameterExpression] - ) -> Union[ParameterExpression, int, float, complex]: +def format_parameter_value( + operand: Union[ParameterExpression], +) -> Union[ParameterExpression, int, float, complex]: """Convert ParameterExpression into the most suitable data type. Args: @@ -87,26 +88,32 @@ def instruction_duration_validation(duration: int): """ if isinstance(duration, ParameterExpression): raise UnassignedDurationError( - 'Instruction duration {} is not assigned. ' - 'Please bind all durations to an integer value before playing in the Schedule, ' - 'or use ScheduleBlock to align instructions with unassigned duration.' - ''.format(repr(duration))) + "Instruction duration {} is not assigned. " + "Please bind all durations to an integer value before playing in the Schedule, " + "or use ScheduleBlock to align instructions with unassigned duration." + "".format(repr(duration)) + ) if not isinstance(duration, (int, np.integer)) or duration < 0: raise QiskitError( - 'Instruction duration must be a non-negative integer, ' - 'got {} instead.'.format(duration)) + "Instruction duration must be a non-negative integer, " + "got {} instead.".format(duration) + ) def deprecated_functionality(func): """A decorator that raises deprecation warning without showing alternative method.""" + @functools.wraps(func) def wrapper(*args, **kwargs): - warnings.warn(f'Calling {func.__name__} is being deprecated and will be removed soon. ' - 'No alternative method will be provided with this change. ' - 'If there is any practical usage of this functionality, please write ' - 'an issue in Qiskit/qiskit-terra repository.', - category=DeprecationWarning, - stacklevel=2) + warnings.warn( + f"Calling {func.__name__} is being deprecated and will be removed soon. " + "No alternative method will be provided with this change. " + "If there is any practical usage of this functionality, please write " + "an issue in Qiskit/qiskit-terra repository.", + category=DeprecationWarning, + stacklevel=2, + ) return func(*args, **kwargs) + return wrapper diff --git a/qiskit/qasm/__init__.py b/qiskit/qasm/__init__.py index 35adbbd303ca..9010df392331 100644 --- a/qiskit/qasm/__init__.py +++ b/qiskit/qasm/__init__.py @@ -43,8 +43,10 @@ from .qasm import Qasm from .exceptions import QasmError + try: import pygments + HAS_PYGMENTS = True except ImportError: HAS_PYGMENTS = False diff --git a/qiskit/qasm/exceptions.py b/qiskit/qasm/exceptions.py index 76fd19f715ed..ad869c23e656 100644 --- a/qiskit/qasm/exceptions.py +++ b/qiskit/qasm/exceptions.py @@ -23,7 +23,7 @@ class QasmError(QiskitError): def __init__(self, *msg): """Set the error message.""" super().__init__(*msg) - self.msg = ' '.join(msg) + self.msg = " ".join(msg) def __str__(self): """Return the message.""" diff --git a/qiskit/qasm/node/barrier.py b/qiskit/qasm/node/barrier.py index 44aa325bf9e5..7596f6255083 100644 --- a/qiskit/qasm/node/barrier.py +++ b/qiskit/qasm/node/barrier.py @@ -25,11 +25,14 @@ class Barrier(Node): def __init__(self, children): """Create the barrier node.""" - super().__init__('barrier', children, None) + super().__init__("barrier", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Barrier.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Barrier.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "barrier " + self.children[0].qasm() + ";" diff --git a/qiskit/qasm/node/binaryop.py b/qiskit/qasm/node/binaryop.py index b2d27018c978..f35093d4b173 100644 --- a/qiskit/qasm/node/binaryop.py +++ b/qiskit/qasm/node/binaryop.py @@ -27,41 +27,63 @@ class BinaryOp(Node): def __init__(self, children): """Create the binaryop node.""" - super().__init__('binop', children, None) + super().__init__("binop", children, None) def qasm(self, prec=None, nested_scope=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'BinaryOp.qasm(..., prec)\' is no longer ' - 'used and is being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'BinaryOp.qasm(..., prec)' is no longer " + "used and is being deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'BinaryOp.qasm(..., nested_scope)\' is no longer ' - 'used and is being deprecated.', DeprecationWarning, 2) - return "(" + self.children[1].qasm() + self.children[0].value + \ - self.children[2].qasm() + ")" + warnings.warn( + "Parameter 'BinaryOp.qasm(..., nested_scope)' is no longer " + "used and is being deprecated.", + DeprecationWarning, + 2, + ) + return ( + "(" + self.children[1].qasm() + self.children[0].value + self.children[2].qasm() + ")" + ) def latex(self, prec=None, nested_scope=None): """Return the corresponding math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'BinaryOp.latex(..., prec)\' is no longer used ' - 'and is being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'BinaryOp.latex(..., prec)' is no longer used " + "and is being deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'BinaryOp.latex(..., nested_scope)\' is no longer used ' - 'and is being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'BinaryOp.latex(..., nested_scope)' is no longer used " + "and is being deprecated.", + DeprecationWarning, + 2, + ) try: from pylatexenc.latexencode import utf8tolatex except ImportError as ex: - raise ImportError("To export latex from qasm " - "pylatexenc needs to be installed. Run " - "'pip install pylatexenc' before using this " - "method.") from ex + raise ImportError( + "To export latex from qasm " + "pylatexenc needs to be installed. Run " + "'pip install pylatexenc' before using this " + "method." + ) from ex return utf8tolatex(self.sym()) def real(self, nested_scope=None): """Return the correspond floating point number.""" if nested_scope is not None: - warnings.warn('Parameter \'BinaryOp.real(..., nested_scope)\' is no longer used and is' - ' being deprecated.', DeprecationWarning) + warnings.warn( + "Parameter 'BinaryOp.real(..., nested_scope)' is no longer used and is" + " being deprecated.", + DeprecationWarning, + ) operation = self.children[0].operation() lhs = self.children[1].real() rhs = self.children[2].real() diff --git a/qiskit/qasm/node/binaryoperator.py b/qiskit/qasm/node/binaryoperator.py index 052aa3d3fbff..b2650cc6e080 100644 --- a/qiskit/qasm/node/binaryoperator.py +++ b/qiskit/qasm/node/binaryoperator.py @@ -20,11 +20,11 @@ VALID_OPERATORS = { - '+': operator.add, - '-': operator.sub, - '*': operator.mul, - '/': operator.truediv, - '^': operator.pow + "+": operator.add, + "-": operator.sub, + "*": operator.mul, + "/": operator.truediv, + "^": operator.pow, } @@ -33,9 +33,10 @@ class BinaryOperator(Node): This node has no children. The data is in the value field. """ + def __init__(self, operation): """Create the operator node.""" - super().__init__('operator', None, None) + super().__init__("operator", None, None) self.value = operation def operation(self): @@ -50,6 +51,10 @@ def operation(self): def qasm(self, prec=None): """Return the QASM representation.""" if prec is not None: - warnings.warn('Parameter \'BinaryOperator.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'BinaryOperator.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) return self.value diff --git a/qiskit/qasm/node/cnot.py b/qiskit/qasm/node/cnot.py index 49b5d674936d..218ef15af713 100644 --- a/qiskit/qasm/node/cnot.py +++ b/qiskit/qasm/node/cnot.py @@ -26,12 +26,14 @@ class Cnot(Node): def __init__(self, children): """Create the cnot node.""" - super().__init__('cnot', children, None) + super().__init__("cnot", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Cnot.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) - return "CX " + self.children[0].qasm() + "," + \ - self.children[1].qasm() + ";" + warnings.warn( + "Parameter 'Cnot.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) + return "CX " + self.children[0].qasm() + "," + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/node/creg.py b/qiskit/qasm/node/creg.py index c45dfc992e98..c153978f17d2 100644 --- a/qiskit/qasm/node/creg.py +++ b/qiskit/qasm/node/creg.py @@ -24,7 +24,7 @@ class Creg(Node): def __init__(self, children): """Create the creg node.""" - super().__init__('creg', children, None) + super().__init__("creg", children, None) # This is the indexed id, the full "id[n]" object self.id = children[0] # pylint: disable=invalid-name # Name of the creg @@ -38,13 +38,16 @@ def __init__(self, children): def to_string(self, indent): """Print the node data, with indent.""" - ind = indent * ' ' - print(ind, 'creg') + ind = indent * " " + print(ind, "creg") self.children[0].to_string(indent + 3) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Creg.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Creg.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "creg " + self.id.qasm() + ";" diff --git a/qiskit/qasm/node/customunitary.py b/qiskit/qasm/node/customunitary.py index 36f3719be230..c317e8a5f17a 100644 --- a/qiskit/qasm/node/customunitary.py +++ b/qiskit/qasm/node/customunitary.py @@ -32,7 +32,7 @@ class CustomUnitary(Node): def __init__(self, children): """Create the custom gate node.""" - super().__init__('custom_unitary', children, None) + super().__init__("custom_unitary", children, None) self.id = children[0] # pylint: disable=invalid-name self.name = self.id.name if len(children) == 3: @@ -45,8 +45,12 @@ def __init__(self, children): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'CustomUnitary.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'CustomUnitary.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) string = self.name if self.arguments is not None: string += "(" + self.arguments.qasm() + ")" diff --git a/qiskit/qasm/node/expressionlist.py b/qiskit/qasm/node/expressionlist.py index d70ac43be399..3e81af165462 100644 --- a/qiskit/qasm/node/expressionlist.py +++ b/qiskit/qasm/node/expressionlist.py @@ -24,7 +24,7 @@ class ExpressionList(Node): def __init__(self, children): """Create the expression list node.""" - super().__init__('expression_list', children, None) + super().__init__("expression_list", children, None) def size(self): """Return the number of expressions.""" @@ -33,6 +33,10 @@ def size(self): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'ExpressionList.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'ExpressionList.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/external.py b/qiskit/qasm/node/external.py index 62517bab1a18..5ebf127fd72f 100644 --- a/qiskit/qasm/node/external.py +++ b/qiskit/qasm/node/external.py @@ -28,30 +28,44 @@ class External(Node): def __init__(self, children): """Create the external node.""" - super().__init__('external', children, None) + super().__init__("external", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'External.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'External.qasm(..., prec)' is no longer used and is being " + "deprecated.", + DeprecationWarning, + 2, + ) return self.children[0].qasm() + "(" + self.children[1].qasm() + ")" def latex(self, prec=None, nested_scope=None): """Return the corresponding math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'External.latex(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'External.latex(..., prec)' is no longer used and is being " + "deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'External.latex(..., nested_scope)\' is no longer used and ' - 'is being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'External.latex(..., nested_scope)' is no longer used and " + "is being deprecated.", + DeprecationWarning, + 2, + ) try: from pylatexenc.latexencode import utf8tolatex except ImportError as ex: - raise ImportError("To export latex from qasm " - "pylatexenc needs to be installed. Run " - "'pip install pylatexenc' before using this " - "method.") from ex + raise ImportError( + "To export latex from qasm " + "pylatexenc needs to be installed. Run " + "'pip install pylatexenc' before using this " + "method." + ) from ex return utf8tolatex(self.sym()) def real(self, nested_scope=None): @@ -59,15 +73,15 @@ def real(self, nested_scope=None): op = self.children[0].name expr = self.children[1] dispatch = { - 'sin': np.sin, - 'cos': np.cos, - 'tan': np.tan, - 'asin': np.arcsin, - 'acos': np.arccos, - 'atan': np.arctan, - 'exp': np.exp, - 'ln': np.log, - 'sqrt': np.sqrt + "sin": np.sin, + "cos": np.cos, + "tan": np.tan, + "asin": np.arcsin, + "acos": np.arccos, + "atan": np.arctan, + "exp": np.exp, + "ln": np.log, + "sqrt": np.sqrt, } if op in dispatch: arg = expr.real(nested_scope) @@ -80,15 +94,15 @@ def sym(self, nested_scope=None): op = self.children[0].name expr = self.children[1] dispatch = { - 'sin': np.sin, - 'cos': np.cos, - 'tan': np.tan, - 'asin': np.arcsin, - 'acos': np.arccos, - 'atan': np.arctan, - 'exp': np.exp, - 'ln': np.log, - 'sqrt': np.sqrt + "sin": np.sin, + "cos": np.cos, + "tan": np.tan, + "asin": np.arcsin, + "acos": np.arccos, + "atan": np.arctan, + "exp": np.exp, + "ln": np.log, + "sqrt": np.sqrt, } if op in dispatch: arg = expr.sym(nested_scope) diff --git a/qiskit/qasm/node/format.py b/qiskit/qasm/node/format.py index b1967a927b8f..ba4bff45aafb 100644 --- a/qiskit/qasm/node/format.py +++ b/qiskit/qasm/node/format.py @@ -24,7 +24,7 @@ class Format(Node): def __init__(self, value): """Create the version node.""" super().__init__("format", None, None) - parts = re.match(r'(\w+)\s+(\d+)\.(\d+)', value) + parts = re.match(r"(\w+)\s+(\d+)\.(\d+)", value) self.language = parts.group(1) self.majorversion = parts.group(2) self.minorversion = parts.group(3) @@ -36,6 +36,9 @@ def version(self): def qasm(self, prec=None): """Return the corresponding format string.""" if prec is not None: - warnings.warn('Parameter \'Format.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Format.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "%s %s;" % (self.language, self.version()) diff --git a/qiskit/qasm/node/gate.py b/qiskit/qasm/node/gate.py index a64cb9d7273e..6a93b0aef4e9 100644 --- a/qiskit/qasm/node/gate.py +++ b/qiskit/qasm/node/gate.py @@ -28,7 +28,7 @@ class Gate(Node): def __init__(self, children): """Create the gate node.""" - super().__init__('gate', children, None) + super().__init__("gate", children, None) self.id = children[0] # pylint: disable=invalid-name # The next three fields are required by the symbtab self.name = self.id.name @@ -57,8 +57,11 @@ def n_bits(self): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Gate.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Gate.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) string = "gate " + self.name if self.arguments is not None: string += "(" + self.arguments.qasm() + ")" diff --git a/qiskit/qasm/node/gatebody.py b/qiskit/qasm/node/gatebody.py index c0c7b9ce078a..a1f5db05d647 100644 --- a/qiskit/qasm/node/gatebody.py +++ b/qiskit/qasm/node/gatebody.py @@ -25,13 +25,17 @@ class GateBody(Node): def __init__(self, children): """Create the gatebody node.""" - super().__init__('gate_body', children, None) + super().__init__("gate_body", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'GateBody.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'GateBody.qasm(..., prec)' is no longer used and is being " + "deprecated.", + DeprecationWarning, + 2, + ) string = "" for children in self.children: string += " " + children.qasm() + "\n" diff --git a/qiskit/qasm/node/id.py b/qiskit/qasm/node/id.py index 0c77043e8b9e..e63f16cbbdc0 100644 --- a/qiskit/qasm/node/id.py +++ b/qiskit/qasm/node/id.py @@ -38,45 +38,55 @@ def __init__(self, id, line, file): def to_string(self, indent): """Print the node with indent.""" - ind = indent * ' ' - print(ind, 'id', self.name) + ind = indent * " " + print(ind, "id", self.name) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Id.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Id.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return self.name def latex(self, prec=None, nested_scope=None): """Return the correspond math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'Id.latex(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Id.latex(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) if not nested_scope: return "\textrm{" + self.name + "}" else: if self.name not in nested_scope[-1]: - raise NodeException("Expected local parameter name: ", - "name=%s, " % self.name, - "line=%s, " % self.line, - "file=%s" % self.file) + raise NodeException( + "Expected local parameter name: ", + "name=%s, " % self.name, + "line=%s, " % self.line, + "file=%s" % self.file, + ) return nested_scope[-1][self.name].latex(nested_scope[0:-1]) def sym(self, nested_scope=None): """Return the correspond symbolic number.""" if not nested_scope or self.name not in nested_scope[-1]: - raise NodeException("Expected local parameter name: ", - "name=%s, line=%s, file=%s" % ( - self.name, self.line, self.file)) + raise NodeException( + "Expected local parameter name: ", + "name=%s, line=%s, file=%s" % (self.name, self.line, self.file), + ) return nested_scope[-1][self.name].sym(nested_scope[0:-1]) def real(self, nested_scope=None): """Return the correspond floating point number.""" if not nested_scope or self.name not in nested_scope[-1]: - raise NodeException("Expected local parameter name: ", - "name=%s, line=%s, file=%s" % ( - self.name, self.line, self.file)) + raise NodeException( + "Expected local parameter name: ", + "name=%s, line=%s, file=%s" % (self.name, self.line, self.file), + ) return nested_scope[-1][self.name].real(nested_scope[0:-1]) diff --git a/qiskit/qasm/node/idlist.py b/qiskit/qasm/node/idlist.py index 7da609970d9a..6b0b801f09ff 100644 --- a/qiskit/qasm/node/idlist.py +++ b/qiskit/qasm/node/idlist.py @@ -24,7 +24,7 @@ class IdList(Node): def __init__(self, children): """Create the idlist node.""" - super().__init__('id_list', children, None) + super().__init__("id_list", children, None) def size(self): """Return the length of the list.""" @@ -33,7 +33,9 @@ def size(self): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'IdList.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) - return ",".join([self.children[j].qasm() - for j in range(self.size())]) + warnings.warn( + "Parameter 'IdList.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) + return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/if_.py b/qiskit/qasm/node/if_.py index 41977f0d3b51..aac8fcebdab7 100644 --- a/qiskit/qasm/node/if_.py +++ b/qiskit/qasm/node/if_.py @@ -27,13 +27,21 @@ class If(Node): def __init__(self, children): """Create the if node.""" - super().__init__('if', children, None) + super().__init__("if", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'If.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) - return "if(" + self.children[0].qasm() + "==" \ - + str(self.children[1].value) + ") " + \ - self.children[2].qasm() + warnings.warn( + "Parameter 'If.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) + return ( + "if(" + + self.children[0].qasm() + + "==" + + str(self.children[1].value) + + ") " + + self.children[2].qasm() + ) diff --git a/qiskit/qasm/node/indexedid.py b/qiskit/qasm/node/indexedid.py index 5cf42c42fed6..dbcc2b84b343 100644 --- a/qiskit/qasm/node/indexedid.py +++ b/qiskit/qasm/node/indexedid.py @@ -26,7 +26,7 @@ class IndexedId(Node): def __init__(self, children): """Create the indexed id node.""" - super().__init__('indexed_id', children, None) + super().__init__("indexed_id", children, None) self.id = children[0] # pylint: disable=invalid-name self.name = self.id.name self.line = self.id.line @@ -35,12 +35,16 @@ def __init__(self, children): def to_string(self, indent): """Print with indent.""" - ind = indent * ' ' - print(ind, 'indexed_id', self.name, self.index) + ind = indent * " " + print(ind, "indexed_id", self.name, self.index) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'IndexedId.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'IndexedId.qasm(..., prec)' is no longer used and is being " + "deprecated.", + DeprecationWarning, + 2, + ) return self.name + "[%d]" % self.index diff --git a/qiskit/qasm/node/intnode.py b/qiskit/qasm/node/intnode.py index 7237f22946e3..177ceab3eeea 100644 --- a/qiskit/qasm/node/intnode.py +++ b/qiskit/qasm/node/intnode.py @@ -31,24 +31,34 @@ def __init__(self, id): def to_string(self, indent): """Print with indent.""" - ind = indent * ' ' - print(ind, 'int', self.value) + ind = indent * " " + print(ind, "int", self.value) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Int.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Int.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "%d" % self.value def latex(self, prec=None, nested_scope=None): """Return the corresponding math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'Int.latex(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Int.latex(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'Int.latex(..., nested_scope)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Int.latex(..., nested_scope)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) return "%d" % self.value def sym(self, nested_scope=None): diff --git a/qiskit/qasm/node/measure.py b/qiskit/qasm/node/measure.py index 0ba50bf6ef55..d0c23d8b5319 100644 --- a/qiskit/qasm/node/measure.py +++ b/qiskit/qasm/node/measure.py @@ -25,12 +25,14 @@ class Measure(Node): def __init__(self, children): """Create the measure node.""" - super().__init__('measure', children, None) + super().__init__("measure", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Measure.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) - return "measure " + self.children[0].qasm() + " -> " + \ - self.children[1].qasm() + ";" + warnings.warn( + "Parameter 'Measure.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) + return "measure " + self.children[0].qasm() + " -> " + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/node/node.py b/qiskit/qasm/node/node.py index 41c1dbbce87d..6f64dfb1343f 100644 --- a/qiskit/qasm/node/node.py +++ b/qiskit/qasm/node/node.py @@ -38,13 +38,13 @@ def add_child(self, node): def to_string(self, indent): """Print with indent.""" - ind = indent * ' ' + ind = indent * " " if self.root: - print(ind, self.type, '---', self.root) + print(ind, self.type, "---", self.root) else: print(ind, self.type) indent = indent + 3 - ind = indent * ' ' + ind = indent * " " for children in self.children: if children is None: print("OOPS! type of parent is", type(self)) diff --git a/qiskit/qasm/node/nodeexception.py b/qiskit/qasm/node/nodeexception.py index e51cf7a1af2a..d351f499c929 100644 --- a/qiskit/qasm/node/nodeexception.py +++ b/qiskit/qasm/node/nodeexception.py @@ -19,7 +19,7 @@ class NodeException(Exception): def __init__(self, *msg): """Set the error message.""" super().__init__(*msg) - self.msg = ' '.join(msg) + self.msg = " ".join(msg) def __str__(self): """Return the message.""" diff --git a/qiskit/qasm/node/opaque.py b/qiskit/qasm/node/opaque.py index 9ce8a9b5df42..d374661cab3d 100644 --- a/qiskit/qasm/node/opaque.py +++ b/qiskit/qasm/node/opaque.py @@ -28,7 +28,7 @@ class Opaque(Node): def __init__(self, children): """Create the opaque gate node.""" - super().__init__('opaque', children, None) + super().__init__("opaque", children, None) self.id = children[0] # pylint: disable=invalid-name # The next three fields are required by the symbtab self.name = self.id.name @@ -54,8 +54,11 @@ def n_bits(self): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Opaque.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Opaque.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) string = "opaque %s" % self.name if self.arguments is not None: string += "(" + self.arguments.qasm() + ")" diff --git a/qiskit/qasm/node/prefix.py b/qiskit/qasm/node/prefix.py index 2340aa8f11f7..d28c4b9207b7 100644 --- a/qiskit/qasm/node/prefix.py +++ b/qiskit/qasm/node/prefix.py @@ -26,37 +26,52 @@ class Prefix(Node): def __init__(self, children): """Create the prefix node.""" - super().__init__('prefix', children, None) + super().__init__("prefix", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Prefix.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Prefix.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return self.children[0].value + "(" + self.children[1].qasm() + ")" def latex(self, prec=None, nested_scope=None): """Return the corresponding math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'Prefix.latex(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Prefix.latex(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'Prefix.latex(..., nested_scope)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Prefix.latex(..., nested_scope)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) try: from pylatexenc.latexencode import utf8tolatex except ImportError as ex: - raise ImportError("To export latex from qasm " - "pylatexenc needs to be installed. Run " - "'pip install pylatexenc' before using this " - "method.") from ex + raise ImportError( + "To export latex from qasm " + "pylatexenc needs to be installed. Run " + "'pip install pylatexenc' before using this " + "method." + ) from ex return utf8tolatex(self.sym()) def real(self, nested_scope=None): """Return the correspond floating point number.""" if nested_scope is not None: - warnings.warn('Parameter \'Prefix.real(..., nested_scope)\' is no longer ' - 'used and is being deprecated.', DeprecationWarning) + warnings.warn( + "Parameter 'Prefix.real(..., nested_scope)' is no longer " + "used and is being deprecated.", + DeprecationWarning, + ) operation = self.children[0].operation() expr = self.children[1].real() return operation(expr) diff --git a/qiskit/qasm/node/primarylist.py b/qiskit/qasm/node/primarylist.py index fdbdf334f981..858cdb7f2dde 100644 --- a/qiskit/qasm/node/primarylist.py +++ b/qiskit/qasm/node/primarylist.py @@ -24,7 +24,7 @@ class PrimaryList(Node): def __init__(self, children): """Create the primarylist node.""" - super().__init__('primary_list', children, None) + super().__init__("primary_list", children, None) def size(self): """Return the size of the list.""" @@ -33,7 +33,10 @@ def size(self): def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'PrimaryList.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) - return ",".join([self.children[j].qasm() - for j in range(self.size())]) + warnings.warn( + "Parameter 'PrimaryList.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) + return ",".join([self.children[j].qasm() for j in range(self.size())]) diff --git a/qiskit/qasm/node/program.py b/qiskit/qasm/node/program.py index 65d30f21d98f..6217333e95fe 100644 --- a/qiskit/qasm/node/program.py +++ b/qiskit/qasm/node/program.py @@ -24,13 +24,16 @@ class Program(Node): def __init__(self, children): """Create the program node.""" - super().__init__('program', children, None) + super().__init__("program", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Program.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Program.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) string = "" for children in self.children: string += children.qasm() + "\n" diff --git a/qiskit/qasm/node/qreg.py b/qiskit/qasm/node/qreg.py index 8005b44e3c2f..931845c7360f 100644 --- a/qiskit/qasm/node/qreg.py +++ b/qiskit/qasm/node/qreg.py @@ -24,7 +24,7 @@ class Qreg(Node): def __init__(self, children): """Create the qreg node.""" - super().__init__('qreg', children, None) + super().__init__("qreg", children, None) # This is the indexed id, the full "id[n]" object self.id = children[0] # pylint: disable=invalid-name # Name of the qreg @@ -38,13 +38,16 @@ def __init__(self, children): def to_string(self, indent): """Print the node data, with indent.""" - ind = indent * ' ' - print(ind, 'qreg') + ind = indent * " " + print(ind, "qreg") self.children[0].to_string(indent + 3) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Qreg.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Qreg.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "qreg " + self.id.qasm() + ";" diff --git a/qiskit/qasm/node/real.py b/qiskit/qasm/node/real.py index 1f9f6306b834..8ff35a6c5579 100644 --- a/qiskit/qasm/node/real.py +++ b/qiskit/qasm/node/real.py @@ -32,14 +32,17 @@ def __init__(self, id): def to_string(self, indent): """Print with indent.""" - ind = indent * ' ' - print(ind, 'real', self.value) + ind = indent * " " + print(ind, "real", self.value) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Real.qasm(..., prec)\' is no longer used and' - ' is being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Real.qasm(..., prec)' is no longer used and" " is being deprecated.", + DeprecationWarning, + 2, + ) if self.value == np.pi: return "pi" @@ -48,18 +51,27 @@ def qasm(self, prec=None): def latex(self, prec=None, nested_scope=None): """Return the corresponding math mode latex string.""" if prec is not None: - warnings.warn('Parameter \'Real.latex(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Real.latex(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) if nested_scope is not None: - warnings.warn('Parameter \'Real.latex(..., nested_scope)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Real.latex(..., nested_scope)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) try: from pylatexenc.latexencode import utf8tolatex except ImportError as ex: - raise ImportError("To export latex from qasm " - "pylatexenc needs to be installed. Run " - "'pip install pylatexenc' before using this " - "method.") from ex + raise ImportError( + "To export latex from qasm " + "pylatexenc needs to be installed. Run " + "'pip install pylatexenc' before using this " + "method." + ) from ex return utf8tolatex(self.value) def sym(self, nested_scope=None): diff --git a/qiskit/qasm/node/reset.py b/qiskit/qasm/node/reset.py index 638db224ae1c..47a0dae084bd 100644 --- a/qiskit/qasm/node/reset.py +++ b/qiskit/qasm/node/reset.py @@ -24,11 +24,14 @@ class Reset(Node): def __init__(self, children): """Create the reset node.""" - super().__init__('reset', children, None) + super().__init__("reset", children, None) def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'Reset.qasm(..., prec)\' is no longer used and is being ' - 'deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'Reset.qasm(..., prec)' is no longer used and is being " "deprecated.", + DeprecationWarning, + 2, + ) return "reset " + self.children[0].qasm() + ";" diff --git a/qiskit/qasm/node/unaryoperator.py b/qiskit/qasm/node/unaryoperator.py index 46356dddd500..b091ceae0011 100644 --- a/qiskit/qasm/node/unaryoperator.py +++ b/qiskit/qasm/node/unaryoperator.py @@ -20,8 +20,8 @@ VALID_OPERATORS = { - '+': operator.pos, - '-': operator.neg, + "+": operator.pos, + "-": operator.neg, } @@ -30,9 +30,10 @@ class UnaryOperator(Node): This node has no children. The data is in the value field. """ + def __init__(self, operation): """Create the operator node.""" - super().__init__('unary_operator', None, None) + super().__init__("unary_operator", None, None) self.value = operation def operation(self): @@ -47,6 +48,10 @@ def operation(self): def qasm(self, prec=None): """Return QASM representation.""" if prec is not None: - warnings.warn('Parameter \'UnaryOperator.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) + warnings.warn( + "Parameter 'UnaryOperator.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) return self.value diff --git a/qiskit/qasm/node/universalunitary.py b/qiskit/qasm/node/universalunitary.py index c5dd8f3da998..6a20d959ec0a 100644 --- a/qiskit/qasm/node/universalunitary.py +++ b/qiskit/qasm/node/universalunitary.py @@ -25,14 +25,17 @@ class UniversalUnitary(Node): def __init__(self, children): """Create the U node.""" - super().__init__('universal_unitary', children) + super().__init__("universal_unitary", children) self.arguments = children[0] self.bitlist = children[1] def qasm(self, prec=None): """Return the corresponding OPENQASM string.""" if prec is not None: - warnings.warn('Parameter \'UniversalUnitary.qasm(..., prec)\' is no longer used and is ' - 'being deprecated.', DeprecationWarning, 2) - return "U(" + self.children[0].qasm() + ") " + \ - self.children[1].qasm() + ";" + warnings.warn( + "Parameter 'UniversalUnitary.qasm(..., prec)' is no longer used and is " + "being deprecated.", + DeprecationWarning, + 2, + ) + return "U(" + self.children[0].qasm() + ") " + self.children[1].qasm() + ";" diff --git a/qiskit/qasm/pygments/__init__.py b/qiskit/qasm/pygments/__init__.py index d6eb5a51fe58..a2c605640e47 100644 --- a/qiskit/qasm/pygments/__init__.py +++ b/qiskit/qasm/pygments/__init__.py @@ -26,6 +26,7 @@ """ try: import pygments + HAS_PYGMENTS = True except ImportError: HAS_PYGMENTS = False diff --git a/qiskit/qasm/pygments/lexer.py b/qiskit/qasm/pygments/lexer.py index cb005b5f6271..f78ba3c8b045 100644 --- a/qiskit/qasm/pygments/lexer.py +++ b/qiskit/qasm/pygments/lexer.py @@ -14,96 +14,126 @@ try: from pygments.lexer import RegexLexer - from pygments.token import (Comment, String, Keyword, - Name, Number, Text) + from pygments.token import Comment, String, Keyword, Name, Number, Text from pygments.style import Style except ImportError as ex: - raise ImportError("To use 'qiskit.qasm.pygments' pygments>2.4 must be " - 'installed. To install run "pip install pygments".') from ex + raise ImportError( + "To use 'qiskit.qasm.pygments' pygments>2.4 must be " + 'installed. To install run "pip install pygments".' + ) from ex class QasmTerminalStyle(Style): """A style for OpenQasm in a Terminal env (e.g. Jupyter print).""" + styles = { - String: 'ansibrightred', - Number: 'ansibrightcyan', - Keyword.Reserved: 'ansibrightgreen', - Keyword.Declaration: 'ansibrightgreen', - Keyword.Type: 'ansibrightmagenta', - Name.Builtin: 'ansibrightblue', - Name.Function: 'ansibrightyellow'} + String: "ansibrightred", + Number: "ansibrightcyan", + Keyword.Reserved: "ansibrightgreen", + Keyword.Declaration: "ansibrightgreen", + Keyword.Type: "ansibrightmagenta", + Name.Builtin: "ansibrightblue", + Name.Function: "ansibrightyellow", + } class QasmHTMLStyle(Style): """A style for OpenQasm in a HTML env (e.g. Jupyter widget).""" + styles = { - String: 'ansired', - Number: 'ansicyan', - Keyword.Reserved: 'ansigreen', - Keyword.Declaration: 'ansigreen', - Keyword.Type: 'ansimagenta', - Name.Builtin: 'ansiblue', - Name.Function: 'ansiyellow'} + String: "ansired", + Number: "ansicyan", + Keyword.Reserved: "ansigreen", + Keyword.Declaration: "ansigreen", + Keyword.Type: "ansimagenta", + Name.Builtin: "ansiblue", + Name.Function: "ansiyellow", + } class OpenQASMLexer(RegexLexer): """A pygments lexer for OpenQasm.""" - name = 'OpenQASM' - aliases = ['qasm'] - filenames = ['*.qasm'] - gates = ['id', 'cx', 'x', 'y', 'z', 's', 'sdg', 'h', - 't', 'tdg', 'ccx', 'c3x', 'c4x', 'c3sqrtx', - 'rx', 'ry', 'rz', 'cz', 'cy', 'ch', 'swap', - 'cswap', 'crx', 'cry', 'crz', 'cu1', 'cu3', - 'rxx', 'rzz', 'rccx', 'rc3x', 'u1', 'u2', 'u3'] + name = "OpenQASM" + aliases = ["qasm"] + filenames = ["*.qasm"] + + gates = [ + "id", + "cx", + "x", + "y", + "z", + "s", + "sdg", + "h", + "t", + "tdg", + "ccx", + "c3x", + "c4x", + "c3sqrtx", + "rx", + "ry", + "rz", + "cz", + "cy", + "ch", + "swap", + "cswap", + "crx", + "cry", + "crz", + "cu1", + "cu3", + "rxx", + "rzz", + "rccx", + "rc3x", + "u1", + "u2", + "u3", + ] tokens = { - 'root': [ - (r'\n', Text), - (r'[^\S\n]+', Text), - (r'//\n', Comment), - (r'//.*?$', Comment.Single), - + "root": [ + (r"\n", Text), + (r"[^\S\n]+", Text), + (r"//\n", Comment), + (r"//.*?$", Comment.Single), # Keywords - (r'(OPENQASM|include)\b', Keyword.Reserved, 'keywords'), - (r'(qreg|creg)\b', Keyword.Declaration), - + (r"(OPENQASM|include)\b", Keyword.Reserved, "keywords"), + (r"(qreg|creg)\b", Keyword.Declaration), # Treat 'if' special - (r'(if)\b', Keyword.Reserved, 'if_keywords'), - + (r"(if)\b", Keyword.Reserved, "if_keywords"), # Constants - (r'(pi)\b', Name.Constant), - + (r"(pi)\b", Name.Constant), # Special - (r'(barrier|measure|reset)\b', Name.Builtin, 'params'), - + (r"(barrier|measure|reset)\b", Name.Builtin, "params"), # Gates (Types) - ('(' + '|'.join(gates) + r')\b', Keyword.Type, 'params'), - (r'[unitary\d+]', Keyword.Type), - + ("(" + "|".join(gates) + r")\b", Keyword.Type, "params"), + (r"[unitary\d+]", Keyword.Type), # Functions - (r'(gate)\b', Name.Function, 'gate'), - + (r"(gate)\b", Name.Function, "gate"), # Generic text - (r"[a-zA-Z_][a-zA-Z0-9_]*", Text, 'index')], - - 'keywords': [(r'\s*("([^"]|"")*")', String, '#push'), - (r"\d+", Number, '#push'), - (r'.*\(', Text, 'params')], - - 'if_keywords': [(r'[a-zA-Z0-9_]*', String, '#pop'), - (r"\d+", Number, '#push'), - (r'.*\(', Text, 'params')], - - 'params': [(r"[a-zA-Z_][a-zA-Z0-9_]*", Text, '#push'), - (r'\d+', Number, '#push'), - (r'(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?', - Number, '#push'), - (r'\)', Text)], - - 'gate': [(r'[unitary\d+]', Keyword.Type, '#push'), - (r'p\d+', Text, '#push')], - - 'index': [(r"\d+", Number, '#pop')] + (r"[a-zA-Z_][a-zA-Z0-9_]*", Text, "index"), + ], + "keywords": [ + (r'\s*("([^"]|"")*")', String, "#push"), + (r"\d+", Number, "#push"), + (r".*\(", Text, "params"), + ], + "if_keywords": [ + (r"[a-zA-Z0-9_]*", String, "#pop"), + (r"\d+", Number, "#push"), + (r".*\(", Text, "params"), + ], + "params": [ + (r"[a-zA-Z_][a-zA-Z0-9_]*", Text, "#push"), + (r"\d+", Number, "#push"), + (r"(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?", Number, "#push"), + (r"\)", Text), + ], + "gate": [(r"[unitary\d+]", Keyword.Type, "#push"), (r"p\d+", Text, "#push")], + "index": [(r"\d+", Number, "#pop")], } diff --git a/qiskit/qasm/qasm.py b/qiskit/qasm/qasm.py index 5c25b0d1984b..b78e59235ff4 100644 --- a/qiskit/qasm/qasm.py +++ b/qiskit/qasm/qasm.py @@ -25,8 +25,7 @@ def __init__(self, filename=None, data=None): if filename is None and data is None: raise QasmError("Missing input file and/or data") if filename is not None and data is not None: - raise QasmError("File and data must not both be specified" - "initializing qasm") + raise QasmError("File and data must not both be specified" "initializing qasm") self._filename = filename self._data = data diff --git a/qiskit/qasm/qasmlexer.py b/qiskit/qasm/qasmlexer.py index f8b00631e797..8b066be53732 100644 --- a/qiskit/qasm/qasmlexer.py +++ b/qiskit/qasm/qasmlexer.py @@ -25,7 +25,7 @@ from . import node from .exceptions import QasmError -CORE_LIBS_PATH = os.path.join(os.path.dirname(__file__), 'libs') +CORE_LIBS_PATH = os.path.join(os.path.dirname(__file__), "libs") CORE_LIBS = os.listdir(CORE_LIBS_PATH) @@ -35,6 +35,7 @@ class QasmLexer: This is a wrapper around the PLY lexer to support the "include" statement by creating a stack of lexers. """ + # pylint: disable=invalid-name,missing-function-docstring # pylint: disable=attribute-defined-outside-init,bad-docstring-quotes @@ -80,54 +81,54 @@ def push(self, filename): # ---- Beginning of the PLY lexer ---- literals = r'=()[]{};<>,.+-/*^"' reserved = { - 'barrier': 'BARRIER', - 'creg': 'CREG', - 'gate': 'GATE', - 'if': 'IF', - 'measure': 'MEASURE', - 'opaque': 'OPAQUE', - 'qreg': 'QREG', - 'pi': 'PI', - 'reset': 'RESET', + "barrier": "BARRIER", + "creg": "CREG", + "gate": "GATE", + "if": "IF", + "measure": "MEASURE", + "opaque": "OPAQUE", + "qreg": "QREG", + "pi": "PI", + "reset": "RESET", } tokens = [ - 'NNINTEGER', - 'REAL', - 'CX', - 'U', - 'FORMAT', - 'ASSIGN', - 'MATCHES', - 'ID', - 'STRING', + "NNINTEGER", + "REAL", + "CX", + "U", + "FORMAT", + "ASSIGN", + "MATCHES", + "ID", + "STRING", ] + list(reserved.values()) def t_REAL(self, t): - r'(([0-9]+|([0-9]+)?\.[0-9]+|[0-9]+\.)[eE][+-]?[0-9]+)|(([0-9]+)?\.[0-9]+|[0-9]+\.)' + r"(([0-9]+|([0-9]+)?\.[0-9]+|[0-9]+\.)[eE][+-]?[0-9]+)|(([0-9]+)?\.[0-9]+|[0-9]+\.)" if np.iscomplex(t): return t.real else: return t def t_NNINTEGER(self, t): - r'[1-9]+[0-9]*|0' + r"[1-9]+[0-9]*|0" t.value = int(t.value) return t def t_ASSIGN(self, t): - '->' + "->" return t def t_MATCHES(self, t): - '==' + "==" return t def t_STRING(self, t): - r'\"([^\\\"]|\\.)*\"' + r"\"([^\\\"]|\\.)*\" " return t def t_INCLUDE(self, _): - 'include' + "include" # Now eat up the next two tokens which must be # 1 - the name of the include file, and # 2 - a terminating semicolon @@ -147,42 +148,43 @@ def t_INCLUDE(self, _): incfile = os.path.join(CORE_LIBS_PATH, incfile) next_token = self.lexer.token() - if next_token is None or next_token.value != ';': + if next_token is None or next_token.value != ";": raise QasmError('Invalid syntax, missing ";" at line', str(lineno)) if not os.path.exists(incfile): raise QasmError( - 'Include file %s cannot be found, line %s, file %s' % - (incfile, str(next_token.lineno), self.filename)) + "Include file %s cannot be found, line %s, file %s" + % (incfile, str(next_token.lineno), self.filename) + ) self.push(incfile) return self.lexer.token() def t_FORMAT(self, t): - r'OPENQASM\s+(\d+)\.(\d+)' + r"OPENQASM\s+(\d+)\.(\d+)" return t def t_COMMENT(self, _): - r'//.*' + r"//.*" pass def t_CX(self, t): - 'CX' + "CX" return t def t_U(self, t): - 'U' + "U" return t def t_ID(self, t): - r'[a-z][a-zA-Z0-9_]*' + r"[a-z][a-zA-Z0-9_]*" - t.type = self.reserved.get(t.value, 'ID') - if t.type == 'ID': + t.type = self.reserved.get(t.value, "ID") + if t.type == "ID": t.value = node.Id(t.value, self.lineno, self.filename) return t def t_newline(self, t): - r'\n+' + r"\n+" self.lineno += len(t.value) t.lexer.lineno = self.lineno @@ -192,8 +194,10 @@ def t_eof(self, _): return self.lexer.token() return None - t_ignore = ' \t\r' + t_ignore = " \t\r" def t_error(self, t): - raise QasmError("Unable to match any token rule, got -->%s<-- " - "Check your OPENQASM source and any include statements." % t.value[0]) + raise QasmError( + "Unable to match any token rule, got -->%s<-- " + "Check your OPENQASM source and any include statements." % t.value[0] + ) diff --git a/qiskit/qasm/qasmparser.py b/qiskit/qasm/qasmparser.py index 1600d216c91c..330f8372ae14 100644 --- a/qiskit/qasm/qasmparser.py +++ b/qiskit/qasm/qasmparser.py @@ -35,22 +35,21 @@ def __init__(self, filename): filename = "" self.lexer = QasmLexer(filename) self.tokens = self.lexer.tokens - self.parse_dir = tempfile.mkdtemp(prefix='qiskit') + self.parse_dir = tempfile.mkdtemp(prefix="qiskit") self.precedence = ( - ('left', '+', '-'), - ('left', '*', '/'), - ('left', 'negative', 'positive'), - ('right', '^')) + ("left", "+", "-"), + ("left", "*", "/"), + ("left", "negative", "positive"), + ("right", "^"), + ) # For yacc, also, write_tables = Bool and optimize = Bool - self.parser = yacc.yacc(module=self, debug=False, - outputdir=self.parse_dir) + self.parser = yacc.yacc(module=self, debug=False, outputdir=self.parse_dir) self.qasm = None self.parse_deb = False - self.global_symtab = {} # global symtab - self.current_symtab = self.global_symtab # top of symbol stack - self.symbols = [] # symbol stack - self.external_functions = ['sin', 'cos', 'tan', 'exp', 'ln', 'sqrt', - 'acos', 'atan', 'asin'] + self.global_symtab = {} # global symtab + self.current_symtab = self.global_symtab # top of symbol stack + self.symbols = [] # symbol stack + self.external_functions = ["sin", "cos", "tan", "exp", "ln", "sqrt", "acos", "atan", "asin"] def __enter__(self): return self @@ -70,11 +69,14 @@ def update_symtab(self, obj): """ if obj.name in self.current_symtab: prev = self.current_symtab[obj.name] - raise QasmError("Duplicate declaration for", obj.type + " '" - + obj.name + "' at line", str(obj.line) - + ', file', obj.file - + '.\nPrevious occurrence at line', - str(prev.line) + ', file', prev.file) + raise QasmError( + "Duplicate declaration for", + obj.type + " '" + obj.name + "' at line", + str(obj.line) + ", file", + obj.file + ".\nPrevious occurrence at line", + str(prev.line) + ", file", + prev.file, + ) self.current_symtab[obj.name] = obj def verify_declared_bit(self, obj): @@ -82,16 +84,18 @@ def verify_declared_bit(self, obj): # We are verifying gate args against the formal parameters of a # gate prototype. if obj.name not in self.current_symtab: - raise QasmError("Cannot find symbol '" + obj.name - + "' in argument list for gate, line", - str(obj.line), 'file', obj.file) + raise QasmError( + "Cannot find symbol '" + obj.name + "' in argument list for gate, line", + str(obj.line), + "file", + obj.file, + ) # This insures the thing is from the bitlist and not from the # argument list. sym = self.current_symtab[obj.name] - if not (sym.type == 'id' and sym.is_bit): - raise QasmError("Bit", obj.name, - 'is not declared as a bit in the gate.') + if not (sym.type == "id" and sym.is_bit): + raise QasmError("Bit", obj.name, "is not declared as a bit in the gate.") def verify_bit_list(self, obj): """Verify each qubit in a list of ids.""" @@ -115,10 +119,15 @@ def verify_exp_list(self, obj): continue if children.name not in self.current_symtab: - raise QasmError("Argument '" + children.name - + "' in expression cannot be " - + "found, line", str(children.line), - "file", children.file) + raise QasmError( + "Argument '" + + children.name + + "' in expression cannot be " + + "found, line", + str(children.line), + "file", + children.file, + ) else: if hasattr(children, "children"): self.verify_exp_list(children) @@ -126,35 +135,66 @@ def verify_exp_list(self, obj): def verify_as_gate(self, obj, bitlist, arglist=None): """Verify a user defined gate call.""" if obj.name not in self.global_symtab: - raise QasmError("Cannot find gate definition for '" + obj.name - + "', line", str(obj.line), 'file', obj.file) + raise QasmError( + "Cannot find gate definition for '" + obj.name + "', line", + str(obj.line), + "file", + obj.file, + ) g_sym = self.global_symtab[obj.name] - if not (g_sym.type == 'gate' or g_sym.type == 'opaque'): - raise QasmError("'" + obj.name + "' is used as a gate " - + "or opaque call but the symbol is neither;" - + " it is a '" + g_sym.type + "' line", - str(obj.line), 'file', obj.file) + if not (g_sym.type == "gate" or g_sym.type == "opaque"): + raise QasmError( + "'" + + obj.name + + "' is used as a gate " + + "or opaque call but the symbol is neither;" + + " it is a '" + + g_sym.type + + "' line", + str(obj.line), + "file", + obj.file, + ) if g_sym.n_bits() != bitlist.size(): - raise QasmError("Gate or opaque call to '" + obj.name - + "' uses", str(bitlist.size()), - "qubits but is declared for", - str(g_sym.n_bits()), "qubits", "line", - str(obj.line), 'file', obj.file) + raise QasmError( + "Gate or opaque call to '" + obj.name + "' uses", + str(bitlist.size()), + "qubits but is declared for", + str(g_sym.n_bits()), + "qubits", + "line", + str(obj.line), + "file", + obj.file, + ) if arglist: if g_sym.n_args() != arglist.size(): - raise QasmError("Gate or opaque call to '" + obj.name - + "' uses", str(arglist.size()), - "qubits but is declared for", - str(g_sym.n_args()), "qubits", "line", - str(obj.line), 'file', obj.file) + raise QasmError( + "Gate or opaque call to '" + obj.name + "' uses", + str(arglist.size()), + "qubits but is declared for", + str(g_sym.n_args()), + "qubits", + "line", + str(obj.line), + "file", + obj.file, + ) else: if g_sym.n_args() > 0: - raise QasmError("Gate or opaque call to '" + obj.name - + "' has no arguments but is declared for", - str(g_sym.n_args()), "qubits", "line", - str(obj.line), 'file', obj.file) + raise QasmError( + "Gate or opaque call to '" + + obj.name + + "' has no arguments but is declared for", + str(g_sym.n_args()), + "qubits", + "line", + str(obj.line), + "file", + obj.file, + ) def verify_reg(self, obj, object_type): """Verify a register.""" @@ -162,26 +202,47 @@ def verify_reg(self, obj, object_type): # types must match # indexes must be checked if obj.name not in self.global_symtab: - raise QasmError('Cannot find definition for', object_type, "'" - + obj.name + "'", 'at line', str(obj.line), - 'file', obj.file) + raise QasmError( + "Cannot find definition for", + object_type, + "'" + obj.name + "'", + "at line", + str(obj.line), + "file", + obj.file, + ) g_sym = self.global_symtab[obj.name] if g_sym.type != object_type: - raise QasmError("Type for '" + g_sym.name + "' should be '" - + object_type + "' but was found to be '" - + g_sym.type + "'", "line", str(obj.line), - "file", obj.file) - - if obj.type == 'indexed_id': + raise QasmError( + "Type for '" + + g_sym.name + + "' should be '" + + object_type + + "' but was found to be '" + + g_sym.type + + "'", + "line", + str(obj.line), + "file", + obj.file, + ) + + if obj.type == "indexed_id": bound = g_sym.index ndx = obj.index if ndx < 0 or ndx >= bound: - raise QasmError("Register index for '" + g_sym.name - + "' out of bounds. Index is", str(ndx), - "bound is 0 <= index <", str(bound), - "at line", str(obj.line), "file", obj.file) + raise QasmError( + "Register index for '" + g_sym.name + "' out of bounds. Index is", + str(ndx), + "bound is 0 <= index <", + str(bound), + "at line", + str(obj.line), + "file", + obj.file, + ) def verify_reg_list(self, obj, object_type): """Verify a list of registers.""" @@ -250,8 +311,7 @@ def verify_distinct(self, list_of_nodes): else: raise QasmError("internal error, verify_distinct") if len(bit_list) != len(set(bit_list)): - raise QasmError("duplicate identifiers at line %d file %s" - % (line_number, filename)) + raise QasmError("duplicate identifiers at line %d file %s" % (line_number, filename)) def pop_scope(self): """Return to the previous scope.""" @@ -263,11 +323,11 @@ def push_scope(self): self.current_symtab = {} # ---- Begin the PLY parser ---- - start = 'main' + start = "main" def p_main(self, program): """ - main : program + main : program """ self.qasm = program[1] @@ -277,13 +337,13 @@ def p_main(self, program): # ---------------------------------------- def p_program_0(self, program): """ - program : statement + program : statement """ program[0] = node.Program([program[1]]) def p_program_1(self, program): """ - program : program statement + program : program statement """ program[0] = program[1] program[0].add_child(program[2]) @@ -295,64 +355,63 @@ def p_program_1(self, program): # ---------------------------------------- def p_statement(self, program): """ - statement : decl - | quantum_op ';' - | format ';' - | ignore - | quantum_op error - | format error + statement : decl + | quantum_op ';' + | format ';' + | ignore + | quantum_op error + | format error """ if len(program) > 2: - if program[2] != ';': - raise QasmError("Missing ';' at end of statement; " - + "received", str(program[2].value)) + if program[2] != ";": + raise QasmError( + "Missing ';' at end of statement; " + "received", str(program[2].value) + ) program[0] = program[1] def p_format(self, program): """ - format : FORMAT + format : FORMAT """ program[0] = node.Format(program[1]) def p_format_0(self, _): """ - format : FORMAT error + format : FORMAT error """ version = "2.0;" - raise QasmError("Invalid version string. Expected '" + version - + "'. Is the semicolon missing?") + raise QasmError( + "Invalid version string. Expected '" + version + "'. Is the semicolon missing?" + ) # ---------------------------------------- # id : ID # ---------------------------------------- def p_id(self, program): """ - id : ID + id : ID """ program[0] = program[1] def p_id_e(self, program): """ - id : error + id : error """ - raise QasmError("Expected an ID, received '" - + str(program[1].value) + "'") + raise QasmError("Expected an ID, received '" + str(program[1].value) + "'") # ---------------------------------------- # indexed_id : ID [ int ] # ---------------------------------------- def p_indexed_id(self, program): """ - indexed_id : id '[' NNINTEGER ']' - | id '[' NNINTEGER error - | id '[' error + indexed_id : id '[' NNINTEGER ']' + | id '[' NNINTEGER error + | id '[' error """ if len(program) == 4: - raise QasmError("Expecting an integer index; received", - str(program[3].value)) - if program[4] != ']': - raise QasmError("Missing ']' in indexed ID; received", - str(program[4].value)) + raise QasmError("Expecting an integer index; received", str(program[3].value)) + if program[4] != "]": + raise QasmError("Missing ']' in indexed ID; received", str(program[4].value)) program[0] = node.IndexedId([program[1], node.Int(program[3])]) # ---------------------------------------- @@ -361,8 +420,8 @@ def p_indexed_id(self, program): # ---------------------------------------- def p_primary(self, program): """ - primary : id - | indexed_id + primary : id + | indexed_id """ program[0] = program[1] @@ -372,13 +431,13 @@ def p_primary(self, program): # ---------------------------------------- def p_id_list_0(self, program): """ - id_list : id + id_list : id """ program[0] = node.IdList([program[1]]) def p_id_list_1(self, program): """ - id_list : id_list ',' id + id_list : id_list ',' id """ program[0] = program[1] program[0].add_child(program[3]) @@ -389,14 +448,14 @@ def p_id_list_1(self, program): # ---------------------------------------- def p_gate_id_list_0(self, program): """ - gate_id_list : id + gate_id_list : id """ program[0] = node.IdList([program[1]]) self.update_symtab(program[1]) def p_gate_id_list_1(self, program): """ - gate_id_list : gate_id_list ',' id + gate_id_list : gate_id_list ',' id """ program[0] = program[1] program[0].add_child(program[3]) @@ -408,7 +467,7 @@ def p_gate_id_list_1(self, program): # ---------------------------------------- def p_bit_list_0(self, program): """ - bit_list : id + bit_list : id """ program[0] = node.IdList([program[1]]) program[1].is_bit = True @@ -416,7 +475,7 @@ def p_bit_list_0(self, program): def p_bit_list_1(self, program): """ - bit_list : bit_list ',' id + bit_list : bit_list ',' id """ program[0] = program[1] program[0].add_child(program[3]) @@ -429,13 +488,13 @@ def p_bit_list_1(self, program): # ---------------------------------------- def p_primary_list_0(self, program): """ - primary_list : primary + primary_list : primary """ program[0] = node.PrimaryList([program[1]]) def p_primary_list_1(self, program): """ - primary_list : primary_list ',' primary + primary_list : primary_list ',' primary """ program[0] = program[1] program[1].add_child(program[3]) @@ -447,16 +506,18 @@ def p_primary_list_1(self, program): # ---------------------------------------- def p_decl(self, program): """ - decl : qreg_decl ';' - | creg_decl ';' - | qreg_decl error - | creg_decl error - | gate_decl + decl : qreg_decl ';' + | creg_decl ';' + | qreg_decl error + | creg_decl error + | gate_decl """ if len(program) > 2: - if program[2] != ';': - raise QasmError("Missing ';' in qreg or creg declaration." - " Instead received '" + program[2].value + "'") + if program[2] != ";": + raise QasmError( + "Missing ';' in qreg or creg declaration." + " Instead received '" + program[2].value + "'" + ) program[0] = program[1] # ---------------------------------------- @@ -464,44 +525,48 @@ def p_decl(self, program): # ---------------------------------------- def p_qreg_decl(self, program): """ - qreg_decl : QREG indexed_id + qreg_decl : QREG indexed_id """ program[0] = node.Qreg([program[2]]) if program[2].name in self.external_functions: - raise QasmError("QREG names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "QREG names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) if program[2].index == 0: raise QasmError("QREG size must be positive") self.update_symtab(program[0]) def p_qreg_decl_e(self, program): """ - qreg_decl : QREG error + qreg_decl : QREG error """ - raise QasmError("Expecting indexed id (ID[int]) in QREG" - + " declaration; received", program[2].value) + raise QasmError( + "Expecting indexed id (ID[int]) in QREG" + " declaration; received", program[2].value + ) # ---------------------------------------- # creg_decl : QREG indexed_id # ---------------------------------------- def p_creg_decl(self, program): """ - creg_decl : CREG indexed_id + creg_decl : CREG indexed_id """ program[0] = node.Creg([program[2]]) if program[2].name in self.external_functions: - raise QasmError("CREG names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "CREG names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) if program[2].index == 0: raise QasmError("CREG size must be positive") self.update_symtab(program[0]) def p_creg_decl_e(self, program): """ - creg_decl : CREG error + creg_decl : CREG error """ - raise QasmError("Expecting indexed id (ID[int]) in CREG" - + " declaration; received", program[2].value) + raise QasmError( + "Expecting indexed id (ID[int]) in CREG" + " declaration; received", program[2].value + ) # Gate_body will throw if there are errors, so we don't need to cover # that here. Same with the id_lists - if they are not legal, we die @@ -515,23 +580,25 @@ def p_creg_decl_e(self, program): # ---------------------------------------- def p_gate_decl_0(self, program): """ - gate_decl : GATE id gate_scope bit_list gate_body + gate_decl : GATE id gate_scope bit_list gate_body """ program[0] = node.Gate([program[2], program[4], program[5]]) if program[2].name in self.external_functions: - raise QasmError("GATE names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) self.pop_scope() self.update_symtab(program[0]) def p_gate_decl_1(self, program): """ - gate_decl : GATE id gate_scope '(' ')' bit_list gate_body + gate_decl : GATE id gate_scope '(' ')' bit_list gate_body """ program[0] = node.Gate([program[2], program[6], program[7]]) if program[2].name in self.external_functions: - raise QasmError("GATE names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) self.pop_scope() self.update_symtab(program[0]) @@ -539,17 +606,17 @@ def p_gate_decl_2(self, program): """ gate_decl : GATE id gate_scope '(' gate_id_list ')' bit_list gate_body """ - program[0] = node.Gate( - [program[2], program[5], program[7], program[8]]) + program[0] = node.Gate([program[2], program[5], program[7], program[8]]) if program[2].name in self.external_functions: - raise QasmError("GATE names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "GATE names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) self.pop_scope() self.update_symtab(program[0]) def p_gate_scope(self, _): """ - gate_scope : + gate_scope : """ self.push_scope() @@ -565,16 +632,17 @@ def p_gate_scope(self, _): # ---------------------------------------- def p_gate_body_0(self, program): """ - gate_body : '{' '}' + gate_body : '{' '}' """ - if program[2] != '}': - raise QasmError("Missing '}' in gate definition; received'" - + str(program[2].value) + "'") + if program[2] != "}": + raise QasmError( + "Missing '}' in gate definition; received'" + str(program[2].value) + "'" + ) program[0] = node.GateBody(None) def p_gate_body_1(self, program): """ - gate_body : '{' gate_op_list '}' + gate_body : '{' gate_op_list '}' """ program[0] = node.GateBody(program[2]) @@ -587,13 +655,13 @@ def p_gate_body_1(self, program): # ---------------------------------------- def p_gate_op_list_0(self, program): """ - gate_op_list : gate_op + gate_op_list : gate_op """ program[0] = [program[1]] def p_gate_op_list_1(self, program): """ - gate_op_list : gate_op_list gate_op + gate_op_list : gate_op_list gate_op """ program[0] = program[1] program[0].append(program[2]) @@ -613,10 +681,10 @@ def p_gate_op_list_1(self, program): # ---------------------------------------- def p_unitary_op_0(self, program): """ - unitary_op : U '(' exp_list ')' primary + unitary_op : U '(' exp_list ')' primary """ program[0] = node.UniversalUnitary([program[3], program[5]]) - self.verify_reg(program[5], 'qreg') + self.verify_reg(program[5], "qreg") self.verify_exp_list(program[3]) def p_unitary_op_1(self, program): @@ -624,8 +692,8 @@ def p_unitary_op_1(self, program): unitary_op : CX primary ',' primary """ program[0] = node.Cnot([program[2], program[4]]) - self.verify_reg(program[2], 'qreg') - self.verify_reg(program[4], 'qreg') + self.verify_reg(program[2], "qreg") + self.verify_reg(program[4], "qreg") self.verify_distinct([program[2], program[4]]) # TODO: check that if both primary are id, same size # TODO: this needs to be checked in other cases too @@ -636,7 +704,7 @@ def p_unitary_op_2(self, program): """ program[0] = node.CustomUnitary([program[1], program[2]]) self.verify_as_gate(program[1], program[2]) - self.verify_reg_list(program[2], 'qreg') + self.verify_reg_list(program[2], "qreg") self.verify_distinct([program[2]]) def p_unitary_op_3(self, program): @@ -645,7 +713,7 @@ def p_unitary_op_3(self, program): """ program[0] = node.CustomUnitary([program[1], program[4]]) self.verify_as_gate(program[1], program[4]) - self.verify_reg_list(program[4], 'qreg') + self.verify_reg_list(program[4], "qreg") self.verify_distinct([program[4]]) def p_unitary_op_4(self, program): @@ -654,7 +722,7 @@ def p_unitary_op_4(self, program): """ program[0] = node.CustomUnitary([program[1], program[3], program[5]]) self.verify_as_gate(program[1], program[5], arglist=program[3]) - self.verify_reg_list(program[5], 'qreg') + self.verify_reg_list(program[5], "qreg") self.verify_exp_list(program[3]) self.verify_distinct([program[5]]) @@ -681,8 +749,7 @@ def p_gate_op_0e1(self, p): """ gate_op : U '(' exp_list ')' error """ - raise QasmError("Invalid U inside gate definition. " - + "Missing bit id or ';'") + raise QasmError("Invalid U inside gate definition. " + "Missing bit id or ';'") def p_gate_op_0e2(self, _): """ @@ -703,17 +770,23 @@ def p_gate_op_1e1(self, program): """ gate_op : CX error """ - raise QasmError("Invalid CX inside gate definition. " - + "Expected an ID or ',', received '" - + str(program[2].value) + "'") + raise QasmError( + "Invalid CX inside gate definition. " + + "Expected an ID or ',', received '" + + str(program[2].value) + + "'" + ) def p_gate_op_1e2(self, program): """ gate_op : CX id ',' error """ - raise QasmError("Invalid CX inside gate definition. " - + "Expected an ID or ';', received '" - + str(program[4].value) + "'") + raise QasmError( + "Invalid CX inside gate definition. " + + "Expected an ID or ';', received '" + + str(program[4].value) + + "'" + ) def p_gate_op_2(self, program): """ @@ -756,15 +829,13 @@ def p_gate_op_4e0(self, _): """ gate_op : id '(' ')' error """ - raise QasmError("Invalid bit list inside gate definition or" - + " missing ';'") + raise QasmError("Invalid bit list inside gate definition or" + " missing ';'") def p_gate_op_4e1(self, _): """ gate_op : id '(' error """ - raise QasmError("Unmatched () for gate invocation inside gate" - + " invocation.") + raise QasmError("Unmatched () for gate invocation inside gate" + " invocation.") def p_gate_op_5(self, program): """ @@ -789,19 +860,20 @@ def p_gate_op_5e(self, _): # ---------------------------------------- def p_opaque_0(self, program): """ - opaque : OPAQUE id gate_scope bit_list + opaque : OPAQUE id gate_scope bit_list """ # TODO: Review Opaque function program[0] = node.Opaque([program[2], program[4]]) if program[2].name in self.external_functions: - raise QasmError("OPAQUE names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "OPAQUE names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) self.pop_scope() self.update_symtab(program[0]) def p_opaque_1(self, program): """ - opaque : OPAQUE id gate_scope '(' ')' bit_list + opaque : OPAQUE id gate_scope '(' ')' bit_list """ program[0] = node.Opaque([program[2], program[6]]) self.pop_scope() @@ -809,18 +881,19 @@ def p_opaque_1(self, program): def p_opaque_2(self, program): """ - opaque : OPAQUE id gate_scope '(' gate_id_list ')' bit_list + opaque : OPAQUE id gate_scope '(' gate_id_list ')' bit_list """ program[0] = node.Opaque([program[2], program[5], program[7]]) if program[2].name in self.external_functions: - raise QasmError("OPAQUE names cannot be reserved words. " - + "Received '" + program[2].name + "'") + raise QasmError( + "OPAQUE names cannot be reserved words. " + "Received '" + program[2].name + "'" + ) self.pop_scope() self.update_symtab(program[0]) def p_opaque_1e(self, _): """ - opaque : OPAQUE id gate_scope '(' error + opaque : OPAQUE id gate_scope '(' error """ raise QasmError("Poorly formed OPAQUE statement.") @@ -829,18 +902,17 @@ def p_opaque_1e(self, _): # ---------------------------------------- def p_measure(self, program): """ - measure : MEASURE primary ASSIGN primary + measure : MEASURE primary ASSIGN primary """ program[0] = node.Measure([program[2], program[4]]) - self.verify_reg(program[2], 'qreg') - self.verify_reg(program[4], 'creg') + self.verify_reg(program[2], "qreg") + self.verify_reg(program[4], "creg") def p_measure_e(self, program): """ - measure : MEASURE primary error + measure : MEASURE primary error """ - raise QasmError("Illegal measure statement." + - str(program[3].value)) + raise QasmError("Illegal measure statement." + str(program[3].value)) # ---------------------------------------- # barrier : BARRIER primary_list @@ -852,7 +924,7 @@ def p_barrier(self, program): barrier : BARRIER primary_list """ program[0] = node.Barrier([program[2]]) - self.verify_reg_list(program[2], 'qreg') + self.verify_reg_list(program[2], "qreg") self.verify_distinct([program[2]]) # ---------------------------------------- @@ -863,7 +935,7 @@ def p_reset(self, program): reset : RESET primary """ program[0] = node.Reset([program[2]]) - self.verify_reg(program[2], 'qreg') + self.verify_reg(program[2], "qreg") # ---------------------------------------- # IF '(' ID MATCHES NNINTEGER ')' quantum_op @@ -877,20 +949,23 @@ def p_if(self, program): if : IF error """ if len(program) == 3: - raise QasmError("Ill-formed IF statement. Perhaps a" - + " missing '('?") + raise QasmError("Ill-formed IF statement. Perhaps a" + " missing '('?") if len(program) == 5: - raise QasmError("Ill-formed IF statement. Expected '==', " - + "received '" + str(program[4].value)) + raise QasmError( + "Ill-formed IF statement. Expected '==', " + "received '" + str(program[4].value) + ) if len(program) == 6: - raise QasmError("Ill-formed IF statement. Expected a number, " - + "received '" + str(program[5].value)) + raise QasmError( + "Ill-formed IF statement. Expected a number, " + + "received '" + + str(program[5].value) + ) if len(program) == 7: raise QasmError("Ill-formed IF statement, unmatched '('") - if program[7].type == 'if': + if program[7].type == "if": raise QasmError("Nested IF statements not allowed") - if program[7].type == 'barrier': + if program[7].type == "barrier": raise QasmError("barrier not permitted in IF statement") program[0] = node.If([program[3], node.Int(program[5]), program[7]]) @@ -907,12 +982,12 @@ def p_if(self, program): # ---------------------------------------- def p_quantum_op(self, program): """ - quantum_op : unitary_op - | opaque - | measure - | barrier - | reset - | if + quantum_op : unitary_op + | opaque + | measure + | barrier + | reset + | if """ program[0] = program[1] @@ -928,42 +1003,41 @@ def p_quantum_op(self, program): # ---------------------------------------- def p_unary_0(self, program): """ - unary : NNINTEGER + unary : NNINTEGER """ program[0] = node.Int(program[1]) def p_unary_1(self, program): """ - unary : REAL + unary : REAL """ program[0] = node.Real(program[1]) def p_unary_2(self, program): """ - unary : PI + unary : PI """ program[0] = node.Real(np.pi) def p_unary_3(self, program): """ - unary : id + unary : id """ program[0] = program[1] def p_unary_4(self, program): """ - unary : '(' expression ')' + unary : '(' expression ')' """ program[0] = program[2] def p_unary_6(self, program): """ - unary : id '(' expression ')' + unary : id '(' expression ')' """ # note this is a semantic check, not syntactic if program[1].name not in self.external_functions: - raise QasmError("Illegal external function call: ", - str(program[1].name)) + raise QasmError("Illegal external function call: ", str(program[1].name)) program[0] = node.External([program[1], program[3]]) # ---------------------------------------- @@ -972,8 +1046,8 @@ def p_unary_6(self, program): def p_expression_1(self, program): """ - expression : '-' expression %prec negative - | '+' expression %prec positive + expression : '-' expression %prec negative + | '+' expression %prec positive """ program[0] = node.Prefix([node.UnaryOperator(program[1]), program[2]]) @@ -985,12 +1059,11 @@ def p_expression_0(self, program): | expression '-' expression | expression '^' expression """ - program[0] = node.BinaryOp([node.BinaryOperator(program[2]), - program[1], program[3]]) + program[0] = node.BinaryOp([node.BinaryOperator(program[2]), program[1], program[3]]) def p_expression_2(self, program): """ - expression : unary + expression : unary """ program[0] = program[1] @@ -1000,20 +1073,20 @@ def p_expression_2(self, program): # ---------------------------------------- def p_exp_list_0(self, program): """ - exp_list : expression + exp_list : expression """ program[0] = node.ExpressionList([program[1]]) def p_exp_list_1(self, program): """ - exp_list : exp_list ',' expression + exp_list : exp_list ',' expression """ program[0] = program[1] program[0].add_child(program[3]) def p_ignore(self, _): """ - ignore : STRING + ignore : STRING """ # this should never hit but it keeps the insuppressible warnings at bay pass @@ -1022,11 +1095,10 @@ def p_error(self, program): # EOF is a special case because the stupid error token isn't placed # on the stack if not program: - raise QasmError("Error at end of file. " - + "Perhaps there is a missing ';'") + raise QasmError("Error at end of file. " + "Perhaps there is a missing ';'") col = self.find_column(self.lexer.data, program) - print("Error near line", str(self.lexer.lineno), 'Column', col) + print("Error near line", str(self.lexer.lineno), "Column", col) def find_column(self, input_, token): """Compute the column. @@ -1036,7 +1108,7 @@ def find_column(self, input_, token): """ if token is None: return 0 - last_cr = input_.rfind('\n', 0, token.lexpos) + last_cr = input_.rfind("\n", 0, token.lexpos) if last_cr < 0: last_cr = 0 column = (token.lexpos - last_cr) + 1 @@ -1053,7 +1125,7 @@ def read_tokens(self): yield token except QasmError as e: - print('Exception tokenizing qasm file:', e.msg) + print("Exception tokenizing qasm file:", e.msg) def parse_debug(self, val): """Set the parse_deb field.""" @@ -1062,15 +1134,13 @@ def parse_debug(self, val): elif val is False: self.parse_deb = False else: - raise QasmError("Illegal debug value '" + str(val) - + "' must be True or False.") + raise QasmError("Illegal debug value '" + str(val) + "' must be True or False.") def parse(self, data): """Parse some data.""" self.parser.parse(data, lexer=self.lexer, debug=self.parse_deb) if self.qasm is None: - raise QasmError("Uncaught exception in parser; " - + "see previous messages for details.") + raise QasmError("Uncaught exception in parser; " + "see previous messages for details.") return self.qasm def print_tree(self): diff --git a/qiskit/qobj/__init__.py b/qiskit/qobj/__init__.py index 5f543e2e016b..c1723d71988d 100644 --- a/qiskit/qobj/__init__.py +++ b/qiskit/qobj/__init__.py @@ -83,11 +83,12 @@ class Qobj(QasmQobj): """A backwards compat alias for QasmQobj.""" - def __init__(self, qobj_id=None, config=None, experiments=None, - header=None): + def __init__(self, qobj_id=None, config=None, experiments=None, header=None): """Initialize a Qobj object.""" - warnings.warn('qiskit.qobj.Qobj is deprecated use either QasmQobj or ' - 'PulseQobj depending on your application instead.', - DeprecationWarning, stacklevel=2) - super().__init__(qobj_id=qobj_id, config=config, - experiments=experiments, header=header) + warnings.warn( + "qiskit.qobj.Qobj is deprecated use either QasmQobj or " + "PulseQobj depending on your application instead.", + DeprecationWarning, + stacklevel=2, + ) + super().__init__(qobj_id=qobj_id, config=config, experiments=experiments, header=header) diff --git a/qiskit/qobj/common.py b/qiskit/qobj/common.py index ed3b8caebbe0..3a078b82e2bf 100644 --- a/qiskit/qobj/common.py +++ b/qiskit/qobj/common.py @@ -19,10 +19,8 @@ import fastjsonschema -path_part = 'schemas/qobj_schema.json' -path = os.path.join( - os.path.dirname(os.path.dirname(os.path.abspath(__file__))), - path_part) +path_part = "schemas/qobj_schema.json" +path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), path_part) with open(path) as fd: json_schema = json.loads(fd.read()) validator = fastjsonschema.compile(json_schema) @@ -75,9 +73,11 @@ def __eq__(self, other): class QobjHeader(QobjDictField): """A class used to represent a dictionary header in Qobj objects.""" + pass class QobjExperimentHeader(QobjHeader): """A class representing a header dictionary for a Qobj Experiment.""" + pass diff --git a/qiskit/qobj/converters/lo_config.py b/qiskit/qobj/converters/lo_config.py index 80c0a3831b29..87bde4eb9fbf 100644 --- a/qiskit/qobj/converters/lo_config.py +++ b/qiskit/qobj/converters/lo_config.py @@ -17,13 +17,20 @@ class LoConfigConverter: - """ This class supports to convert LoConfig into ~`lo_freq` attribute of configs. + """This class supports to convert LoConfig into ~`lo_freq` attribute of configs. The format of LO frequency setup can be easily modified by replacing `get_qubit_los` and `get_meas_los` to align with your backend. """ - def __init__(self, qobj_model, qubit_lo_freq, meas_lo_freq, - qubit_lo_range=None, meas_lo_range=None, **run_config): + def __init__( + self, + qobj_model, + qubit_lo_freq, + meas_lo_freq, + qubit_lo_range=None, + meas_lo_range=None, + **run_config, + ): """Create new converter. Args: @@ -64,11 +71,11 @@ def __call__(self, user_lo_config): q_los = self.get_qubit_los(user_lo_config) if q_los: - lo_config['qubit_lo_freq'] = [freq/1e9 for freq in q_los] + lo_config["qubit_lo_freq"] = [freq / 1e9 for freq in q_los] m_los = self.get_meas_los(user_lo_config) if m_los: - lo_config['meas_lo_freq'] = [freq/1e9 for freq in m_los] + lo_config["meas_lo_freq"] = [freq / 1e9 for freq in m_los] return self.qobj_model(**lo_config) diff --git a/qiskit/qobj/converters/pulse_instruction.py b/qiskit/qobj/converters/pulse_instruction.py index 1e68c19a9c4c..5782d7a8ad85 100644 --- a/qiskit/qobj/converters/pulse_instruction.py +++ b/qiskit/qobj/converters/pulse_instruction.py @@ -39,6 +39,7 @@ class ParametricPulseShapes(Enum): The enum name is the transport layer name for pulse shapes, the value is its mapping to the OpenPulse Command in Qiskit. """ + gaussian = library.Gaussian gaussian_square = library.GaussianSquare drag = library.Drag @@ -47,12 +48,13 @@ class ParametricPulseShapes(Enum): class ConversionMethodBinder: """Conversion method registrar.""" + def __init__(self): """Acts as method registration decorator and tracker for conversion methods.""" self._bound_instructions = {} def __call__(self, bound): - """ Converter decorator method. + """Converter decorator method. Converter is defined for object to be converted matched on hash @@ -75,7 +77,7 @@ def get_bound_method(self, bound): try: return self._bound_instructions[bound] except KeyError as ex: - raise QiskitError(f'Bound method for {bound} is not found.') from ex + raise QiskitError(f"Bound method for {bound} is not found.") from ex class InstructionToQobjConverter: @@ -106,6 +108,7 @@ def convert_custom_command(self, shift, instruction): }) return self.qobj_model(**command_dict) """ + # class level tracking of conversion methods bind_instruction = ConversionMethodBinder() @@ -134,49 +137,52 @@ def convert_acquire(self, shift, instruction): Returns: dict: Dictionary of required parameters. """ - meas_level = self._run_config.get('meas_level', 2) + meas_level = self._run_config.get("meas_level", 2) mem_slot = [] if instruction.mem_slot: mem_slot = [instruction.mem_slot.index] command_dict = { - 'name': 'acquire', - 't0': shift + instruction.start_time, - 'duration': instruction.duration, - 'qubits': [instruction.channel.index], - 'memory_slot': mem_slot + "name": "acquire", + "t0": shift + instruction.start_time, + "duration": instruction.duration, + "qubits": [instruction.channel.index], + "memory_slot": mem_slot, } if meas_level == MeasLevel.CLASSIFIED: # setup discriminators if instruction.discriminator: - command_dict.update({ - 'discriminators': [ - QobjMeasurementOption( - name=instruction.discriminator.name, - params=instruction.discriminator.params) - ] - }) + command_dict.update( + { + "discriminators": [ + QobjMeasurementOption( + name=instruction.discriminator.name, + params=instruction.discriminator.params, + ) + ] + } + ) # setup register_slots if instruction.reg_slot: - command_dict.update({ - 'register_slot': [instruction.reg_slot.index] - }) + command_dict.update({"register_slot": [instruction.reg_slot.index]}) if meas_level in [MeasLevel.KERNELED, MeasLevel.CLASSIFIED]: # setup kernels if instruction.kernel: - command_dict.update({ - 'kernels': [ - QobjMeasurementOption( - name=instruction.kernel.name, - params=instruction.kernel.params) - ] - }) + command_dict.update( + { + "kernels": [ + QobjMeasurementOption( + name=instruction.kernel.name, params=instruction.kernel.params + ) + ] + } + ) return self._qobj_model(**command_dict) def convert_bundled_acquires( - self, - shift, - instructions_, + self, + shift, + instructions_, ): """Bundle a list of acquires instructions at the same time into a single Qobj acquire instruction. @@ -192,7 +198,7 @@ def convert_bundled_acquires( if not instructions_: raise QiskitError('"instructions" may not be empty.') - meas_level = self._run_config.get('meas_level', 2) + meas_level = self._run_config.get("meas_level", 2) t0 = instructions_[0].start_time duration = instructions_[0].duration @@ -207,15 +213,14 @@ def convert_bundled_acquires( if instruction.start_time != t0: raise QiskitError( - 'The supplied acquire instructions have different starting times. ' - 'Something has gone wrong calling this code. Please report this ' - 'issue.' + "The supplied acquire instructions have different starting times. " + "Something has gone wrong calling this code. Please report this " + "issue." ) if instruction.duration != duration: raise QiskitError( - 'Acquire instructions beginning at the same time must have ' - 'same duration.' + "Acquire instructions beginning at the same time must have " "same duration." ) if instruction.mem_slot: @@ -224,9 +229,12 @@ def convert_bundled_acquires( if meas_level == MeasLevel.CLASSIFIED: # setup discriminators if instruction.discriminator: - discriminators.append(QobjMeasurementOption( - name=instruction.discriminator.name, - params=instruction.discriminator.params)) + discriminators.append( + QobjMeasurementOption( + name=instruction.discriminator.name, + params=instruction.discriminator.params, + ) + ) # setup register_slots if instruction.reg_slot: register_slots.append(instruction.reg_slot.index) @@ -234,46 +242,48 @@ def convert_bundled_acquires( if meas_level in [MeasLevel.KERNELED, MeasLevel.CLASSIFIED]: # setup kernels if instruction.kernel: - kernels.append(QobjMeasurementOption( - name=instruction.kernel.name, - params=instruction.kernel.params)) + kernels.append( + QobjMeasurementOption( + name=instruction.kernel.name, params=instruction.kernel.params + ) + ) command_dict = { - 'name': 'acquire', - 't0': t0 + shift, - 'duration': duration, - 'qubits': qubits, + "name": "acquire", + "t0": t0 + shift, + "duration": duration, + "qubits": qubits, } if memory_slots: - command_dict['memory_slot'] = memory_slots + command_dict["memory_slot"] = memory_slots if register_slots: - command_dict['register_slot'] = register_slots + command_dict["register_slot"] = register_slots if discriminators: num_discriminators = len(discriminators) if num_discriminators == len(qubits) or num_discriminators == 1: - command_dict['discriminators'] = discriminators + command_dict["discriminators"] = discriminators else: raise QiskitError( - 'A discriminator must be supplied for every acquisition or a single ' - 'discriminator for all acquisitions.' - ) + "A discriminator must be supplied for every acquisition or a single " + "discriminator for all acquisitions." + ) if kernels: num_kernels = len(kernels) if num_kernels == len(qubits) or num_kernels == 1: - command_dict['kernels'] = kernels + command_dict["kernels"] = kernels else: raise QiskitError( - 'A kernel must be supplied for every acquisition or a single ' - 'kernel for all acquisitions.' - ) + "A kernel must be supplied for every acquisition or a single " + "kernel for all acquisitions." + ) return self._qobj_model(**command_dict) @bind_instruction(instructions.SetFrequency) def convert_set_frequency(self, shift, instruction): - """ Return converted `SetFrequencyInstruction`. + """Return converted `SetFrequencyInstruction`. Args: shift (int): Offset time. @@ -283,10 +293,10 @@ def convert_set_frequency(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'setf', - 't0': shift+instruction.start_time, - 'ch': instruction.channel.name, - 'frequency': instruction.frequency / 1e9 + "name": "setf", + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "frequency": instruction.frequency / 1e9, } return self._qobj_model(**command_dict) @@ -302,10 +312,10 @@ def convert_shift_frequency(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'shiftf', - 't0': shift+instruction.start_time, - 'ch': instruction.channel.name, - 'frequency': instruction.frequency / 1e9 + "name": "shiftf", + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "frequency": instruction.frequency / 1e9, } return self._qobj_model(**command_dict) @@ -320,10 +330,10 @@ def convert_set_phase(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'setp', - 't0': shift + instruction.start_time, - 'ch': instruction.channel.name, - 'phase': instruction.phase + "name": "setp", + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "phase": instruction.phase, } return self._qobj_model(**command_dict) @@ -338,10 +348,10 @@ def convert_shift_phase(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'fc', - 't0': shift + instruction.start_time, - 'ch': instruction.channel.name, - 'phase': instruction.phase + "name": "fc", + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "phase": instruction.phase, } return self._qobj_model(**command_dict) @@ -356,10 +366,10 @@ def convert_delay(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'delay', - 't0': shift + instruction.start_time, - 'ch': instruction.channel.name, - 'duration': instruction.duration + "name": "delay", + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "duration": instruction.duration, } return self._qobj_model(**command_dict) @@ -375,17 +385,17 @@ def convert_play(self, shift, instruction): """ if isinstance(instruction.pulse, library.ParametricPulse): command_dict = { - 'name': 'parametric_pulse', - 'pulse_shape': ParametricPulseShapes(type(instruction.pulse)).name, - 't0': shift + instruction.start_time, - 'ch': instruction.channel.name, - 'parameters': instruction.pulse.parameters + "name": "parametric_pulse", + "pulse_shape": ParametricPulseShapes(type(instruction.pulse)).name, + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, + "parameters": instruction.pulse.parameters, } else: command_dict = { - 'name': instruction.name, - 't0': shift + instruction.start_time, - 'ch': instruction.channel.name + "name": instruction.name, + "t0": shift + instruction.start_time, + "ch": instruction.channel.name, } return self._qobj_model(**command_dict) @@ -401,20 +411,20 @@ def convert_snapshot(self, shift, instruction): dict: Dictionary of required parameters. """ command_dict = { - 'name': 'snapshot', - 't0': shift + instruction.start_time, - 'label': instruction.label, - 'type': instruction.type + "name": "snapshot", + "t0": shift + instruction.start_time, + "label": instruction.label, + "type": instruction.type, } return self._qobj_model(**command_dict) class QobjToInstructionConverter: - """Converts Qobj models to pulse Instructions - """ + """Converts Qobj models to pulse Instructions""" + # class level tracking of conversion methods bind_name = ConversionMethodBinder() - chan_regex = re.compile(r'([a-zA-Z]+)(\d+)') + chan_regex = re.compile(r"([a-zA-Z]+)(\d+)") def __init__(self, pulse_library, **run_config): """Create new converter. @@ -455,7 +465,7 @@ def get_channel(self, channel: str) -> channels.PulseChannel: elif prefix == channels.ControlChannel.prefix: return channels.ControlChannel(index) - raise QiskitError('Channel %s is not valid' % channel) + raise QiskitError("Channel %s is not valid" % channel) @staticmethod def disassemble_value(value_expr: Union[float, str]) -> Union[float, ParameterExpression]: @@ -475,7 +485,7 @@ def disassemble_value(value_expr: Union[float, str]) -> Union[float, ParameterEx value_expr = str_expr(**{pname: Parameter(pname) for pname in str_expr.params}) return value_expr - @bind_name('acquire') + @bind_name("acquire") def convert_acquire(self, instruction): """Return converted `Acquire`. @@ -491,30 +501,35 @@ def convert_acquire(self, instruction): mem_slots = [channels.MemorySlot(instruction.memory_slot[i]) for i in range(len(qubits))] - if hasattr(instruction, 'register_slot'): - register_slots = [channels.RegisterSlot(instruction.register_slot[i]) - for i in range(len(qubits))] + if hasattr(instruction, "register_slot"): + register_slots = [ + channels.RegisterSlot(instruction.register_slot[i]) for i in range(len(qubits)) + ] else: register_slots = [None] * len(qubits) - discriminators = (instruction.discriminators - if hasattr(instruction, 'discriminators') else None) + discriminators = ( + instruction.discriminators if hasattr(instruction, "discriminators") else None + ) if not isinstance(discriminators, list): discriminators = [discriminators] if any(discriminators[i] != discriminators[0] for i in range(len(discriminators))): - warnings.warn("Can currently only support one discriminator per acquire. Defaulting " - "to first discriminator entry.") + warnings.warn( + "Can currently only support one discriminator per acquire. Defaulting " + "to first discriminator entry." + ) discriminator = discriminators[0] if discriminator: discriminator = Discriminator(name=discriminators[0].name, **discriminators[0].params) - kernels = (instruction.kernels - if hasattr(instruction, 'kernels') else None) + kernels = instruction.kernels if hasattr(instruction, "kernels") else None if not isinstance(kernels, list): kernels = [kernels] if any(kernels[0] != kernels[i] for i in range(len(kernels))): - warnings.warn("Can currently only support one kernel per acquire. Defaulting to first " - "kernel entry.") + warnings.warn( + "Can currently only support one kernel per acquire. Defaulting to first " + "kernel entry." + ) kernel = kernels[0] if kernel: kernel = Kernel(name=kernels[0].name, **kernels[0].params) @@ -522,13 +537,21 @@ def convert_acquire(self, instruction): schedule = Schedule() for acquire_channel, mem_slot, reg_slot in zip(acquire_channels, mem_slots, register_slots): - schedule |= instructions.Acquire(duration, acquire_channel, mem_slot=mem_slot, - reg_slot=reg_slot, kernel=kernel, - discriminator=discriminator) << t0 + schedule |= ( + instructions.Acquire( + duration, + acquire_channel, + mem_slot=mem_slot, + reg_slot=reg_slot, + kernel=kernel, + discriminator=discriminator, + ) + << t0 + ) return schedule - @bind_name('setp') + @bind_name("setp") def convert_set_phase(self, instruction): """Return converted `SetPhase`. @@ -543,7 +566,7 @@ def convert_set_phase(self, instruction): return instructions.SetPhase(phase, channel) << t0 - @bind_name('fc') + @bind_name("fc") def convert_shift_phase(self, instruction): """Return converted `ShiftPhase`. @@ -558,7 +581,7 @@ def convert_shift_phase(self, instruction): return instructions.ShiftPhase(phase, channel) << t0 - @bind_name('setf') + @bind_name("setf") def convert_set_frequency(self, instruction): """Return converted `SetFrequencyInstruction`. @@ -575,7 +598,7 @@ def convert_set_frequency(self, instruction): return instructions.SetFrequency(frequency, channel) << t0 - @bind_name('shiftf') + @bind_name("shiftf") def convert_shift_frequency(self, instruction): """Return converted `ShiftFrequency`. @@ -593,7 +616,7 @@ def convert_shift_frequency(self, instruction): return instructions.ShiftFrequency(frequency, channel) << t0 - @bind_name('delay') + @bind_name("delay") def convert_delay(self, instruction): """Return converted `Delay`. @@ -630,7 +653,7 @@ def convert_named_drive(self, instruction): channel = self.get_channel(instruction.ch) return instructions.Play(pulse, channel) << t0 - @bind_name('parametric_pulse') + @bind_name("parametric_pulse") def convert_parametric(self, instruction): """Return the ParametricPulse implementation that is described by the instruction. @@ -655,17 +678,18 @@ def convert_parametric(self, instruction): pulse_name = instruction.label except AttributeError: sorted_params = sorted(tuple(instruction.parameters.items()), key=lambda x: x[0]) - base_str = '{pulse}_{params}'.format( - pulse=instruction.pulse_shape, - params=str(sorted_params)) - short_pulse_id = hashlib.md5(base_str.encode('utf-8')).hexdigest()[:4] - pulse_name = '{0}_{1}'.format(instruction.pulse_shape, short_pulse_id) - - pulse = ParametricPulseShapes[instruction.pulse_shape].value(**instruction.parameters, - name=pulse_name) + base_str = "{pulse}_{params}".format( + pulse=instruction.pulse_shape, params=str(sorted_params) + ) + short_pulse_id = hashlib.md5(base_str.encode("utf-8")).hexdigest()[:4] + pulse_name = "{0}_{1}".format(instruction.pulse_shape, short_pulse_id) + + pulse = ParametricPulseShapes[instruction.pulse_shape].value( + **instruction.parameters, name=pulse_name + ) return instructions.Play(pulse, channel) << t0 - @bind_name('snapshot') + @bind_name("snapshot") def convert_snapshot(self, instruction): """Return converted `Snapshot`. diff --git a/qiskit/qobj/pulse_qobj.py b/qiskit/qobj/pulse_qobj.py index 3fa7d1316cc8..5be4d677a149 100644 --- a/qiskit/qobj/pulse_qobj.py +++ b/qiskit/qobj/pulse_qobj.py @@ -49,9 +49,9 @@ def to_dict(self): Returns: dict: The dictionary form of the QasmMeasurementOption. """ - out_dict = {'name': self.name} - if hasattr(self, 'params'): - out_dict['params'] = self.params + out_dict = {"name": self.name} + if hasattr(self, "params"): + out_dict["params"] = self.params return out_dict @classmethod @@ -64,7 +64,7 @@ def from_dict(cls, data): Returns: QobjMeasurementOption: The object from the input dictionary. """ - name = data.pop('name') + name = data.pop("name") return cls(name, **data) def __eq__(self, other): @@ -77,15 +77,42 @@ def __eq__(self, other): class PulseQobjInstruction: """A class representing a single instruction in an PulseQobj Experiment.""" - _COMMON_ATTRS = ['ch', 'conditional', 'val', 'phase', 'frequency', - 'duration', 'qubits', 'memory_slot', 'register_slot', - 'label', 'type', 'pulse_shape', 'parameters'] - - def __init__(self, name, t0, ch=None, conditional=None, val=None, phase=None, - duration=None, qubits=None, memory_slot=None, - register_slot=None, kernels=None, discriminators=None, - label=None, type=None, pulse_shape=None, - parameters=None, frequency=None): + _COMMON_ATTRS = [ + "ch", + "conditional", + "val", + "phase", + "frequency", + "duration", + "qubits", + "memory_slot", + "register_slot", + "label", + "type", + "pulse_shape", + "parameters", + ] + + def __init__( + self, + name, + t0, + ch=None, + conditional=None, + val=None, + phase=None, + duration=None, + qubits=None, + memory_slot=None, + register_slot=None, + kernels=None, + discriminators=None, + label=None, + type=None, + pulse_shape=None, + parameters=None, + frequency=None, + ): """Instantiate a new PulseQobjInstruction object. Args: @@ -163,18 +190,14 @@ def to_dict(self): Returns: dict: The dictionary form of the PulseQobjInstruction. """ - out_dict = { - 'name': self.name, - 't0': self.t0 - } + out_dict = {"name": self.name, "t0": self.t0} for attr in self._COMMON_ATTRS: if hasattr(self, attr): out_dict[attr] = getattr(self, attr) - if hasattr(self, 'kernels'): - out_dict['kernels'] = [x.to_dict() for x in self.kernels] - if hasattr(self, 'discriminators'): - out_dict['discriminators'] = [ - x.to_dict() for x in self.discriminators] + if hasattr(self, "kernels"): + out_dict["kernels"] = [x.to_dict() for x in self.kernels] + if hasattr(self, "discriminators"): + out_dict["discriminators"] = [x.to_dict() for x in self.discriminators] return out_dict def __repr__(self): @@ -186,7 +209,7 @@ def __repr__(self): out += ', %s="%s"' % (attr, attr_val) else: out += ", %s=%s" % (attr, attr_val) - out += ')' + out += ")" return out def __str__(self): @@ -194,7 +217,7 @@ def __str__(self): out += "\t\tt0: %s\n" % self.t0 for attr in self._COMMON_ATTRS: if hasattr(self, attr): - out += '\t\t%s: %s\n' % (attr, getattr(self, attr)) + out += "\t\t%s: %s\n" % (attr, getattr(self, attr)) return out @classmethod @@ -207,19 +230,18 @@ def from_dict(cls, data): Returns: PulseQobjInstruction: The object from the input dictionary. """ - t0 = data.pop('t0') - name = data.pop('name') - if 'kernels' in data: - kernels = data.pop('kernels') + t0 = data.pop("t0") + name = data.pop("name") + if "kernels" in data: + kernels = data.pop("kernels") kernel_obj = [QobjMeasurementOption.from_dict(x) for x in kernels] - data['kernels'] = kernel_obj - if 'discriminators' in data: - discriminators = data.pop('discriminators') - discriminators_obj = [ - QobjMeasurementOption.from_dict(x) for x in discriminators] - data['discriminators'] = discriminators_obj - if 'parameters' in data and 'amp' in data['parameters']: - data['parameters']['amp'] = _to_complex(data['parameters']['amp']) + data["kernels"] = kernel_obj + if "discriminators" in data: + discriminators = data.pop("discriminators") + discriminators_obj = [QobjMeasurementOption.from_dict(x) for x in discriminators] + data["discriminators"] = discriminators_obj + if "parameters" in data and "amp" in data["parameters"]: + data["parameters"]["amp"] = _to_complex(data["parameters"]["amp"]) return cls(name, t0, **data) @@ -250,10 +272,22 @@ def _to_complex(value: Union[List[float], complex]) -> complex: class PulseQobjConfig(QobjDictField): """A configuration for a Pulse Qobj.""" - def __init__(self, meas_level, meas_return, pulse_library, - qubit_lo_freq, meas_lo_freq, memory_slot_size=None, - rep_time=None, rep_delay=None, shots=None, max_credits=None, - seed_simulator=None, memory_slots=None, **kwargs): + def __init__( + self, + meas_level, + meas_return, + pulse_library, + qubit_lo_freq, + meas_lo_freq, + memory_slot_size=None, + rep_time=None, + rep_delay=None, + shots=None, + max_credits=None, + seed_simulator=None, + memory_slots=None, + **kwargs, + ): """Instantiate a PulseQobjConfig object. Args: @@ -315,9 +349,8 @@ def to_dict(self): dict: The dictionary form of the PulseQobjConfig. """ out_dict = copy.copy(self.__dict__) - if hasattr(self, 'pulse_library'): - out_dict['pulse_library'] = [ - x.to_dict() for x in self.pulse_library] + if hasattr(self, "pulse_library"): + out_dict["pulse_library"] = [x.to_dict() for x in self.pulse_library] return out_dict @@ -331,10 +364,10 @@ def from_dict(cls, data): Returns: PulseQobjConfig: The object from the input dictionary. """ - if 'pulse_library' in data: - pulse_lib = data.pop('pulse_library') + if "pulse_library" in data: + pulse_lib = data.pop("pulse_library") pulse_lib_obj = [PulseLibraryItem.from_dict(x) for x in pulse_lib] - data['pulse_library'] = pulse_lib_obj + data["pulse_library"] = pulse_lib_obj return cls(**data) @@ -365,43 +398,41 @@ def to_dict(self): Returns: dict: The dictionary form of the PulseQobjExperiment. """ - out_dict = { - 'instructions': [x.to_dict() for x in self.instructions] - } - if hasattr(self, 'config'): - out_dict['config'] = self.config.to_dict() - if hasattr(self, 'header'): - out_dict['header'] = self.header.to_dict() + out_dict = {"instructions": [x.to_dict() for x in self.instructions]} + if hasattr(self, "config"): + out_dict["config"] = self.config.to_dict() + if hasattr(self, "header"): + out_dict["header"] = self.header.to_dict() return out_dict def __repr__(self): instructions_str = [repr(x) for x in self.instructions] - instructions_repr = '[' + ', '.join(instructions_str) + ']' + instructions_repr = "[" + ", ".join(instructions_str) + "]" out = "PulseQobjExperiment(" out += instructions_repr - if hasattr(self, 'config') or hasattr(self, 'header'): - out += ', ' - if hasattr(self, 'config'): + if hasattr(self, "config") or hasattr(self, "header"): + out += ", " + if hasattr(self, "config"): out += "config=" + str(repr(self.config)) + ", " - if hasattr(self, 'header'): + if hasattr(self, "header"): out += "header=" + str(repr(self.header)) + ", " - out += ')' + out += ")" return out def __str__(self): - out = '\nPulse Experiment:\n' - if hasattr(self, 'config'): + out = "\nPulse Experiment:\n" + if hasattr(self, "config"): config = pprint.pformat(self.config.to_dict()) else: - config = '{}' - if hasattr(self, 'header'): + config = "{}" + if hasattr(self, "header"): header = pprint.pformat(self.header.to_dict() or {}) else: - header = '{}' - out += 'Header:\n%s\n' % header - out += 'Config:\n%s\n\n' % config + header = "{}" + out += "Header:\n%s\n" % header + out += "Config:\n%s\n\n" % config for instruction in self.instructions: - out += '\t%s\n' % instruction + out += "\t%s\n" % instruction return out @classmethod @@ -415,16 +446,16 @@ def from_dict(cls, data): PulseQobjExperiment: The object from the input dictionary. """ config = None - if 'config' in data: - config = PulseQobjExperimentConfig.from_dict(data.pop('config')) + if "config" in data: + config = PulseQobjExperimentConfig.from_dict(data.pop("config")) header = None - if 'header' in data: - header = QobjExperimentHeader.from_dict(data.pop('header')) + if "header" in data: + header = QobjExperimentHeader.from_dict(data.pop("header")) instructions = None - if 'instructions' in data: + if "instructions" in data: instructions = [ - PulseQobjInstruction.from_dict( - inst) for inst in data.pop('instructions')] + PulseQobjInstruction.from_dict(inst) for inst in data.pop("instructions") + ] return cls(instructions, config, header) def __eq__(self, other): @@ -469,8 +500,7 @@ def __init__(self, name, samples): """ self.name = name if isinstance(samples[0], list): - self.samples = numpy.array( - [complex(sample[0], sample[1]) for sample in samples]) + self.samples = numpy.array([complex(sample[0], sample[1]) for sample in samples]) else: self.samples = samples @@ -480,7 +510,7 @@ def to_dict(self): Returns: dict: The dictionary form of the PulseLibraryItem. """ - return {'name': self.name, 'samples': self.samples} + return {"name": self.name, "samples": self.samples} @classmethod def from_dict(cls, data): @@ -498,8 +528,7 @@ def __repr__(self): return "PulseLibraryItem(%s, %s)" % (self.name, repr(self.samples)) def __str__(self): - return "Pulse Library Item:\n\tname: %s\n\tsamples: %s" % ( - self.name, self.samples) + return "Pulse Library Item:\n\tname: %s\n\tsamples: %s" % (self.name, self.samples) def __eq__(self, other): if isinstance(other, PulseLibraryItem): @@ -511,8 +540,7 @@ def __eq__(self, other): class PulseQobj: """A Pulse Qobj.""" - def __init__(self, qobj_id, config, experiments, - header=None): + def __init__(self, qobj_id, config, experiments, header=None): """Instantiate a new Pulse Qobj Object. Each Pulse Qobj object is used to represent a single payload that will @@ -531,12 +559,13 @@ def __init__(self, qobj_id, config, experiments, self.config = config self.header = header or QobjHeader() self.experiments = experiments - self.type = 'PULSE' - self.schema_version = '1.2.0' + self.type = "PULSE" + self.schema_version = "1.2.0" def _validate_json_schema(self, out_dict): class PulseQobjEncoder(json.JSONEncoder): """A json encoder for pulse qobj""" + def default(self, obj): if isinstance(obj, numpy.ndarray): return obj.tolist() @@ -549,10 +578,13 @@ def default(self, obj): def __repr__(self): experiments_str = [repr(x) for x in self.experiments] - experiments_repr = '[' + ', '.join(experiments_str) + ']' + experiments_repr = "[" + ", ".join(experiments_str) + "]" out = "PulseQobj(qobj_id='%s', config=%s, experiments=%s, header=%s)" % ( - self.qobj_id, repr(self.config), experiments_repr, - repr(self.header)) + self.qobj_id, + repr(self.config), + experiments_repr, + repr(self.header), + ) return out def __str__(self): @@ -598,12 +630,12 @@ def default(self, obj): dict: A dictionary representation of the PulseQobj object """ out_dict = { - 'qobj_id': self.qobj_id, - 'header': self.header.to_dict(), - 'config': self.config.to_dict(), - 'schema_version': self.schema_version, - 'type': self.type, - 'experiments': [x.to_dict() for x in self.experiments] + "qobj_id": self.qobj_id, + "header": self.header.to_dict(), + "config": self.config.to_dict(), + "schema_version": self.schema_version, + "type": self.type, + "experiments": [x.to_dict() for x in self.experiments], } if validate: warnings.warn( @@ -611,8 +643,10 @@ def default(self, obj): "is deprecated and will be removed in a future release. " "If you're relying on this schema validation you should " "pull the schemas from the Qiskit/ibmq-schemas and directly " - "validate your payloads with that", DeprecationWarning, - stacklevel=2) + "validate your payloads with that", + DeprecationWarning, + stacklevel=2, + ) self._validate_json_schema(out_dict) return out_dict @@ -629,19 +663,18 @@ def from_dict(cls, data): PulseQobj: The PulseQobj from the input dictionary. """ config = None - if 'config' in data: - config = PulseQobjConfig.from_dict(data['config']) + if "config" in data: + config = PulseQobjConfig.from_dict(data["config"]) experiments = None - if 'experiments' in data: - experiments = [ - PulseQobjExperiment.from_dict( - exp) for exp in data['experiments']] + if "experiments" in data: + experiments = [PulseQobjExperiment.from_dict(exp) for exp in data["experiments"]] header = None - if 'header' in data: - header = QobjHeader.from_dict(data['header']) + if "header" in data: + header = QobjHeader.from_dict(data["header"]) - return cls(qobj_id=data.get('qobj_id'), config=config, - experiments=experiments, header=header) + return cls( + qobj_id=data.get("qobj_id"), config=config, experiments=experiments, header=header + ) def __eq__(self, other): if isinstance(other, PulseQobj): diff --git a/qiskit/qobj/qasm_qobj.py b/qiskit/qobj/qasm_qobj.py index 1ae6a0955f50..bcb096830154 100644 --- a/qiskit/qobj/qasm_qobj.py +++ b/qiskit/qobj/qasm_qobj.py @@ -30,9 +30,21 @@ class QasmQobjInstruction: """A class representing a single instruction in an QasmQobj Experiment.""" - def __init__(self, name, params=None, qubits=None, register=None, - memory=None, condition=None, conditional=None, label=None, - mask=None, relation=None, val=None, snapshot_type=None): + def __init__( + self, + name, + params=None, + qubits=None, + register=None, + memory=None, + condition=None, + conditional=None, + label=None, + mask=None, + relation=None, + val=None, + snapshot_type=None, + ): """Instantiate a new QasmQobjInstruction object. Args: @@ -95,14 +107,24 @@ def to_dict(self): Returns: dict: The dictionary form of the QasmQobjInstruction. """ - out_dict = {'name': self.name} - for attr in ['params', 'qubits', 'register', 'memory', '_condition', - 'conditional', 'label', 'mask', 'relation', 'val', - 'snapshot_type']: + out_dict = {"name": self.name} + for attr in [ + "params", + "qubits", + "register", + "memory", + "_condition", + "conditional", + "label", + "mask", + "relation", + "val", + "snapshot_type", + ]: if hasattr(self, attr): # TODO: Remove the param type conversion when Aer understands # ParameterExpression type - if attr == 'params': + if attr == "params": params = [] for param in list(getattr(self, attr)): if isinstance(param, ParameterExpression): @@ -117,25 +139,45 @@ def to_dict(self): def __repr__(self): out = "QasmQobjInstruction(name='%s'" % self.name - for attr in ['params', 'qubits', 'register', 'memory', '_condition', - 'conditional', 'label', 'mask', 'relation', 'val', - 'snapshot_type']: + for attr in [ + "params", + "qubits", + "register", + "memory", + "_condition", + "conditional", + "label", + "mask", + "relation", + "val", + "snapshot_type", + ]: attr_val = getattr(self, attr, None) if attr_val is not None: if isinstance(attr_val, str): out += ', %s="%s"' % (attr, attr_val) else: out += ", %s=%s" % (attr, attr_val) - out += ')' + out += ")" return out def __str__(self): out = "Instruction: %s\n" % self.name - for attr in ['params', 'qubits', 'register', 'memory', '_condition', - 'conditional', 'label', 'mask', 'relation', 'val', - 'snapshot_type']: + for attr in [ + "params", + "qubits", + "register", + "memory", + "_condition", + "conditional", + "label", + "mask", + "relation", + "val", + "snapshot_type", + ]: if hasattr(self, attr): - out += '\t\t%s: %s\n' % (attr, getattr(self, attr)) + out += "\t\t%s: %s\n" % (attr, getattr(self, attr)) return out @classmethod @@ -148,7 +190,7 @@ def from_dict(cls, data): Returns: QasmQobjInstruction: The object from the input dictionary. """ - name = data.pop('name') + name = data.pop("name") return cls(name, **data) def __eq__(self, other): @@ -179,19 +221,22 @@ def __init__(self, config=None, header=None, instructions=None): def __repr__(self): instructions_str = [repr(x) for x in self.instructions] - instructions_repr = '[' + ', '.join(instructions_str) + ']' + instructions_repr = "[" + ", ".join(instructions_str) + "]" out = "QasmQobjExperiment(config=%s, header=%s, instructions=%s)" % ( - repr(self.config), repr(self.header), instructions_repr) + repr(self.config), + repr(self.header), + instructions_repr, + ) return out def __str__(self): - out = '\nQASM Experiment:\n' + out = "\nQASM Experiment:\n" config = pprint.pformat(self.config.to_dict()) header = pprint.pformat(self.header.to_dict()) - out += 'Header:\n%s\n' % header - out += 'Config:\n%s\n\n' % config + out += "Header:\n%s\n" % header + out += "Config:\n%s\n\n" % config for instruction in self.instructions: - out += '\t%s\n' % instruction + out += "\t%s\n" % instruction return out def to_dict(self): @@ -201,9 +246,9 @@ def to_dict(self): dict: The dictionary form of the QasmQObjExperiment. """ out_dict = { - 'config': self.config.to_dict(), - 'header': self.header.to_dict(), - 'instructions': [x.to_dict() for x in self.instructions] + "config": self.config.to_dict(), + "header": self.header.to_dict(), + "instructions": [x.to_dict() for x in self.instructions], } return out_dict @@ -218,16 +263,16 @@ def from_dict(cls, data): QasmQobjExperiment: The object from the input dictionary. """ config = None - if 'config' in data: - config = QasmQobjExperimentConfig.from_dict(data.pop('config')) + if "config" in data: + config = QasmQobjExperimentConfig.from_dict(data.pop("config")) header = None - if 'header' in data: - header = QasmQobjExperimentHeader.from_dict(data.pop('header')) + if "header" in data: + header = QasmQobjExperimentHeader.from_dict(data.pop("header")) instructions = None - if 'instructions' in data: + if "instructions" in data: instructions = [ - QasmQobjInstruction.from_dict( - inst) for inst in data.pop('instructions')] + QasmQobjInstruction.from_dict(inst) for inst in data.pop("instructions") + ] return cls(config, header, instructions) def __eq__(self, other): @@ -240,20 +285,22 @@ def __eq__(self, other): class QasmQobjConfig(SimpleNamespace): """A configuration for a QASM Qobj.""" - def __init__(self, - shots=None, - max_credits=None, - seed_simulator=None, - memory=None, - parameter_binds=None, - meas_level=None, - meas_return=None, - memory_slots=None, - n_qubits=None, - pulse_library=None, - calibrations=None, - rep_delay=None, - **kwargs): + def __init__( + self, + shots=None, + max_credits=None, + seed_simulator=None, + memory=None, + parameter_binds=None, + meas_level=None, + meas_return=None, + memory_slots=None, + n_qubits=None, + pulse_library=None, + calibrations=None, + rep_delay=None, + **kwargs, + ): """Model for RunConfig. Args: @@ -321,11 +368,11 @@ def to_dict(self): dict: The dictionary form of the QasmQobjConfig. """ out_dict = copy.copy(self.__dict__) - if hasattr(self, 'pulse_library'): - out_dict['pulse_library'] = [x.to_dict() for x in self.pulse_library] + if hasattr(self, "pulse_library"): + out_dict["pulse_library"] = [x.to_dict() for x in self.pulse_library] - if hasattr(self, 'calibrations'): - out_dict['calibrations'] = self.calibrations.to_dict() + if hasattr(self, "calibrations"): + out_dict["calibrations"] = self.calibrations.to_dict() return out_dict @@ -339,14 +386,14 @@ def from_dict(cls, data): Returns: QasmQobjConfig: The object from the input dictionary. """ - if 'pulse_library' in data: - pulse_lib = data.pop('pulse_library') + if "pulse_library" in data: + pulse_lib = data.pop("pulse_library") pulse_lib_obj = [PulseLibraryItem.from_dict(x) for x in pulse_lib] - data['pulse_library'] = pulse_lib_obj + data["pulse_library"] = pulse_lib_obj - if 'calibrations' in data: - calibrations = data.pop('calibrations') - data['calibrations'] = QasmExperimentCalibrations.from_dict(calibrations) + if "calibrations" in data: + calibrations = data.pop("calibrations") + data["calibrations"] = QasmExperimentCalibrations.from_dict(calibrations) return cls(**data) @@ -359,6 +406,7 @@ def __eq__(self, other): class QasmQobjExperimentHeader(QobjDictField): """A header for a single QASM experiment in the qobj.""" + pass @@ -378,15 +426,15 @@ def __init__(self, calibrations=None, **kwargs): def to_dict(self): out_dict = copy.copy(self.__dict__) - if hasattr(self, 'calibrations'): - out_dict['calibrations'] = self.calibrations.to_dict() + if hasattr(self, "calibrations"): + out_dict["calibrations"] = self.calibrations.to_dict() return out_dict @classmethod def from_dict(cls, data): - if 'calibrations' in data: - calibrations = data.pop('calibrations') - data['calibrations'] = QasmExperimentCalibrations.from_dict(calibrations) + if "calibrations" in data: + calibrations = data.pop("calibrations") + data["calibrations"] = QasmExperimentCalibrations.from_dict(calibrations) return cls(**data) @@ -412,7 +460,7 @@ def to_dict(self): """ out_dict = copy.copy(self.__dict__) - out_dict['gates'] = [x.to_dict() for x in self.gates] + out_dict["gates"] = [x.to_dict() for x in self.gates] return out_dict @classmethod @@ -426,8 +474,8 @@ def from_dict(cls, data): Returns: QasmExperimentCalibrations: The QasmExperimentCalibrations from the input dictionary. """ - gates = data.pop('gates') - data['gates'] = [GateCalibration.from_dict(x) for x in gates] + gates = data.pop("gates") + data["gates"] = [GateCalibration.from_dict(x) for x in gates] return cls(**data) @@ -452,8 +500,14 @@ def __init__(self, name, qubits, params, instructions): self.instructions = instructions def __hash__(self): - return hash((self.name, tuple(self.qubits), tuple(self.params), - tuple(str(inst) for inst in self.instructions))) + return hash( + ( + self.name, + tuple(self.qubits), + tuple(self.params), + tuple(str(inst) for inst in self.instructions), + ) + ) def to_dict(self): """Return a dictionary format representation of the Gate Calibration. @@ -462,7 +516,7 @@ def to_dict(self): dict: The dictionary form of the GateCalibration. """ out_dict = copy.copy(self.__dict__) - out_dict['instructions'] = [x.to_dict() for x in self.instructions] + out_dict["instructions"] = [x.to_dict() for x in self.instructions] return out_dict @classmethod @@ -476,16 +530,15 @@ def from_dict(cls, data): Returns: GateCalibration: The GateCalibration from the input dictionary. """ - instructions = data.pop('instructions') - data['instructions'] = [PulseQobjInstruction.from_dict(x) for x in instructions] + instructions = data.pop("instructions") + data["instructions"] = [PulseQobjInstruction.from_dict(x) for x in instructions] return cls(**data) class QasmQobj: """A QASM Qobj.""" - def __init__(self, qobj_id=None, config=None, experiments=None, - header=None): + def __init__(self, qobj_id=None, config=None, experiments=None, header=None): """Instantiate a new QASM Qobj Object. Each QASM Qobj object is used to represent a single payload that will @@ -504,12 +557,13 @@ def __init__(self, qobj_id=None, config=None, experiments=None, self.config = config or QasmQobjConfig() self.experiments = experiments or [] self.qobj_id = qobj_id - self.type = 'QASM' - self.schema_version = '1.3.0' + self.type = "QASM" + self.schema_version = "1.3.0" def _validate_json_schema(self, out_dict): class QobjEncoder(json.JSONEncoder): """A json encoder for qobj""" + def default(self, obj): if isinstance(obj, numpy.ndarray): return obj.tolist() @@ -522,10 +576,13 @@ def default(self, obj): def __repr__(self): experiments_str = [repr(x) for x in self.experiments] - experiments_repr = '[' + ', '.join(experiments_str) + ']' + experiments_repr = "[" + ", ".join(experiments_str) + "]" out = "QasmQobj(qobj_id='%s', config=%s, experiments=%s, header=%s)" % ( - self.qobj_id, repr(self.config), experiments_repr, - repr(self.header)) + self.qobj_id, + repr(self.config), + experiments_repr, + repr(self.header), + ) return out def __str__(self): @@ -572,12 +629,12 @@ def default(self, obj): dict: A dictionary representation of the QasmQobj object """ out_dict = { - 'qobj_id': self.qobj_id, - 'header': self.header.to_dict(), - 'config': self.config.to_dict(), - 'schema_version': self.schema_version, - 'type': 'QASM', - 'experiments': [x.to_dict() for x in self.experiments] + "qobj_id": self.qobj_id, + "header": self.header.to_dict(), + "config": self.config.to_dict(), + "schema_version": self.schema_version, + "type": "QASM", + "experiments": [x.to_dict() for x in self.experiments], } if validate: warnings.warn( @@ -585,8 +642,10 @@ def default(self, obj): "deprecated and will be removed in a future release. " "If you're relying on this schema validation you should " "pull the schemas from the Qiskit/ibmq-schemas and directly " - "validate your payloads with that", DeprecationWarning, - stacklevel=2) + "validate your payloads with that", + DeprecationWarning, + stacklevel=2, + ) self._validate_json_schema(out_dict) return out_dict @@ -602,19 +661,18 @@ def from_dict(cls, data): QasmQobj: The QasmQobj from the input dictionary. """ config = None - if 'config' in data: - config = QasmQobjConfig.from_dict(data['config']) + if "config" in data: + config = QasmQobjConfig.from_dict(data["config"]) experiments = None - if 'experiments' in data: - experiments = [ - QasmQobjExperiment.from_dict( - exp) for exp in data['experiments']] + if "experiments" in data: + experiments = [QasmQobjExperiment.from_dict(exp) for exp in data["experiments"]] header = None - if 'header' in data: - header = QobjHeader.from_dict(data['header']) + if "header" in data: + header = QobjHeader.from_dict(data["header"]) - return cls(qobj_id=data.get('qobj_id'), config=config, - experiments=experiments, header=header) + return cls( + qobj_id=data.get("qobj_id"), config=config, experiments=experiments, header=header + ) def __eq__(self, other): if isinstance(other, QasmQobj): diff --git a/qiskit/qobj/utils.py b/qiskit/qobj/utils.py index c7c319e3bcca..2331f98d05bf 100644 --- a/qiskit/qobj/utils.py +++ b/qiskit/qobj/utils.py @@ -22,18 +22,21 @@ class QobjType(str, Enum): """Qobj.type allowed values.""" - QASM = 'QASM' - PULSE = 'PULSE' + + QASM = "QASM" + PULSE = "PULSE" class MeasReturnType(str, Enum): """PulseQobjConfig meas_return allowed values.""" - AVERAGE = 'avg' - SINGLE = 'single' + + AVERAGE = "avg" + SINGLE = "single" class MeasLevel(IntEnum): """MeasLevel allowed values.""" + RAW = 0 KERNELED = 1 CLASSIFIED = 2 @@ -53,8 +56,10 @@ def validate_qobj_against_schema(qobj): "deprecated and will be removed in a future release. " "If you're relying on this schema validation you should " "pull the schemas from the Qiskit/ibmq-schemas and directly " - "validate your payloads with that", DeprecationWarning, - stacklevel=2) + "validate your payloads with that", + DeprecationWarning, + stacklevel=2, + ) try: qobj.to_dict(validate=True) except JsonSchemaException as err: diff --git a/qiskit/quantum_info/__init__.py b/qiskit/quantum_info/__init__.py index 626525002c59..7cf677a0b3ae 100644 --- a/qiskit/quantum_info/__init__.py +++ b/qiskit/quantum_info/__init__.py @@ -119,29 +119,43 @@ decompose_clifford """ -from .operators import (Operator, ScalarOp, Pauli, Clifford, SparsePauliOp) -from .operators import (PauliTable, StabilizerTable, pauli_basis, pauli_group) +from .operators import Operator, ScalarOp, Pauli, Clifford, SparsePauliOp +from .operators import PauliTable, StabilizerTable, pauli_basis, pauli_group from .operators.channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM -from .operators.measures import (process_fidelity, - average_gate_fidelity, - gate_error, - diamond_norm) +from .operators.measures import process_fidelity, average_gate_fidelity, gate_error, diamond_norm from .operators.dihedral import CNOTDihedral from .states import Statevector, DensityMatrix -from .states import (partial_trace, state_fidelity, purity, entropy, - concurrence, entanglement_of_formation, - mutual_information, shannon_entropy) - -from .random import (random_quantum_channel, random_unitary, - random_clifford, random_pauli, random_pauli_table, - random_stabilizer_table, - random_hermitian, random_statevector, - random_density_matrix, - random_cnotdihedral) - -from .synthesis import (OneQubitEulerDecomposer, TwoQubitBasisDecomposer, - two_qubit_cnot_decompose, Quaternion, - decompose_clifford) +from .states import ( + partial_trace, + state_fidelity, + purity, + entropy, + concurrence, + entanglement_of_formation, + mutual_information, + shannon_entropy, +) + +from .random import ( + random_quantum_channel, + random_unitary, + random_clifford, + random_pauli, + random_pauli_table, + random_stabilizer_table, + random_hermitian, + random_statevector, + random_density_matrix, + random_cnotdihedral, +) + +from .synthesis import ( + OneQubitEulerDecomposer, + TwoQubitBasisDecomposer, + two_qubit_cnot_decompose, + Quaternion, + decompose_clifford, +) from .analysis import hellinger_distance, hellinger_fidelity diff --git a/qiskit/quantum_info/analysis/distance.py b/qiskit/quantum_info/analysis/distance.py index 5daf21f6e244..39bc04883266 100644 --- a/qiskit/quantum_info/analysis/distance.py +++ b/qiskit/quantum_info/analysis/distance.py @@ -33,22 +33,22 @@ def hellinger_distance(dist_p, dist_q): p_normed = {} for key, val in dist_p.items(): - p_normed[key] = val/p_sum + p_normed[key] = val / p_sum q_normed = {} for key, val in dist_q.items(): - q_normed[key] = val/q_sum + q_normed[key] = val / q_sum total = 0 for key, val in p_normed.items(): if key in q_normed.keys(): - total += (np.sqrt(val) - np.sqrt(q_normed[key]))**2 + total += (np.sqrt(val) - np.sqrt(q_normed[key])) ** 2 del q_normed[key] else: total += val total += sum(q_normed.values()) - dist = np.sqrt(total)/np.sqrt(2) + dist = np.sqrt(total) / np.sqrt(2) return dist @@ -97,4 +97,4 @@ def hellinger_fidelity(dist_p, dist_q): `Hellinger Distance @ wikipedia `_ """ dist = hellinger_distance(dist_p, dist_q) - return (1-dist**2)**2 + return (1 - dist ** 2) ** 2 diff --git a/qiskit/quantum_info/analysis/make_observable.py b/qiskit/quantum_info/analysis/make_observable.py index 38ee0ffa1fe1..856d6a0853d8 100644 --- a/qiskit/quantum_info/analysis/make_observable.py +++ b/qiskit/quantum_info/analysis/make_observable.py @@ -33,7 +33,7 @@ def make_dict_observable(matrix_observable): observable = np.array(matrix_observable) observable_size = len(observable) observable_bits = int(np.ceil(np.log2(observable_size))) - binary_formatter = '0{}b'.format(observable_bits) + binary_formatter = "0{}b".format(observable_bits) if observable.ndim == 2: observable = observable.diagonal() for state_no in range(observable_size): diff --git a/qiskit/quantum_info/operators/__init__.py b/qiskit/quantum_info/operators/__init__.py index 4725c69aa8af..1ef583140ed6 100644 --- a/qiskit/quantum_info/operators/__init__.py +++ b/qiskit/quantum_info/operators/__init__.py @@ -15,11 +15,8 @@ from .operator import Operator from .scalar_op import ScalarOp from .channel import Choi, SuperOp, Kraus, Stinespring, Chi, PTM -from .measures import (process_fidelity, - average_gate_fidelity, - gate_error, - diamond_norm) -from .symplectic import (Clifford, Pauli, SparsePauliOp, PauliTable, StabilizerTable) +from .measures import process_fidelity, average_gate_fidelity, gate_error, diamond_norm +from .symplectic import Clifford, Pauli, SparsePauliOp, PauliTable, StabilizerTable from .symplectic import pauli_basis from .pauli import pauli_group from .dihedral import CNOTDihedral diff --git a/qiskit/quantum_info/operators/base_operator.py b/qiskit/quantum_info/operators/base_operator.py index dc57cc97cf08..0292c8638729 100644 --- a/qiskit/quantum_info/operators/base_operator.py +++ b/qiskit/quantum_info/operators/base_operator.py @@ -27,8 +27,9 @@ class BaseOperator(GroupMixin, ABC): """Abstract operator base class.""" - def __init__(self, input_dims=None, output_dims=None, - num_qubits=None, shape=None, op_shape=None): + def __init__( + self, input_dims=None, output_dims=None, num_qubits=None, shape=None, op_shape=None + ): """Initialize a BaseOperator shape Args: @@ -48,10 +49,9 @@ def __init__(self, input_dims=None, output_dims=None, if op_shape: self._op_shape = op_shape else: - self._op_shape = OpShape.auto(shape=shape, - dims_l=output_dims, - dims_r=input_dims, - num_qubits=num_qubits) + self._op_shape = OpShape.auto( + shape=shape, dims_l=output_dims, dims_r=input_dims, num_qubits=num_qubits + ) # Set higher priority than Numpy array and matrix classes __array_priority__ = 20 @@ -64,14 +64,14 @@ def __call__(self, *qargs): if n_qargs not in self._op_shape.num_qargs: raise QiskitError( "qargs does not match the number of operator qargs " - f"({n_qargs} not in {self._op_shape.num_qargs})") + f"({n_qargs} not in {self._op_shape.num_qargs})" + ) ret = copy.copy(self) ret._qargs = tuple(qargs) return ret def __eq__(self, other): - return (isinstance(other, type(self)) - and self._op_shape == other._op_shape) + return isinstance(other, type(self)) and self._op_shape == other._op_shape @property def qargs(self): @@ -116,8 +116,8 @@ def reshape(self, input_dims=None, output_dims=None, num_qubits=None): subsystem output dimensions is not constant. """ new_shape = OpShape.auto( - dims_l=output_dims, dims_r=input_dims, num_qubits=num_qubits, - shape=self._op_shape.shape) + dims_l=output_dims, dims_r=input_dims, num_qubits=num_qubits, shape=self._op_shape.shape + ) ret = copy.copy(self) ret._op_shape = new_shape return ret diff --git a/qiskit/quantum_info/operators/channel/chi.py b/qiskit/quantum_info/operators/channel/chi.py index d403918c8295..dad3082fb642 100644 --- a/qiskit/quantum_info/operators/channel/chi.py +++ b/qiskit/quantum_info/operators/channel/chi.py @@ -50,6 +50,7 @@ class Chi(QuantumChannel): for open quantum systems*, Quant. Inf. Comp. 15, 0579-0811 (2015). `arXiv:1111.6950 [quant-ph] `_ """ + def __init__(self, data, input_dims=None, output_dims=None): """Initialize a quantum channel Chi-matrix operator. @@ -80,7 +81,7 @@ def __init__(self, data, input_dims=None, output_dims=None): # Determine input and output dimensions dim_l, dim_r = chi_mat.shape if dim_l != dim_r: - raise QiskitError('Invalid Chi-matrix input.') + raise QiskitError("Invalid Chi-matrix input.") if input_dims: input_dim = np.product(input_dims) if output_dims: @@ -108,7 +109,7 @@ def __init__(self, data, input_dims=None, output_dims=None): data = self._init_transformer(data) input_dim, output_dim = data.dim # Now that the input is an operator we convert it to a Chi object - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") chi_mat = _to_chi(rep, data._data, input_dim, output_dim) if input_dims is None: input_dims = data.input_dims() @@ -116,7 +117,7 @@ def __init__(self, data, input_dims=None, output_dims=None): output_dims = data.output_dims() # Check input is N-qubit channel num_qubits = int(np.log2(input_dim)) - if 2**num_qubits != input_dim or input_dim != output_dim: + if 2 ** num_qubits != input_dim or input_dim != output_dim: raise QiskitError("Input is not an n-qubit Chi matrix.") super().__init__(chi_mat, num_qubits=num_qubits) @@ -128,8 +129,7 @@ def __array__(self, dtype=None): @property def _bipartite_shape(self): """Return the shape for bipartite matrix""" - return (self._input_dim, self._output_dim, self._input_dim, - self._output_dim) + return (self._input_dim, self._output_dim, self._input_dim, self._output_dim) def _evolve(self, state, qargs=None): return SuperOp(self)._evolve(state, qargs) @@ -152,10 +152,9 @@ def adjoint(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if qargs is not None: - return Chi( - SuperOp(self).compose(other, qargs=qargs, front=front)) + return Chi(SuperOp(self).compose(other, qargs=qargs, front=front)) # If no qargs we compose via Choi representation to avoid an additional # representation conversion to SuperOp and then convert back to Chi return Chi(Choi(self).compose(other, front=front)) diff --git a/qiskit/quantum_info/operators/channel/choi.py b/qiskit/quantum_info/operators/channel/choi.py index 5dfff4ae7857..dfbf12be5761 100644 --- a/qiskit/quantum_info/operators/channel/choi.py +++ b/qiskit/quantum_info/operators/channel/choi.py @@ -90,7 +90,7 @@ def __init__(self, data, input_dims=None, output_dims=None): # Determine input and output dimensions dim_l, dim_r = choi_mat.shape if dim_l != dim_r: - raise QiskitError('Invalid Choi-matrix input.') + raise QiskitError("Invalid Choi-matrix input.") if input_dims: input_dim = np.product(input_dims) if output_dims: @@ -105,8 +105,9 @@ def __init__(self, data, input_dims=None, output_dims=None): # Check dimensions if input_dim * output_dim != dim_l: raise QiskitError("Invalid shape for input Choi-matrix.") - op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, - shape=(output_dim, input_dim)) + op_shape = OpShape.auto( + dims_l=output_dims, dims_r=input_dims, shape=(output_dim, input_dim) + ) else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. @@ -121,7 +122,7 @@ def __init__(self, data, input_dims=None, output_dims=None): op_shape = data._op_shape output_dim, input_dim = op_shape.shape # Now that the input is an operator we convert it to a Choi object - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") choi_mat = _to_choi(rep, data._data, input_dim, output_dim) super().__init__(choi_mat, op_shape=op_shape) @@ -133,8 +134,7 @@ def __array__(self, dtype=None): @property def _bipartite_shape(self): """Return the shape for bipartite matrix""" - return (self._input_dim, self._output_dim, self._input_dim, - self._output_dim) + return (self._input_dim, self._output_dim, self._input_dim, self._output_dim) def _evolve(self, state, qargs=None): return SuperOp(self)._evolve(state, qargs) @@ -161,10 +161,9 @@ def transpose(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if qargs is not None: - return Choi( - SuperOp(self).compose(other, qargs=qargs, front=front)) + return Choi(SuperOp(self).compose(other, qargs=qargs, front=front)) if not isinstance(other, Choi): other = Choi(other) @@ -179,8 +178,10 @@ def compose(self, other, qargs=None, front=False): second = np.reshape(other._data, other._bipartite_shape) # Contract Choi matrices for composition - data = np.reshape(np.einsum('iAjB,AkBl->ikjl', first, second), - (input_dim * output_dim, input_dim * output_dim)) + data = np.reshape( + np.einsum("iAjB,AkBl->ikjl", first, second), + (input_dim * output_dim, input_dim * output_dim), + ) ret = Choi(data) ret._op_shape = new_shape return ret @@ -199,9 +200,9 @@ def expand(self, other): def _tensor(cls, a, b): ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) - ret._data = _bipartite_tensor(a._data, b.data, - shape1=a._bipartite_shape, - shape2=b._bipartite_shape) + ret._data = _bipartite_tensor( + a._data, b.data, shape1=a._bipartite_shape, shape2=b._bipartite_shape + ) return ret diff --git a/qiskit/quantum_info/operators/channel/kraus.py b/qiskit/quantum_info/operators/channel/kraus.py index 685d65e3ac28..9dd06040148d 100644 --- a/qiskit/quantum_info/operators/channel/kraus.py +++ b/qiskit/quantum_info/operators/channel/kraus.py @@ -104,23 +104,20 @@ def __init__(self, data, input_dims=None, output_dims=None): for i in data[1:]: op = np.asarray(i, dtype=complex) if op.shape != shape: - raise QiskitError( - "Kraus operators are different dimensions.") + raise QiskitError("Kraus operators are different dimensions.") kraus.append(op) # Convert single Kraus set to general Kraus pair kraus = (kraus, None) # Check if generalized Kraus set ([A_i], [B_i]) for channel: # E(rho) = sum_i A_i * rho * B_i^dagger - elif isinstance(data, - tuple) and len(data) == 2 and len(data[0]) > 0: + elif isinstance(data, tuple) and len(data) == 2 and len(data[0]) > 0: kraus_left = [np.asarray(data[0][0], dtype=complex)] shape = kraus_left[0].shape for i in data[0][1:]: op = np.asarray(i, dtype=complex) if op.shape != shape: - raise QiskitError( - "Kraus operators are different dimensions.") + raise QiskitError("Kraus operators are different dimensions.") kraus_left.append(op) if data[1] is None: kraus = (kraus_left, None) @@ -129,14 +126,12 @@ def __init__(self, data, input_dims=None, output_dims=None): for i in data[1]: op = np.asarray(i, dtype=complex) if op.shape != shape: - raise QiskitError( - "Kraus operators are different dimensions.") + raise QiskitError("Kraus operators are different dimensions.") kraus_right.append(op) kraus = (kraus_left, kraus_right) else: raise QiskitError("Invalid input for Kraus channel.") - op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, - shape=kraus[0][0].shape) + op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, shape=kraus[0][0].shape) else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. @@ -151,7 +146,7 @@ def __init__(self, data, input_dims=None, output_dims=None): op_shape = data._op_shape output_dim, input_dim = op_shape.shape # Now that the input is an operator we convert it to a Kraus - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") kraus = _to_kraus(rep, data._data, input_dim, output_dim) # Initialize either single or general Kraus @@ -225,10 +220,9 @@ def adjoint(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if qargs is not None: - return Kraus( - SuperOp(self).compose(other, qargs=qargs, front=front)) + return Kraus(SuperOp(self).compose(other, qargs=qargs, front=front)) if not isinstance(other, Kraus): other = Kraus(other) @@ -287,13 +281,13 @@ def _tensor(cls, a, b): return ret def __add__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, QuantumChannel): other = Choi(other) return self._add(other, qargs=qargs) def __sub__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, QuantumChannel): other = Choi(other) return self._add(-other, qargs=qargs) diff --git a/qiskit/quantum_info/operators/channel/ptm.py b/qiskit/quantum_info/operators/channel/ptm.py index 17ee0b83cf57..80fee1c6b49c 100644 --- a/qiskit/quantum_info/operators/channel/ptm.py +++ b/qiskit/quantum_info/operators/channel/ptm.py @@ -98,7 +98,7 @@ def __init__(self, data, input_dims=None, output_dims=None): output_dim = np.product(input_dims) else: output_dim = int(np.sqrt(dout)) - if output_dim**2 != dout or input_dim**2 != din or input_dim != output_dim: + if output_dim ** 2 != dout or input_dim ** 2 != din or input_dim != output_dim: raise QiskitError("Invalid shape for PTM matrix.") else: # Otherwise we initialize by conversion from another Qiskit @@ -113,7 +113,7 @@ def __init__(self, data, input_dims=None, output_dims=None): data = self._init_transformer(data) input_dim, output_dim = data.dim # Now that the input is an operator we convert it to a PTM object - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") ptm = _to_ptm(rep, data._data, input_dim, output_dim) if input_dims is None: input_dims = data.input_dims() @@ -121,7 +121,7 @@ def __init__(self, data, input_dims=None, output_dims=None): output_dims = data.output_dims() # Check input is N-qubit channel num_qubits = int(np.log2(input_dim)) - if 2**num_qubits != input_dim or input_dim != output_dim: + if 2 ** num_qubits != input_dim or input_dim != output_dim: raise QiskitError("Input is not an n-qubit Pauli transfer matrix.") super().__init__(ptm, num_qubits=num_qubits) @@ -133,8 +133,7 @@ def __array__(self, dtype=None): @property def _bipartite_shape(self): """Return the shape for bipartite matrix""" - return (self._output_dim, self._output_dim, self._input_dim, - self._input_dim) + return (self._output_dim, self._output_dim, self._input_dim, self._input_dim) def _evolve(self, state, qargs=None): return SuperOp(self)._evolve(state, qargs) @@ -157,10 +156,9 @@ def adjoint(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if qargs is not None: - return PTM( - SuperOp(self).compose(other, qargs=qargs, front=front)) + return PTM(SuperOp(self).compose(other, qargs=qargs, front=front)) # Convert other to PTM if not isinstance(other, PTM): diff --git a/qiskit/quantum_info/operators/channel/quantum_channel.py b/qiskit/quantum_info/operators/channel/quantum_channel.py index f36fb48514bb..3d17a0ae4bef 100644 --- a/qiskit/quantum_info/operators/channel/quantum_channel.py +++ b/qiskit/quantum_info/operators/channel/quantum_channel.py @@ -48,19 +48,21 @@ def __init__(self, data, num_qubits=None, op_shape=None): super().__init__(num_qubits=num_qubits, op_shape=op_shape) def __repr__(self): - prefix = '{}('.format(self._channel_rep) - pad = len(prefix) * ' ' - return '{}{},\n{}input_dims={}, output_dims={})'.format( - prefix, np.array2string( - np.asarray(self.data), separator=', ', prefix=prefix), - pad, self.input_dims(), self.output_dims()) + prefix = "{}(".format(self._channel_rep) + pad = len(prefix) * " " + return "{}{},\n{}input_dims={}, output_dims={})".format( + prefix, + np.array2string(np.asarray(self.data), separator=", ", prefix=prefix), + pad, + self.input_dims(), + self.output_dims(), + ) def __eq__(self, other): """Test if two QuantumChannels are equal.""" if not super().__eq__(other): return False - return np.allclose( - self.data, other.data, rtol=self.rtol, atol=self.atol) + return np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) @property def data(self): @@ -141,16 +143,16 @@ def power(self, n): raise QiskitError("Can only take power with input_dim = output_dim.") rep = self._channel_rep input_dim, output_dim = self.dim - superop = _transform_rep(rep, 'SuperOp', self._data, input_dim, output_dim) + superop = _transform_rep(rep, "SuperOp", self._data, input_dim, output_dim) superop = np.linalg.matrix_power(superop, n) # Convert back to original representation ret = copy.copy(self) - ret._data = _transform_rep('SuperOp', rep, superop, input_dim, output_dim) + ret._data = _transform_rep("SuperOp", rep, superop, input_dim, output_dim) return ret def __sub__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, type(self)): other = type(self)(other) return self._add(-other, qargs=qargs) @@ -184,8 +186,7 @@ def _multiply(self, other): def is_cptp(self, atol=None, rtol=None): """Return True if completely-positive trace-preserving (CPTP).""" choi = _to_choi(self._channel_rep, self._data, *self.dim) - return self._is_cp_helper(choi, atol, rtol) and self._is_tp_helper( - choi, atol, rtol) + return self._is_cp_helper(choi, atol, rtol) and self._is_tp_helper(choi, atol, rtol) def is_tp(self, atol=None, rtol=None): """Test if a channel is trace-preserving (TP)""" @@ -223,16 +224,15 @@ def to_instruction(self): QiskitError: if input data is not an N-qubit CPTP quantum channel. """ from qiskit.circuit.instruction import Instruction + # Check if input is an N-qubit CPTP channel. num_qubits = int(np.log2(self._input_dim)) - if self._input_dim != self._output_dim or 2**num_qubits != self._input_dim: + if self._input_dim != self._output_dim or 2 ** num_qubits != self._input_dim: raise QiskitError( - 'Cannot convert QuantumChannel to Instruction: channel is not an N-qubit channel.' + "Cannot convert QuantumChannel to Instruction: channel is not an N-qubit channel." ) if not self.is_cptp(): - raise QiskitError( - 'Cannot convert QuantumChannel to Instruction: channel is not CPTP.' - ) + raise QiskitError("Cannot convert QuantumChannel to Instruction: channel is not CPTP.") # Next we convert to the Kraus representation. Since channel is CPTP we know # that there is only a single set of Kraus operators kraus, _ = _to_kraus(self._channel_rep, self._data, *self.dim) @@ -241,7 +241,7 @@ def to_instruction(self): # converting to an Operator and using its to_instruction method if len(kraus) == 1: return Operator(kraus[0]).to_instruction() - return Instruction('kraus', num_qubits, 0, kraus) + return Instruction("kraus", num_qubits, 0, kraus) def _is_cp_helper(self, choi, atol, rtol): """Test if a channel is completely-positive (CP)""" @@ -259,8 +259,7 @@ def _is_tp_helper(self, choi, atol, rtol): rtol = self.rtol # Check if the partial trace is the identity matrix d_in, d_out = self.dim - mat = np.trace( - np.reshape(choi, (d_in, d_out, d_in, d_out)), axis1=1, axis2=3) + mat = np.trace(np.reshape(choi, (d_in, d_out, d_in, d_out)), axis1=1, axis2=3) tp_cond = np.linalg.eigvalsh(mat - np.eye(len(mat))) zero = np.isclose(tp_cond, 0, atol=atol, rtol=rtol) return np.all(zero) @@ -271,11 +270,11 @@ def _format_state(self, state, density_matrix=False): shape = state.shape ndim = state.ndim if ndim > 2: - raise QiskitError('Input state is not a vector or matrix.') + raise QiskitError("Input state is not a vector or matrix.") # Flatten column-vector to vector if ndim == 2: if shape[1] != 1 and shape[1] != shape[0]: - raise QiskitError('Input state is not a vector or matrix.') + raise QiskitError("Input state is not a vector or matrix.") if shape[1] == 1: # flatten column-vector to vector state = np.reshape(state, shape[0]) @@ -310,13 +309,13 @@ def _init_transformer(cls, data): # the original object if isinstance(data, QuantumChannel): return data - if hasattr(data, 'to_quantumchannel'): + if hasattr(data, "to_quantumchannel"): # If the data object is not a QuantumChannel it will give # preference to a 'to_quantumchannel' attribute that allows # an arbitrary object to define its own conversion to any # quantum channel subclass. return data.to_quantumchannel() - if hasattr(data, 'to_channel'): + if hasattr(data, "to_channel"): # TODO: this 'to_channel' method is the same case as the above # but is used by current version of Aer. It should be removed # once Aer is nupdated to use `to_quantumchannel` diff --git a/qiskit/quantum_info/operators/channel/stinespring.py b/qiskit/quantum_info/operators/channel/stinespring.py index 88b4864e341d..ee615fb902b1 100644 --- a/qiskit/quantum_info/operators/channel/stinespring.py +++ b/qiskit/quantum_info/operators/channel/stinespring.py @@ -93,8 +93,7 @@ def __init__(self, data, input_dims=None, output_dims=None): if data[1] is None: stine = (np.asarray(data[0], dtype=complex), None) else: - stine = (np.asarray(data[0], dtype=complex), - np.asarray(data[1], dtype=complex)) + stine = (np.asarray(data[0], dtype=complex), np.asarray(data[1], dtype=complex)) dim_left, dim_right = stine[0].shape # If two Stinespring matrices check they are same shape @@ -108,8 +107,9 @@ def __init__(self, data, input_dims=None, output_dims=None): output_dim = input_dim if dim_left % output_dim != 0: raise QiskitError("Invalid output_dim") - op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, - shape=(output_dim, input_dim)) + op_shape = OpShape.auto( + dims_l=output_dims, dims_r=input_dims, shape=(output_dim, input_dim) + ) else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. @@ -125,7 +125,7 @@ def __init__(self, data, input_dims=None, output_dims=None): output_dim, input_dim = op_shape.shape # Now that the input is an operator we convert it to a # Stinespring operator - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") stine = _to_stinespring(rep, data._data, input_dim, output_dim) # Initialize either single or general Stinespring @@ -181,17 +181,16 @@ def transpose(self): for i, mat in enumerate(self._data): if mat is not None: stine[i] = np.reshape( - np.transpose(np.reshape(mat, (dout, dtr, din)), (2, 1, 0)), - (din * dtr, dout)) + np.transpose(np.reshape(mat, (dout, dtr, din)), (2, 1, 0)), (din * dtr, dout) + ) ret._data = (stine[0], stine[1]) return ret def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if qargs is not None: - return Stinespring( - SuperOp(self).compose(other, qargs=qargs, front=front)) + return Stinespring(SuperOp(self).compose(other, qargs=qargs, front=front)) # Otherwise we convert via Kraus representation rather than # superoperator to avoid unnecessary representation conversions return Stinespring(Kraus(self).compose(other, front=front)) @@ -222,9 +221,7 @@ def _tensor(cls, a, b): shape_out = (dout_a * dtr_a * dout_b * dtr_b, din_a * din_b) sab_l = np.kron(sa_l, sb_l) # Reravel indices - sab_l = np.reshape( - np.transpose(np.reshape(sab_l, shape_in), (0, 2, 1, 3, 4)), - shape_out) + sab_l = np.reshape(np.transpose(np.reshape(sab_l, shape_in), (0, 2, 1, 3, 4)), shape_out) # Compute right Stinespring op if sa_r is None and sb_r is None: @@ -237,21 +234,21 @@ def _tensor(cls, a, b): sab_r = np.kron(sa_r, sb_r) # Reravel indices sab_r = np.reshape( - np.transpose(np.reshape(sab_r, shape_in), (0, 2, 1, 3, 4)), - shape_out) + np.transpose(np.reshape(sab_r, shape_in), (0, 2, 1, 3, 4)), shape_out + ) ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) ret._data = (sab_l, sab_r) return ret def __add__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, QuantumChannel): other = Choi(other) return self._add(other, qargs=qargs) def __sub__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, QuantumChannel): other = Choi(other) return self._add(-other, qargs=qargs) diff --git a/qiskit/quantum_info/operators/channel/superop.py b/qiskit/quantum_info/operators/channel/superop.py index 9922c5bf1a4c..3367b6c58097 100644 --- a/qiskit/quantum_info/operators/channel/superop.py +++ b/qiskit/quantum_info/operators/channel/superop.py @@ -83,10 +83,11 @@ def __init__(self, data, input_dims=None, output_dims=None): dout, din = super_mat.shape input_dim = int(np.sqrt(din)) output_dim = int(np.sqrt(dout)) - if output_dim**2 != dout or input_dim**2 != din: + if output_dim ** 2 != dout or input_dim ** 2 != din: raise QiskitError("Invalid shape for SuperOp matrix.") - op_shape = OpShape.auto(dims_l=output_dims, dims_r=input_dims, - shape=(output_dim, input_dim)) + op_shape = OpShape.auto( + dims_l=output_dims, dims_r=input_dims, shape=(output_dim, input_dim) + ) else: # Otherwise we initialize by conversion from another Qiskit # object into the QuantumChannel. @@ -107,7 +108,7 @@ def __init__(self, data, input_dims=None, output_dims=None): # SuperOp object op_shape = data._op_shape input_dim, output_dim = data.dim - rep = getattr(data, '_channel_rep', 'Operator') + rep = getattr(data, "_channel_rep", "Operator") super_mat = _to_superop(rep, data._data, input_dim, output_dim) # Initialize QuantumChannel super().__init__(super_mat, op_shape=op_shape) @@ -121,13 +122,13 @@ def __array__(self, dtype=None): def _tensor_shape(self): """Return the tensor shape of the superoperator matrix""" return 2 * tuple(reversed(self._op_shape.dims_l())) + 2 * tuple( - reversed(self._op_shape.dims_r())) + reversed(self._op_shape.dims_r()) + ) @property def _bipartite_shape(self): """Return the shape for bipartite matrix""" - return (self._output_dim, self._output_dim, self._input_dim, - self._input_dim) + return (self._output_dim, self._output_dim, self._input_dim, self._input_dim) # --------------------------------------------------------------------- # BaseOperator methods @@ -164,14 +165,14 @@ def expand(self, other): def _tensor(cls, a, b): ret = copy.copy(a) ret._op_shape = a._op_shape.tensor(b._op_shape) - ret._data = _bipartite_tensor(a._data, b.data, - shape1=a._bipartite_shape, - shape2=b._bipartite_shape) + ret._data = _bipartite_tensor( + a._data, b.data, shape1=a._bipartite_shape, shape2=b._bipartite_shape + ) return ret def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, SuperOp): other = SuperOp(other) # Validate dimensions are compatible and return the composed @@ -208,12 +209,13 @@ def compose(self, other, qargs=None, front=False): tensor = np.reshape(self.data, self._tensor_shape) mat = np.reshape(other.data, other._tensor_shape) # Add first set of indices - indices = [2 * num_indices - 1 - qubit for qubit in qargs - ] + [num_indices - 1 - qubit for qubit in qargs] - final_shape = [np.product(output_dims)**2, np.product(input_dims)**2] + indices = [2 * num_indices - 1 - qubit for qubit in qargs] + [ + num_indices - 1 - qubit for qubit in qargs + ] + final_shape = [np.product(output_dims) ** 2, np.product(input_dims) ** 2] data = np.reshape( - Operator._einsum_matmul(tensor, mat, indices, shift, right_mul), - final_shape) + Operator._einsum_matmul(tensor, mat, indices, shift, right_mul), final_shape + ) ret = SuperOp(data, input_dims, output_dims) ret._op_shape = new_shape return ret @@ -252,10 +254,10 @@ def _evolve(self, state, qargs=None): ) # We reshape in column-major vectorization (Fortran order in Numpy) # since that is how the SuperOp is defined - vec = np.ravel(state.data, order='F') - mat = np.reshape(np.dot(self.data, vec), - (self._output_dim, self._output_dim), - order='F') + vec = np.ravel(state.data, order="F") + mat = np.reshape( + np.dot(self.data, vec), (self._output_dim, self._output_dim), order="F" + ) return DensityMatrix(mat, dims=self.output_dims()) # Otherwise we are applying an operator only to subsystems # Check dimensions of subsystems match the operator @@ -268,8 +270,9 @@ def _evolve(self, state, qargs=None): mat = np.reshape(self.data, self._tensor_shape) # Construct list of tensor indices of statevector to be contracted num_indices = len(state.dims()) - indices = [num_indices - 1 - qubit for qubit in qargs - ] + [2 * num_indices - 1 - qubit for qubit in qargs] + indices = [num_indices - 1 - qubit for qubit in qargs] + [ + 2 * num_indices - 1 - qubit for qubit in qargs + ] tensor = Operator._einsum_matmul(tensor, mat, indices) # Replace evolved dimensions new_dims = list(state.dims()) @@ -289,7 +292,7 @@ def _init_instruction(cls, instruction): instruction = instruction.to_instruction() # Initialize an identity superoperator of the correct size # of the circuit - op = SuperOp(np.eye(4**instruction.num_qubits)) + op = SuperOp(np.eye(4 ** instruction.num_qubits)) op._append_instruction(instruction) return op @@ -297,25 +300,23 @@ def _init_instruction(cls, instruction): def _instruction_to_superop(cls, obj): """Return superop for instruction if defined or None otherwise.""" if not isinstance(obj, Instruction): - raise QiskitError('Input is not an instruction.') + raise QiskitError("Input is not an instruction.") chan = None - if obj.name == 'reset': + if obj.name == "reset": # For superoperator evolution we can simulate a reset as # a non-unitary superoperator matrix - chan = SuperOp( - np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], - [0, 0, 0, 0]])) - if obj.name == 'kraus': + chan = SuperOp(np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]])) + if obj.name == "kraus": kraus = obj.params dim = len(kraus[0]) - chan = SuperOp(_to_superop('Kraus', (kraus, None), dim, dim)) - elif hasattr(obj, 'to_matrix'): + chan = SuperOp(_to_superop("Kraus", (kraus, None), dim, dim)) + elif hasattr(obj, "to_matrix"): # If instruction is a gate first we see if it has a # `to_matrix` definition and if so use that. try: kraus = [obj.to_matrix()] dim = len(kraus[0]) - chan = SuperOp(_to_superop('Kraus', (kraus, None), dim, dim)) + chan = SuperOp(_to_superop("Kraus", (kraus, None), dim, dim)) except QiskitError: pass return chan @@ -337,18 +338,18 @@ def _append_instruction(self, obj, qargs=None): # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if obj.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format( - obj.name)) + raise QiskitError("Cannot apply Instruction: {}".format(obj.name)) if not isinstance(obj.definition, QuantumCircuit): - raise QiskitError('{} instruction definition is {}; ' - 'expected QuantumCircuit'.format( - obj.name, type(obj.definition))) + raise QiskitError( + "{} instruction definition is {}; " + "expected QuantumCircuit".format(obj.name, type(obj.definition)) + ) qubit_indices = {bit: idx for idx, bit in enumerate(obj.definition.qubits)} for instr, qregs, cregs in obj.definition.data: if cregs: raise QiskitError( - 'Cannot apply instruction with classical registers: {}' - .format(instr.name)) + "Cannot apply instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register if qargs is None: new_qargs = [qubit_indices[tup] for tup in qregs] diff --git a/qiskit/quantum_info/operators/channel/transformations.py b/qiskit/quantum_info/operators/channel/transformations.py index b4879ae42c4b..42acfc6127cd 100644 --- a/qiskit/quantum_info/operators/channel/transformations.py +++ b/qiskit/quantum_info/operators/channel/transformations.py @@ -29,158 +29,157 @@ def _transform_rep(input_rep, output_rep, data, input_dim, output_dim): """Transform a QuantumChannel between representation.""" if input_rep == output_rep: return data - if output_rep == 'Choi': + if output_rep == "Choi": return _to_choi(input_rep, data, input_dim, output_dim) - if output_rep == 'Operator': + if output_rep == "Operator": return _to_operator(input_rep, data, input_dim, output_dim) - if output_rep == 'SuperOp': + if output_rep == "SuperOp": return _to_superop(input_rep, data, input_dim, output_dim) - if output_rep == 'Kraus': + if output_rep == "Kraus": return _to_kraus(input_rep, data, input_dim, output_dim) - if output_rep == 'Chi': + if output_rep == "Chi": return _to_chi(input_rep, data, input_dim, output_dim) - if output_rep == 'PTM': + if output_rep == "PTM": return _to_ptm(input_rep, data, input_dim, output_dim) - if output_rep == 'Stinespring': + if output_rep == "Stinespring": return _to_stinespring(input_rep, data, input_dim, output_dim) - raise QiskitError('Invalid QuantumChannel {}'.format(output_rep)) + raise QiskitError("Invalid QuantumChannel {}".format(output_rep)) def _to_choi(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the Choi representation.""" - if rep == 'Choi': + if rep == "Choi": return data - if rep == 'Operator': - return _from_operator('Choi', data, input_dim, output_dim) - if rep == 'SuperOp': + if rep == "Operator": + return _from_operator("Choi", data, input_dim, output_dim) + if rep == "SuperOp": return _superop_to_choi(data, input_dim, output_dim) - if rep == 'Kraus': + if rep == "Kraus": return _kraus_to_choi(data) - if rep == 'Chi': + if rep == "Chi": return _chi_to_choi(data, input_dim) - if rep == 'PTM': + if rep == "PTM": data = _ptm_to_superop(data, input_dim) return _superop_to_choi(data, input_dim, output_dim) - if rep == 'Stinespring': + if rep == "Stinespring": return _stinespring_to_choi(data, input_dim, output_dim) - raise QiskitError('Invalid QuantumChannel {}'.format(rep)) + raise QiskitError("Invalid QuantumChannel {}".format(rep)) def _to_superop(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the SuperOp representation.""" - if rep == 'SuperOp': + if rep == "SuperOp": return data - if rep == 'Operator': - return _from_operator('SuperOp', data, input_dim, output_dim) - if rep == 'Choi': + if rep == "Operator": + return _from_operator("SuperOp", data, input_dim, output_dim) + if rep == "Choi": return _choi_to_superop(data, input_dim, output_dim) - if rep == 'Kraus': + if rep == "Kraus": return _kraus_to_superop(data) - if rep == 'Chi': + if rep == "Chi": data = _chi_to_choi(data, input_dim) return _choi_to_superop(data, input_dim, output_dim) - if rep == 'PTM': + if rep == "PTM": return _ptm_to_superop(data, input_dim) - if rep == 'Stinespring': + if rep == "Stinespring": return _stinespring_to_superop(data, input_dim, output_dim) - raise QiskitError('Invalid QuantumChannel {}'.format(rep)) + raise QiskitError("Invalid QuantumChannel {}".format(rep)) def _to_kraus(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the Kraus representation.""" - if rep == 'Kraus': + if rep == "Kraus": return data - if rep == 'Stinespring': + if rep == "Stinespring": return _stinespring_to_kraus(data, output_dim) - if rep == 'Operator': - return _from_operator('Kraus', data, input_dim, output_dim) + if rep == "Operator": + return _from_operator("Kraus", data, input_dim, output_dim) # Convert via Choi and Kraus - if rep != 'Choi': + if rep != "Choi": data = _to_choi(rep, data, input_dim, output_dim) return _choi_to_kraus(data, input_dim, output_dim) def _to_chi(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the Chi representation.""" - if rep == 'Chi': + if rep == "Chi": return data # Check valid n-qubit input _check_nqubit_dim(input_dim, output_dim) - if rep == 'Operator': - return _from_operator('Chi', data, input_dim, output_dim) + if rep == "Operator": + return _from_operator("Chi", data, input_dim, output_dim) # Convert via Choi representation - if rep != 'Choi': + if rep != "Choi": data = _to_choi(rep, data, input_dim, output_dim) return _choi_to_chi(data, input_dim) def _to_ptm(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the PTM representation.""" - if rep == 'PTM': + if rep == "PTM": return data # Check valid n-qubit input _check_nqubit_dim(input_dim, output_dim) - if rep == 'Operator': - return _from_operator('PTM', data, input_dim, output_dim) + if rep == "Operator": + return _from_operator("PTM", data, input_dim, output_dim) # Convert via Superoperator representation - if rep != 'SuperOp': + if rep != "SuperOp": data = _to_superop(rep, data, input_dim, output_dim) return _superop_to_ptm(data, input_dim) def _to_stinespring(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the Stinespring representation.""" - if rep == 'Stinespring': + if rep == "Stinespring": return data - if rep == 'Operator': - return _from_operator('Stinespring', data, input_dim, output_dim) + if rep == "Operator": + return _from_operator("Stinespring", data, input_dim, output_dim) # Convert via Superoperator representation - if rep != 'Kraus': + if rep != "Kraus": data = _to_kraus(rep, data, input_dim, output_dim) return _kraus_to_stinespring(data, input_dim, output_dim) def _to_operator(rep, data, input_dim, output_dim): """Transform a QuantumChannel to the Operator representation.""" - if rep == 'Operator': + if rep == "Operator": return data - if rep == 'Stinespring': + if rep == "Stinespring": return _stinespring_to_operator(data, output_dim) # Convert via Kraus representation - if rep != 'Kraus': + if rep != "Kraus": data = _to_kraus(rep, data, input_dim, output_dim) return _kraus_to_operator(data) def _from_operator(rep, data, input_dim, output_dim): """Transform Operator representation to other representation.""" - if rep == 'Operator': + if rep == "Operator": return data - if rep == 'SuperOp': + if rep == "SuperOp": return np.kron(np.conj(data), data) - if rep == 'Choi': - vec = np.ravel(data, order='F') + if rep == "Choi": + vec = np.ravel(data, order="F") return np.outer(vec, np.conj(vec)) - if rep == 'Kraus': + if rep == "Kraus": return [data], None - if rep == 'Stinespring': + if rep == "Stinespring": return data, None - if rep == 'Chi': + if rep == "Chi": _check_nqubit_dim(input_dim, output_dim) - data = _from_operator('Choi', data, input_dim, output_dim) + data = _from_operator("Choi", data, input_dim, output_dim) return _choi_to_chi(data, input_dim) - if rep == 'PTM': + if rep == "PTM": _check_nqubit_dim(input_dim, output_dim) - data = _from_operator('SuperOp', data, input_dim, output_dim) + data = _from_operator("SuperOp", data, input_dim, output_dim) return _superop_to_ptm(data, input_dim) - raise QiskitError('Invalid QuantumChannel {}'.format(rep)) + raise QiskitError("Invalid QuantumChannel {}".format(rep)) def _kraus_to_operator(data): """Transform Kraus representation to Operator representation.""" if data[1] is not None or len(data[0]) > 1: - raise QiskitError( - 'Channel cannot be converted to Operator representation') + raise QiskitError("Channel cannot be converted to Operator representation") return data[0][0] @@ -188,8 +187,7 @@ def _stinespring_to_operator(data, output_dim): """Transform Stinespring representation to Operator representation.""" trace_dim = data[0].shape[0] // output_dim if data[1] is not None or trace_dim != 1: - raise QiskitError( - 'Channel cannot be converted to Operator representation') + raise QiskitError("Channel cannot be converted to Operator representation") return data[0] @@ -211,11 +209,11 @@ def _kraus_to_choi(data): kraus_l, kraus_r = data if kraus_r is None: for i in kraus_l: - vec = i.ravel(order='F') + vec = i.ravel(order="F") choi += np.outer(vec, vec.conj()) else: for i, j in zip(kraus_l, kraus_r): - choi += np.outer(i.ravel(order='F'), j.ravel(order='F').conj()) + choi += np.outer(i.ravel(order="F"), j.ravel(order="F").conj()) return choi @@ -228,7 +226,7 @@ def _choi_to_kraus(data, input_dim, output_dim, atol=ATOL_DEFAULT): # threading issue that is causing segfaults. # Need schur here since la.eig does not # guarentee orthogonality in degenerate subspaces - w, v = la.schur(data, output='complex') + w, v = la.schur(data, output="complex") w = w.diagonal().real # Check eigenvalues are non-negative if len(w[w < -atol]) == 0: @@ -236,8 +234,7 @@ def _choi_to_kraus(data, input_dim, output_dim, atol=ATOL_DEFAULT): kraus = [] for val, vec in zip(w, v.T): if abs(val) > atol: - k = np.sqrt(val) * vec.reshape( - (output_dim, input_dim), order='F') + k = np.sqrt(val) * vec.reshape((output_dim, input_dim), order="F") kraus.append(k) # If we are converting a zero matrix, we need to return a Kraus set # with a single zero-element Kraus matrix @@ -249,10 +246,8 @@ def _choi_to_kraus(data, input_dim, output_dim, atol=ATOL_DEFAULT): kraus_l = [] kraus_r = [] for val, vec_l, vec_r in zip(svals, mat_u.T, mat_vh.conj()): - kraus_l.append( - np.sqrt(val) * vec_l.reshape((output_dim, input_dim), order='F')) - kraus_r.append( - np.sqrt(val) * vec_r.reshape((output_dim, input_dim), order='F')) + kraus_l.append(np.sqrt(val) * vec_l.reshape((output_dim, input_dim), order="F")) + kraus_r.append(np.sqrt(val) * vec_r.reshape((output_dim, input_dim), order="F")) return kraus_l, kraus_r @@ -283,8 +278,8 @@ def _stinespring_to_choi(data, input_dim, output_dim): else: stine_r = np.reshape(data[1], (output_dim, trace_dim, input_dim)) return np.reshape( - np.einsum('iAj,kAl->jilk', stine_l, stine_r.conj()), - 2 * [input_dim * output_dim]) + np.einsum("iAj,kAl->jilk", stine_l, stine_r.conj()), 2 * [input_dim * output_dim] + ) def _stinespring_to_superop(data, input_dim, output_dim): @@ -296,8 +291,9 @@ def _stinespring_to_superop(data, input_dim, output_dim): else: stine_r = np.reshape(data[1], (output_dim, trace_dim, input_dim)) return np.reshape( - np.einsum('iAj,kAl->ikjl', stine_r.conj(), stine_l), - (output_dim * output_dim, input_dim * input_dim)) + np.einsum("iAj,kAl->ikjl", stine_r.conj(), stine_l), + (output_dim * output_dim, input_dim * input_dim), + ) def _kraus_to_stinespring(data, input_dim, output_dim): @@ -306,8 +302,7 @@ def _kraus_to_stinespring(data, input_dim, output_dim): for i, kraus in enumerate(data): if kraus is not None: num_kraus = len(kraus) - stine = np.zeros((output_dim * num_kraus, input_dim), - dtype=complex) + stine = np.zeros((output_dim * num_kraus, input_dim), dtype=complex) for j, mat in enumerate(kraus): vec = np.zeros(num_kraus) vec[j] = 1 @@ -386,11 +381,9 @@ def _bipartite_tensor(mat1, mat2, shape1=None, shape2=None): sdim_b1 = int(np.sqrt(dim_b1)) shape2 = (sdim_b0, sdim_b0, sdim_b1, sdim_b1) # Check dimensions - if len(shape1) != 4 or shape1[0] * shape1[1] != dim_a0 or \ - shape1[2] * shape1[3] != dim_a1: + if len(shape1) != 4 or shape1[0] * shape1[1] != dim_a0 or shape1[2] * shape1[3] != dim_a1: raise QiskitError("Invalid shape_a") - if len(shape2) != 4 or shape2[0] * shape2[1] != dim_b0 or \ - shape2[2] * shape2[3] != dim_b1: + if len(shape2) != 4 or shape2[0] * shape2[1] != dim_b0 or shape2[2] * shape2[3] != dim_b1: raise QiskitError("Invalid shape_b") return _reravel(mat1, mat2, shape1, shape2) @@ -406,8 +399,8 @@ def _reravel(mat1, mat2, shape1, shape2): # Tensor product matrices data = np.kron(mat1, mat2) data = np.reshape( - np.transpose(np.reshape(data, tensor_shape), (0, 2, 1, 3, 4, 6, 5, 7)), - final_shape) + np.transpose(np.reshape(data, tensor_shape), (0, 2, 1, 3, 4, 6, 5, 7)), final_shape + ) return data @@ -415,8 +408,8 @@ def _transform_to_pauli(data, num_qubits): """Change of basis of bipartite matrix representation.""" # Change basis: um_{i=0}^3 |i>><\sigma_i| basis_mat = np.array( - [[1, 0, 0, 1], [0, 1, 1, 0], [0, -1j, 1j, 0], [1, 0j, 0, -1]], - dtype=complex) + [[1, 0, 0, 1], [0, 1, 1, 0], [0, -1j, 1j, 0], [1, 0j, 0, -1]], dtype=complex + ) # Note that we manually renormalized after change of basis # to avoid rounding errors from square-roots of 2. cob = basis_mat @@ -424,18 +417,20 @@ def _transform_to_pauli(data, num_qubits): dim = int(np.sqrt(len(cob))) cob = np.reshape( np.transpose( - np.reshape( - np.kron(basis_mat, cob), (4, dim * dim, 2, 2, dim, dim)), - (0, 1, 2, 4, 3, 5)), (4 * dim * dim, 4 * dim * dim)) - return np.dot(np.dot(cob, data), cob.conj().T) / 2**num_qubits + np.reshape(np.kron(basis_mat, cob), (4, dim * dim, 2, 2, dim, dim)), + (0, 1, 2, 4, 3, 5), + ), + (4 * dim * dim, 4 * dim * dim), + ) + return np.dot(np.dot(cob, data), cob.conj().T) / 2 ** num_qubits def _transform_from_pauli(data, num_qubits): """Change of basis of bipartite matrix representation.""" # Change basis: sum_{i=0}^3 =|\sigma_i>> A[lj,ki].""" return np.reshape( np.transpose(np.reshape(mat, shape), (3, 1, 2, 0)), - (shape[3] * shape[1], shape[0] * shape[2])) + (shape[3] * shape[1], shape[0] * shape[2]), + ) def _check_nqubit_dim(input_dim, output_dim): """Return true if dims correspond to an n-qubit channel.""" if input_dim != output_dim: raise QiskitError( - 'Not an n-qubit channel: input_dim' + - ' ({}) != output_dim ({})'.format(input_dim, output_dim)) + "Not an n-qubit channel: input_dim" + + " ({}) != output_dim ({})".format(input_dim, output_dim) + ) num_qubits = int(np.log2(input_dim)) - if 2**num_qubits != input_dim: - raise QiskitError('Not an n-qubit channel: input_dim != 2 ** n') + if 2 ** num_qubits != input_dim: + raise QiskitError("Not an n-qubit channel: input_dim != 2 ** n") diff --git a/qiskit/quantum_info/operators/custom_iterator.py b/qiskit/quantum_info/operators/custom_iterator.py index e5712943ca7d..ac9fdc2692f6 100644 --- a/qiskit/quantum_info/operators/custom_iterator.py +++ b/qiskit/quantum_info/operators/custom_iterator.py @@ -18,6 +18,7 @@ class CustomIterator(ABC): """Lazy custom iteration and item access.""" + def __init__(self, obj): self.obj = obj self._iter = 0 @@ -30,8 +31,7 @@ def __getitem__(self, key): pass def __repr__(self): - return "<{}_iterator at {}>".format(type(self.obj), - hex(id(self))) + return "<{}_iterator at {}>".format(type(self.obj), hex(id(self))) def __len__(self): return len(self.obj) diff --git a/qiskit/quantum_info/operators/dihedral/dihedral.py b/qiskit/quantum_info/operators/dihedral/dihedral.py index 4b739ebc857d..3b41f9661dde 100644 --- a/qiskit/quantum_info/operators/dihedral/dihedral.py +++ b/qiskit/quantum_info/operators/dihedral/dihedral.py @@ -32,65 +32,65 @@ class CNOTDihedral(BaseOperator, AdjointMixin): """An N-qubit operator from the CNOT-Dihedral group. - The CNOT-Dihedral group is generated by the quantum gates, - :class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.TGate`, - and :class:`~qiskit.circuit.library.XGate`. - - **Representation** - - An :math:`N`-qubit CNOT-Dihedral operator is stored as an affine function and a - phase polynomial, based on the convention in references [1, 2]. - - The affine function consists of an :math:`N \\times N` invertible binary matrix, - and an :math:`N` binary vector. - - The phase polynomial is a polynomial of degree at most 3, - in :math:`N` variables, whose coefficients are in the ring Z_8 with 8 elements. - - .. jupyter-execute:: - - from qiskit import QuantumCircuit - from qiskit.quantum_info import CNOTDihedral - - circ = QuantumCircuit(3) - circ.cx(0, 1) - circ.x(2) - circ.t(1) - circ.t(1) - circ.t(1) - elem = CNOTDihedral(circ) - - # Print the CNOTDihedral element - print(elem) - - **Circuit Conversion** - - CNOTDihedral operators can be initialized from circuits containing *only* the - following gates: :class:`~qiskit.circuit.library.IGate`, - :class:`~qiskit.circuit.library.XGate`, :class:`~qiskit.circuit.library.YGate`, - :class:`~qiskit.circuit.library.ZGate`, - :class:`~qiskit.circuit.library.TGate`, :class:`~qiskit.circuit.library.TdgGate` - :class:`~qiskit.circuit.library.SGate`, :class:`~qiskit.circuit.library.SdgGate`, - :class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.CZGate`, - :class:`~qiskit.circuit.library.SwapGate`. - They can be converted back into a :class:`~qiskit.circuit.QuantumCircuit`, - or :class:`~qiskit.circuit.Gate` object using the :meth:`~CNOTDihedral.to_circuit` - or :meth:`~CNOTDihderal.to_instruction` methods respectively. Note that this - decomposition is not necessarily optimal in terms of number of gates - if the number of qubits is more than two. - - CNOTDihedral operators can also be converted to - :class:`~qiskit.quantum_info.Operator` objects using the - :meth:`to_operator` method. This is done via decomposing to a circuit, - and then simulating the circuit as a unitary operator. - - References: - 1. Shelly Garion and Andrew W. Cross, *Synthesis of CNOT-Dihedral circuits - with optimal number of two qubit gates*, `Quantum 4(369), 2020 - `_ - 2. Andrew W. Cross, Easwar Magesan, Lev S. Bishop, John A. Smolin and Jay M. Gambetta, - *Scalable randomised benchmarking of non-Clifford gates*, - npj Quantum Inf 2, 16012 (2016). + The CNOT-Dihedral group is generated by the quantum gates, + :class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.TGate`, + and :class:`~qiskit.circuit.library.XGate`. + + **Representation** + + An :math:`N`-qubit CNOT-Dihedral operator is stored as an affine function and a + phase polynomial, based on the convention in references [1, 2]. + + The affine function consists of an :math:`N \\times N` invertible binary matrix, + and an :math:`N` binary vector. + + The phase polynomial is a polynomial of degree at most 3, + in :math:`N` variables, whose coefficients are in the ring Z_8 with 8 elements. + + .. jupyter-execute:: + + from qiskit import QuantumCircuit + from qiskit.quantum_info import CNOTDihedral + + circ = QuantumCircuit(3) + circ.cx(0, 1) + circ.x(2) + circ.t(1) + circ.t(1) + circ.t(1) + elem = CNOTDihedral(circ) + + # Print the CNOTDihedral element + print(elem) + + **Circuit Conversion** + + CNOTDihedral operators can be initialized from circuits containing *only* the + following gates: :class:`~qiskit.circuit.library.IGate`, + :class:`~qiskit.circuit.library.XGate`, :class:`~qiskit.circuit.library.YGate`, + :class:`~qiskit.circuit.library.ZGate`, + :class:`~qiskit.circuit.library.TGate`, :class:`~qiskit.circuit.library.TdgGate` + :class:`~qiskit.circuit.library.SGate`, :class:`~qiskit.circuit.library.SdgGate`, + :class:`~qiskit.circuit.library.CXGate`, :class:`~qiskit.circuit.library.CZGate`, + :class:`~qiskit.circuit.library.SwapGate`. + They can be converted back into a :class:`~qiskit.circuit.QuantumCircuit`, + or :class:`~qiskit.circuit.Gate` object using the :meth:`~CNOTDihedral.to_circuit` + or :meth:`~CNOTDihderal.to_instruction` methods respectively. Note that this + decomposition is not necessarily optimal in terms of number of gates + if the number of qubits is more than two. + + CNOTDihedral operators can also be converted to + :class:`~qiskit.quantum_info.Operator` objects using the + :meth:`to_operator` method. This is done via decomposing to a circuit, + and then simulating the circuit as a unitary operator. + + References: + 1. Shelly Garion and Andrew W. Cross, *Synthesis of CNOT-Dihedral circuits + with optimal number of two qubit gates*, `Quantum 4(369), 2020 + `_ + 2. Andrew W. Cross, Easwar Magesan, Lev S. Bishop, John A. Smolin and Jay M. Gambetta, + *Scalable randomised benchmarking of non-Clifford gates*, + npj Quantum Inf 2, 16012 (2016). """ def __init__(self, data=None, num_qubits=None, validate=True): @@ -126,8 +126,7 @@ def __init__(self, data=None, num_qubits=None, validate=True): # Initialize from ScalarOp as N-qubit identity discarding any global phase elif isinstance(data, ScalarOp): - if not data.is_unitary() or set(data._input_dims) != {2} or \ - data.num_qubits is None: + if not data.is_unitary() or set(data._input_dims) != {2} or data.num_qubits is None: raise QiskitError("Can only initialize from N-qubit identity ScalarOp.") self._num_qubits = data.num_qubits # phase polynomial @@ -160,7 +159,7 @@ def __init__(self, data=None, num_qubits=None, validate=True): # Validate the CNOTDihedral element if validate and not self._is_valid(): - raise QiskitError('Invalid CNOTDihedral element.') + raise QiskitError("Invalid CNOTDihedral element.") def _z2matmul(self, left, right): """Compute product of two n x n z2 matrices.""" @@ -177,8 +176,9 @@ def _dot(self, other): if self.num_qubits != other.num_qubits: raise QiskitError("Multiplication on different number of qubits.") result = CNOTDihedral(num_qubits=self.num_qubits) - result.shift = [(x[0] + x[1]) % 2 - for x in zip(self._z2matvecmul(self.linear, other.shift), self.shift)] + result.shift = [ + (x[0] + x[1]) % 2 for x in zip(self._z2matvecmul(self.linear, other.shift), self.shift) + ] result.linear = self._z2matmul(self.linear, other.linear) # Compute x' = B1*x + c1 using the p_j identity new_vars = [] @@ -199,8 +199,9 @@ def _compose(self, other): if self.num_qubits != other.num_qubits: raise QiskitError("Multiplication on different number of qubits.") result = CNOTDihedral(num_qubits=self.num_qubits) - result.shift = [(x[0] + x[1]) % 2 - for x in zip(self._z2matvecmul(other.linear, self.shift), other.shift)] + result.shift = [ + (x[0] + x[1]) % 2 for x in zip(self._z2matvecmul(other.linear, self.shift), other.shift) + ] result.linear = self._z2matmul(other.linear, self.linear) # Compute x' = B1*x + c1 using the p_j identity new_vars = [] @@ -218,9 +219,12 @@ def _compose(self, other): def __eq__(self, other): """Test equality.""" - return isinstance(other, CNOTDihedral) and self.poly == other.poly \ - and (self.linear == other.linear).all() \ + return ( + isinstance(other, CNOTDihedral) + and self.poly == other.poly + and (self.linear == other.linear).all() and (self.shift == other.shift).all() + ) def _append_cx(self, i, j): """Apply a CX gate to this element. @@ -240,7 +244,7 @@ def _append_phase(self, k, i): raise QiskitError("phase qubit out of bounds.") # If the kth bit is flipped, conjugate this gate if self.shift[i] == 1: - k = (7*k) % 8 + k = (7 * k) % 8 # Take all subsets \alpha of the support of row i # of weight up to 3 and add k*(-2)**(|\alpha| - 1) mod 8 # to the corresponding term. @@ -276,9 +280,9 @@ def __str__(self): for col in range(self.num_qubits): if self.linear[row][col] != 0: if wrote: - out += (" + x_" + str(col)) + out += " + x_" + str(col) else: - out += ("x_" + str(col)) + out += "x_" + str(col) wrote = True if self.shift[row] != 0: out += " + 1" @@ -366,10 +370,12 @@ def _tensor(self, other, reverse=False): elem1 = self result = CNOTDihedral(num_qubits=elem0.num_qubits + elem1.num_qubits) - linear = np.block([[elem0.linear, - np.zeros((elem0.num_qubits, elem1.num_qubits), dtype=np.int8)], - [np.zeros((elem1.num_qubits, elem0.num_qubits), dtype=np.int8), - elem1.linear]]) + linear = np.block( + [ + [elem0.linear, np.zeros((elem0.num_qubits, elem1.num_qubits), dtype=np.int8)], + [np.zeros((elem1.num_qubits, elem0.num_qubits), dtype=np.int8), elem1.linear], + ] + ) result.linear = linear shift = np.block([elem0.shift, elem1.shift]) result.shift = shift @@ -392,8 +398,9 @@ def _tensor(self, other, reverse=False): result.poly.set_term([j + elem0.num_qubits, i + elem0.num_qubits], value) for k in range(j): value = elem1.poly.get_term([k, j, i]) - result.poly.set_term([k + elem0.num_qubits, j + elem0.num_qubits, - i + elem0.num_qubits], value) + result.poly.set_term( + [k + elem0.num_qubits, j + elem0.num_qubits, i + elem0.num_qubits], value + ) return result @@ -411,25 +418,24 @@ def adjoint(self): def conjugate(self): circ = self.to_instruction() new_circ = QuantumCircuit(self.num_qubits) - bit_indices = {bit: index - for index, bit in enumerate(circ.definition.qubits)} + bit_indices = {bit: index for index, bit in enumerate(circ.definition.qubits)} for instr, qregs, _ in circ.definition: new_qubits = [bit_indices[tup] for tup in qregs] - if instr.name == 'p': + if instr.name == "p": params = 2 * np.pi - instr.params[0] instr.params[0] = params new_circ.append(instr, new_qubits) - elif instr.name == 't': - instr.name = 'tdg' + elif instr.name == "t": + instr.name = "tdg" new_circ.append(instr, new_qubits) - elif instr.name == 'tdg': - instr.name = 't' + elif instr.name == "tdg": + instr.name = "t" new_circ.append(instr, new_qubits) - elif instr.name == 's': - instr.name = 'sdg' + elif instr.name == "s": + instr.name = "sdg" new_circ.append(instr, new_qubits) - elif instr.name == 'sdg': - instr.name = 's' + elif instr.name == "sdg": + instr.name = "s" new_circ.append(instr, new_qubits) else: new_circ.append(instr, new_qubits) @@ -444,22 +450,29 @@ def transpose(self): def _is_valid(self): """Return True if input is a CNOTDihedral element.""" - if self.poly.weight_0 != 0 or \ - len(self.poly.weight_1) != self.num_qubits or \ - len(self.poly.weight_2) != int(self.num_qubits * (self.num_qubits - 1) / 2) \ - or len(self.poly.weight_3) != int(self.num_qubits * (self.num_qubits - 1) - * (self.num_qubits - 2) / 6): + if ( + self.poly.weight_0 != 0 + or len(self.poly.weight_1) != self.num_qubits + or len(self.poly.weight_2) != int(self.num_qubits * (self.num_qubits - 1) / 2) + or len(self.poly.weight_3) + != int(self.num_qubits * (self.num_qubits - 1) * (self.num_qubits - 2) / 6) + ): return False - if (self.linear).shape != (self.num_qubits, self.num_qubits) or \ - len(self.shift) != self.num_qubits or \ - not np.allclose((np.linalg.det(self.linear) % 2), 1): + if ( + (self.linear).shape != (self.num_qubits, self.num_qubits) + or len(self.shift) != self.num_qubits + or not np.allclose((np.linalg.det(self.linear) % 2), 1) + ): return False - if not (set(self.poly.weight_1.flatten())).issubset({0, 1, 2, 3, 4, 5, 6, 7}) or \ - not (set(self.poly.weight_2.flatten())).issubset({0, 2, 4, 6}) or \ - not (set(self.poly.weight_3.flatten())).issubset({0, 4}): + if ( + not (set(self.poly.weight_1.flatten())).issubset({0, 1, 2, 3, 4, 5, 6, 7}) + or not (set(self.poly.weight_2.flatten())).issubset({0, 2, 4, 6}) + or not (set(self.poly.weight_3.flatten())).issubset({0, 4}) + ): return False - if not (set(self.shift.flatten())).issubset({0, 1}) or \ - not (set(self.linear.flatten())).issubset({0, 1}): + if not (set(self.shift.flatten())).issubset({0, 1}) or not ( + set(self.linear.flatten()) + ).issubset({0, 1}): return False return True diff --git a/qiskit/quantum_info/operators/dihedral/dihedral_circuits.py b/qiskit/quantum_info/operators/dihedral/dihedral_circuits.py index 4bd3097835e2..0f88798e3a74 100644 --- a/qiskit/quantum_info/operators/dihedral/dihedral_circuits.py +++ b/qiskit/quantum_info/operators/dihedral/dihedral_circuits.py @@ -41,13 +41,13 @@ def _append_circuit(elem, circuit, qargs=None): gate = circuit # Handle cx, cz and id since they are basic gates, and cannot be decomposed, - if gate.name == 'cx': + if gate.name == "cx": if len(qargs) != 2: raise QiskitError("Invalid qubits for 2-qubit gate cx.") elem._append_cx(qargs[0], qargs[1]) return elem - elif gate.name == 'cz': + elif gate.name == "cz": if len(qargs) != 2: raise QiskitError("Invalid qubits for 2-qubit gate cz.") elem._append_phase(7, qargs[1]) @@ -59,73 +59,78 @@ def _append_circuit(elem, circuit, qargs=None): elem._append_phase(7, qargs[0]) return elem - if gate.name == 'id': + if gate.name == "id": if len(qargs) != 1: raise QiskitError("Invalid qubits for 1-qubit gate id.") return elem if gate.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format(gate.name)) + raise QiskitError("Cannot apply Instruction: {}".format(gate.name)) if not isinstance(gate.definition, QuantumCircuit): - raise QiskitError('{} instruction definition is {}; expected QuantumCircuit'.format( - gate.name, type(gate.definition))) + raise QiskitError( + "{} instruction definition is {}; expected QuantumCircuit".format( + gate.name, type(gate.definition) + ) + ) flat_instr = gate.definition - bit_indices = {bit: index - for bits in [flat_instr.qubits, flat_instr.clbits] - for index, bit in enumerate(bits)} + bit_indices = { + bit: index + for bits in [flat_instr.qubits, flat_instr.clbits] + for index, bit in enumerate(bits) + } for instr, qregs, _ in gate.definition: # Get the integer position of the flat register new_qubits = [qargs[bit_indices[tup]] for tup in qregs] - if (instr.name == 'x' or gate.name == 'x'): + if instr.name == "x" or gate.name == "x": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate x.") elem._append_x(new_qubits[0]) - elif (instr.name == 'z' or gate.name == 'z'): + elif instr.name == "z" or gate.name == "z": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate z.") elem._append_phase(4, new_qubits[0]) - elif (instr.name == 'y' or gate.name == 'y'): + elif instr.name == "y" or gate.name == "y": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate y.") elem._append_x(new_qubits[0]) elem._append_phase(4, new_qubits[0]) - elif (instr.name == 'p' or gate.name == 'p'): - if (len(new_qubits) != 1 or len(instr.params) != 1): + elif instr.name == "p" or gate.name == "p": + if len(new_qubits) != 1 or len(instr.params) != 1: raise QiskitError("Invalid qubits or params for 1-qubit gate p.") elem._append_phase(int(4 * instr.params[0] / np.pi), new_qubits[0]) - elif (instr.name == 't' or gate.name == 't'): + elif instr.name == "t" or gate.name == "t": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate t.") elem._append_phase(1, new_qubits[0]) - elif (instr.name == 'tdg' or gate.name == 'tdg'): + elif instr.name == "tdg" or gate.name == "tdg": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate tdg.") elem._append_phase(7, new_qubits[0]) - elif (instr.name == 's' or gate.name == 's'): + elif instr.name == "s" or gate.name == "s": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate s.") elem._append_phase(2, new_qubits[0]) - elif (instr.name == 'sdg' or gate.name == 'sdg'): + elif instr.name == "sdg" or gate.name == "sdg": if len(new_qubits) != 1: raise QiskitError("Invalid qubits for 1-qubit gate sdg.") elem._append_phase(6, new_qubits[0]) - elif instr.name == 'cx': + elif instr.name == "cx": if len(new_qubits) != 2: raise QiskitError("Invalid qubits for 2-qubit gate cx.") elem._append_cx(new_qubits[0], new_qubits[1]) - elif instr.name == 'cz': + elif instr.name == "cz": if len(new_qubits) != 2: raise QiskitError("Invalid qubits for 2-qubit gate cz.") elem._append_phase(7, new_qubits[1]) @@ -136,17 +141,17 @@ def _append_circuit(elem, circuit, qargs=None): elem._append_phase(7, new_qubits[1]) elem._append_phase(7, new_qubits[0]) - elif (instr.name == 'swap' or gate.name == 'swap'): + elif instr.name == "swap" or gate.name == "swap": if len(new_qubits) != 2: raise QiskitError("Invalid qubits for 2-qubit gate swap.") elem._append_cx(new_qubits[0], new_qubits[1]) elem._append_cx(new_qubits[1], new_qubits[0]) elem._append_cx(new_qubits[0], new_qubits[1]) - elif instr.name == 'id': + elif instr.name == "id": pass else: - raise QiskitError('Not a CNOT-Dihedral gate: {}'.format(instr.name)) + raise QiskitError("Not a CNOT-Dihedral gate: {}".format(instr.name)) return elem diff --git a/qiskit/quantum_info/operators/dihedral/polynomial.py b/qiskit/quantum_info/operators/dihedral/polynomial.py index 013464b5b88d..7cbb89762814 100644 --- a/qiskit/quantum_info/operators/dihedral/polynomial.py +++ b/qiskit/quantum_info/operators/dihedral/polynomial.py @@ -23,7 +23,7 @@ from qiskit.exceptions import QiskitError -class SpecialPolynomial(): +class SpecialPolynomial: """Multivariate polynomial with special form. Maximum degree 3, n Z_2 variables, coefficients in Z_8. @@ -39,8 +39,8 @@ def __init__(self, n_vars): if n_vars < 1: raise QiskitError("n_vars for SpecialPolynomial is too small.") self.n_vars = n_vars - self.nc2 = int(n_vars * (n_vars-1) / 2) - self.nc3 = int(n_vars * (n_vars-1) * (n_vars-2) / 6) + self.nc2 = int(n_vars * (n_vars - 1) / 2) + self.nc3 = int(n_vars * (n_vars - 1) * (n_vars - 2) / 6) self.weight_0 = 0 self.weight_1 = np.zeros(n_vars, dtype=np.int8) self.weight_2 = np.zeros(self.nc2, dtype=np.int8) @@ -132,7 +132,7 @@ def evaluate(self, xval): check_poly = list(map(lambda x: isinstance(x, SpecialPolynomial), xval)) if False in check_int and False in check_poly: raise QiskitError("Evaluate on a wrong type.") - is_int = (False not in check_int) + is_int = False not in check_int if not is_int: if False in [i.n_vars == self.n_vars for i in xval]: raise QiskitError("Evaluate on incompatible polynomials.") @@ -210,8 +210,7 @@ def get_term(self, indices): return self.weight_1[indices[0]] if length == 2: # sum(self.n_vars-j, {j, 1, indices[0]}) - offset_1 = int(indices[0] * self.n_vars - - ((indices[0] + 1) * indices[0])/2) + offset_1 = int(indices[0] * self.n_vars - ((indices[0] + 1) * indices[0]) / 2) offset_2 = int(indices[1] - indices[0] - 1) return self.weight_2[offset_1 + offset_2] @@ -221,8 +220,9 @@ def get_term(self, indices): tmp_2 = self.n_vars - indices[1] offset_2 = int((tmp_2 - 2) * (tmp_2 - 1) / 2) offset_3 = self.n_vars - indices[2] - offset = int(self.n_vars * (self.n_vars - 1) * (self.n_vars - 2) / 6 - - offset_1 - offset_2 - offset_3) + offset = int( + self.n_vars * (self.n_vars - 1) * (self.n_vars - 2) / 6 - offset_1 - offset_2 - offset_3 + ) return self.weight_3[offset] @@ -255,8 +255,7 @@ def set_term(self, indices, value): self.weight_1[indices[0]] = value elif length == 2: # sum(self.n_vars-j, {j, 1, indices[0]}) - offset_1 = int(indices[0] * self.n_vars - - ((indices[0] + 1) * indices[0])/2) + offset_1 = int(indices[0] * self.n_vars - ((indices[0] + 1) * indices[0]) / 2) offset_2 = int(indices[1] - indices[0] - 1) self.weight_2[offset_1 + offset_2] = value else: # length = 3 @@ -265,15 +264,18 @@ def set_term(self, indices, value): tmp_2 = self.n_vars - indices[1] offset_2 = int((tmp_2 - 2) * (tmp_2 - 1) / 2) offset_3 = self.n_vars - indices[2] - offset = int(self.n_vars * (self.n_vars - 1) * (self.n_vars - 2) / 6 - - offset_1 - offset_2 - offset_3) + offset = int( + self.n_vars * (self.n_vars - 1) * (self.n_vars - 2) / 6 + - offset_1 + - offset_2 + - offset_3 + ) self.weight_3[offset] = value @property def key(self): """Return a string representation.""" - tup = (self.weight_0, tuple(self.weight_1), - tuple(self.weight_2), tuple(self.weight_3)) + tup = (self.weight_0, tuple(self.weight_1), tuple(self.weight_2), tuple(self.weight_3)) return tup def __eq__(self, x): @@ -288,24 +290,23 @@ def __str__(self): if value != 0: out += " + " if value != 1: - out += (str(value) + "*") - out += ("x_" + str(i)) - for i in range(self.n_vars-1): - for j in range(i+1, self.n_vars): + out += str(value) + "*" + out += "x_" + str(i) + for i in range(self.n_vars - 1): + for j in range(i + 1, self.n_vars): value = self.get_term([i, j]) if value != 0: out += " + " if value != 1: - out += (str(value) + "*") - out += ("x_" + str(i) + "*x_" + str(j)) - for i in range(self.n_vars-2): - for j in range(i+1, self.n_vars-1): - for k in range(j+1, self.n_vars): + out += str(value) + "*" + out += "x_" + str(i) + "*x_" + str(j) + for i in range(self.n_vars - 2): + for j in range(i + 1, self.n_vars - 1): + for k in range(j + 1, self.n_vars): value = self.get_term([i, j, k]) if value != 0: out += " + " if value != 1: - out += (str(value) + "*") - out += ("x_" + str(i) + "*x_" + str(j) + - "*x_" + str(k)) + out += str(value) + "*" + out += "x_" + str(i) + "*x_" + str(j) + "*x_" + str(k) return out diff --git a/qiskit/quantum_info/operators/dihedral/random.py b/qiskit/quantum_info/operators/dihedral/random.py index a4ff3d33cb79..6ba2a354f4fd 100644 --- a/qiskit/quantum_info/operators/dihedral/random.py +++ b/qiskit/quantum_info/operators/dihedral/random.py @@ -43,8 +43,7 @@ def random_cnotdihedral(num_qubits, seed=None): elem.poly.weight_1 = weight_1 weight_2 = 2 * rng.integers(4, size=int(num_qubits * (num_qubits - 1) / 2)) elem.poly.weight_2 = weight_2 - weight_3 = 4 * rng.integers(2, size=int(num_qubits * (num_qubits - 1) * - (num_qubits - 2) / 6)) + weight_3 = 4 * rng.integers(2, size=int(num_qubits * (num_qubits - 1) * (num_qubits - 2) / 6)) elem.poly.weight_3 = weight_3 # Random affine function diff --git a/qiskit/quantum_info/operators/linear_op.py b/qiskit/quantum_info/operators/linear_op.py index c77fcb606afa..19316b57ed25 100644 --- a/qiskit/quantum_info/operators/linear_op.py +++ b/qiskit/quantum_info/operators/linear_op.py @@ -21,9 +21,5 @@ # pylint: disable = abstract-method -class LinearOp(BaseOperator, - AdjointMixin, - LinearMixin, - TolerancesMixin, - ABC): - """Abstract linear operator base class. """ +class LinearOp(BaseOperator, AdjointMixin, LinearMixin, TolerancesMixin, ABC): + """Abstract linear operator base class.""" diff --git a/qiskit/quantum_info/operators/measures.py b/qiskit/quantum_info/operators/measures.py index 0b309a22c6a3..37f361bc4c74 100644 --- a/qiskit/quantum_info/operators/measures.py +++ b/qiskit/quantum_info/operators/measures.py @@ -30,6 +30,7 @@ try: import cvxpy + _HAS_CVX = True except ImportError: _HAS_CVX = False @@ -37,10 +38,7 @@ logger = logging.getLogger(__name__) -def process_fidelity(channel, - target=None, - require_cp=True, - require_tp=True): +def process_fidelity(channel, target=None, require_cp=True, require_tp=True): r"""Return the process fidelity of a noisy quantum channel. @@ -89,35 +87,37 @@ def process_fidelity(channel, QiskitError: if the channel and target do not have the same dimensions. """ # Format inputs - channel = _input_formatter( - channel, SuperOp, 'process_fidelity', 'channel') - target = _input_formatter( - target, Operator, 'process_fidelity', 'target') + channel = _input_formatter(channel, SuperOp, "process_fidelity", "channel") + target = _input_formatter(target, Operator, "process_fidelity", "target") if target: # Validate dimensions if channel.dim != target.dim: raise QiskitError( - 'Input quantum channel and target unitary must have the same ' - 'dimensions ({} != {}).'.format(channel.dim, target.dim)) + "Input quantum channel and target unitary must have the same " + "dimensions ({} != {}).".format(channel.dim, target.dim) + ) # Validate complete-positivity and trace-preserving - for label, chan in [('Input', channel), ('Target', target)]: + for label, chan in [("Input", channel), ("Target", target)]: if chan is not None and require_cp: cp_cond = _cp_condition(chan) neg = cp_cond < -1 * chan.atol if np.any(neg): logger.warning( - '%s channel is not CP. Choi-matrix has negative eigenvalues: %s', - label, cp_cond[neg]) + "%s channel is not CP. Choi-matrix has negative eigenvalues: %s", + label, + cp_cond[neg], + ) if chan is not None and require_tp: tp_cond = _tp_condition(chan) - non_zero = np.logical_not(np.isclose( - tp_cond, 0, atol=chan.atol, rtol=chan.rtol)) + non_zero = np.logical_not(np.isclose(tp_cond, 0, atol=chan.atol, rtol=chan.rtol)) if np.any(non_zero): logger.warning( - '%s channel is not TP. Tr_2[Choi] - I has non-zero eigenvalues: %s', - label, tp_cond[non_zero]) + "%s channel is not TP. Tr_2[Choi] - I has non-zero eigenvalues: %s", + label, + tp_cond[non_zero], + ) if isinstance(target, Operator): # Compute fidelity with unitary target by applying the inverse @@ -130,10 +130,10 @@ def process_fidelity(channel, # Compute process fidelity with identity channel if isinstance(channel, Operator): # |Tr[U]/dim| ** 2 - fid = np.abs(np.trace(channel.data) / input_dim)**2 + fid = np.abs(np.trace(channel.data) / input_dim) ** 2 else: # Tr[S] / (dim ** 2) - fid = np.trace(SuperOp(channel).data) / (input_dim**2) + fid = np.trace(SuperOp(channel).data) / (input_dim ** 2) return float(np.real(fid)) # For comparing two non-unitary channels we compute the state fidelity of @@ -144,10 +144,7 @@ def process_fidelity(channel, return state_fidelity(state1, state2, validate=False) -def average_gate_fidelity(channel, - target=None, - require_cp=True, - require_tp=False): +def average_gate_fidelity(channel, target=None, require_cp=True, require_tp=False): r"""Return the average gate fidelity of a noisy quantum channel. The average gate fidelity :math:`F_{\text{ave}}` is given by @@ -185,24 +182,20 @@ def average_gate_fidelity(channel, or have different input and output dimensions. """ # Format inputs - channel = _input_formatter( - channel, SuperOp, 'average_gate_fidelity', 'channel') - target = _input_formatter( - target, Operator, 'average_gate_fidelity', 'target') + channel = _input_formatter(channel, SuperOp, "average_gate_fidelity", "channel") + target = _input_formatter(target, Operator, "average_gate_fidelity", "target") if target is not None: try: target = Operator(target) except QiskitError as ex: raise QiskitError( - 'Target channel is not a unitary channel. To compare ' - 'two non-unitary channels use the ' - '`qiskit.quantum_info.process_fidelity` function instead.') from ex + "Target channel is not a unitary channel. To compare " + "two non-unitary channels use the " + "`qiskit.quantum_info.process_fidelity` function instead." + ) from ex dim, _ = channel.dim - f_pro = process_fidelity(channel, - target=target, - require_cp=require_cp, - require_tp=require_tp) + f_pro = process_fidelity(channel, target=target, require_cp=require_cp, require_tp=require_tp) return (dim * f_pro + 1) / (dim + 1) @@ -241,12 +234,11 @@ def gate_error(channel, target=None, require_cp=True, require_tp=False): or have different input and output dimensions. """ # Format inputs - channel = _input_formatter( - channel, SuperOp, 'gate_error', 'channel') - target = _input_formatter( - target, Operator, 'gate_error', 'target') + channel = _input_formatter(channel, SuperOp, "gate_error", "channel") + target = _input_formatter(target, Operator, "gate_error", "target") return 1 - average_gate_fidelity( - channel, target=target, require_cp=require_cp, require_tp=require_tp) + channel, target=target, require_cp=require_cp, require_tp=require_tp + ) def diamond_norm(choi, **kwargs): @@ -285,9 +277,9 @@ def diamond_norm(choi, **kwargs): function. See the CVXPY documentation for information on available SDP solvers. """ - _cvxpy_check('`diamond_norm`') # Check CVXPY is installed + _cvxpy_check("`diamond_norm`") # Check CVXPY is installed - choi = Choi(_input_formatter(choi, Choi, 'diamond_norm', 'choi')) + choi = Choi(_input_formatter(choi, Choi, "diamond_norm", "choi")) def cvx_bmat(mat_r, mat_i): """Block matrix for embedding complex matrix in reals""" @@ -321,16 +313,22 @@ def cvx_bmat(mat_r, mat_i): # Convert col-vec convention Choi-matrix to row-vec convention and # then take Transpose: Choi_C -> Choi_R.T choi_rt = np.transpose( - np.reshape(choi.data, (dim_in, dim_out, dim_in, dim_out)), - (3, 2, 1, 0)).reshape(choi.data.shape) + np.reshape(choi.data, (dim_in, dim_out, dim_in, dim_out)), (3, 2, 1, 0) + ).reshape(choi.data.shape) choi_rt_r = choi_rt.real choi_rt_i = choi_rt.imag # Constraints cons = [ - r0 >> 0, r0_r == r0_r.T, r0_i == - r0_i.T, cvxpy.trace(r0_r) == 1, - r1 >> 0, r1_r == r1_r.T, r1_i == - r1_i.T, cvxpy.trace(r1_r) == 1, - c >> 0 + r0 >> 0, + r0_r == r0_r.T, + r0_i == -r0_i.T, + cvxpy.trace(r0_r) == 1, + r1 >> 0, + r1_r == r1_r.T, + r1_i == -r1_i.T, + cvxpy.trace(r1_r) == 1, + c >> 0, ] # Objective function @@ -345,14 +343,15 @@ def _cvxpy_check(name): # Check if CVXPY package is installed if not _HAS_CVX: raise QiskitError( - 'CVXPY backage is requried for {}. Install' - ' with `pip install cvxpy` to use.'.format(name)) + "CVXPY backage is requried for {}. Install" + " with `pip install cvxpy` to use.".format(name) + ) # Check CVXPY version version = cvxpy.__version__ - if version[0] != '1': + if version[0] != "1": raise ImportError( - 'Incompatible CVXPY version {} found.' - ' Install version >=1.0.'.format(version)) + "Incompatible CVXPY version {} found." " Install version >=1.0.".format(version) + ) # pylint: disable=too-many-return-statements @@ -365,25 +364,25 @@ def _input_formatter(obj, fallback_class, func_name, arg_name): # Channel-like input if isinstance(obj, QuantumChannel): return obj - if hasattr(obj, 'to_quantumchannel'): + if hasattr(obj, "to_quantumchannel"): return obj.to_quantumchannel() - if hasattr(obj, 'to_channel'): + if hasattr(obj, "to_channel"): return obj.to_channel() # Unitary-like input if isinstance(obj, (Gate, BaseOperator)): return Operator(obj) - if hasattr(obj, 'to_operator'): + if hasattr(obj, "to_operator"): return obj.to_operator() warnings.warn( - 'Passing in a list or Numpy array to `{}` `{}` argument is ' - 'deprecated as of 0.17.0 since the matrix representation cannot be inferred ' - 'unambiguously. Use a Gate or BaseOperator subclass (eg. Operator, ' - 'SuperOp, Choi) object instead.'.format(func_name, arg_name), - DeprecationWarning) - warnings.warn( - 'Treating array input as a {} object'.format(fallback_class.__name__)) + "Passing in a list or Numpy array to `{}` `{}` argument is " + "deprecated as of 0.17.0 since the matrix representation cannot be inferred " + "unambiguously. Use a Gate or BaseOperator subclass (eg. Operator, " + "SuperOp, Choi) object instead.".format(func_name, arg_name), + DeprecationWarning, + ) + warnings.warn("Treating array input as a {} object".format(fallback_class.__name__)) return fallback_class(obj) diff --git a/qiskit/quantum_info/operators/mixins/__init__.py b/qiskit/quantum_info/operators/mixins/__init__.py index 56887953819e..0d3b76ea606a 100644 --- a/qiskit/quantum_info/operators/mixins/__init__.py +++ b/qiskit/quantum_info/operators/mixins/__init__.py @@ -36,16 +36,17 @@ def generate_apidocs(cls): Returns: cls: the original class with updated docstrings. """ + def _replace_name(mixin, methods): if issubclass(cls, mixin): for i in methods: meth = getattr(cls, i) doc = getdoc(meth) if doc is not None: - meth.__doc__ = doc.replace('CLASS', cls.__name__) + meth.__doc__ = doc.replace("CLASS", cls.__name__) - _replace_name(GroupMixin, ('tensor', 'expand', 'compose', 'dot', 'power')) - _replace_name(AdjointMixin, ('transpose', 'conjugate', 'adjoint')) - _replace_name(MultiplyMixin, ('_multiply',)) - _replace_name(LinearMixin, ('_add',)) + _replace_name(GroupMixin, ("tensor", "expand", "compose", "dot", "power")) + _replace_name(AdjointMixin, ("transpose", "conjugate", "adjoint")) + _replace_name(MultiplyMixin, ("_multiply",)) + _replace_name(LinearMixin, ("_add",)) return cls diff --git a/qiskit/quantum_info/operators/mixins/group.py b/qiskit/quantum_info/operators/mixins/group.py index 22f0558d9b8b..b3b95361f1d5 100644 --- a/qiskit/quantum_info/operators/mixins/group.py +++ b/qiskit/quantum_info/operators/mixins/group.py @@ -50,20 +50,23 @@ class GroupMixin(ABC): - ``tensor(self, other)`` - ``expand(self, other)`` """ + @deprecate_function( - 'Using the `__mul__` operator `A * B` as shorthand for' - ' `A.dot(B)` is deprecated as of version 0.17.0 and will be ' - ' removed no earlier than 3 months after the release date.' - ' As an alternative, use the compose operator `B & A`' - ' in place of `A * B` as a replacement.') + "Using the `__mul__` operator `A * B` as shorthand for" + " `A.dot(B)` is deprecated as of version 0.17.0 and will be " + " removed no earlier than 3 months after the release date." + " As an alternative, use the compose operator `B & A`" + " in place of `A * B` as a replacement." + ) def __mul__(self, other): return self.dot(other) @deprecate_function( - 'Using the `__matmul__` operator `A @ B` as shorthand for' - ' `A.compose(B)` is deprecated as of version 0.17.0 and will be ' - ' removed no earlier than 3 months after the release date.' - ' Use the `A & B` instead.') + "Using the `__matmul__` operator `A @ B` as shorthand for" + " `A.compose(B)` is deprecated as of version 0.17.0 and will be " + " removed no earlier than 3 months after the release date." + " Use the `A & B` instead." + ) def __matmul__(self, other): return self.compose(other) diff --git a/qiskit/quantum_info/operators/mixins/linear.py b/qiskit/quantum_info/operators/mixins/linear.py index 1671dd91241c..a512df475a45 100644 --- a/qiskit/quantum_info/operators/mixins/linear.py +++ b/qiskit/quantum_info/operators/mixins/linear.py @@ -38,11 +38,11 @@ class LinearMixin(MultiplyMixin, ABC): """ def __add__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) return self._add(other, qargs=qargs) def __sub__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) return self._add(-other, qargs=qargs) @abstractmethod diff --git a/qiskit/quantum_info/operators/mixins/multiply.py b/qiskit/quantum_info/operators/mixins/multiply.py index aac7b43f5bec..2ae3ec3e2f4d 100644 --- a/qiskit/quantum_info/operators/mixins/multiply.py +++ b/qiskit/quantum_info/operators/mixins/multiply.py @@ -31,6 +31,7 @@ class MultiplyMixin(ABC): - ``_multiply(self, other)`` """ + def __rmul__(self, other): return self._multiply(other) diff --git a/qiskit/quantum_info/operators/mixins/tolerances.py b/qiskit/quantum_info/operators/mixins/tolerances.py index 9325d6228c75..8757260eabb1 100644 --- a/qiskit/quantum_info/operators/mixins/tolerances.py +++ b/qiskit/quantum_info/operators/mixins/tolerances.py @@ -21,6 +21,7 @@ class TolerancesMeta(ABCMeta): """Metaclass to handle tolerances""" + def __init__(cls, *args, **kwargs): cls._ATOL_DEFAULT = ATOL_DEFAULT cls._RTOL_DEFAULT = RTOL_DEFAULT @@ -35,11 +36,11 @@ def atol(cls): def _check_value(cls, value, value_name): """Check if value is within valid ranges""" if value < 0: - raise QiskitError( - "Invalid {} ({}) must be non-negative.".format(value_name, value)) + raise QiskitError("Invalid {} ({}) must be non-negative.".format(value_name, value)) if value > cls._MAX_TOL: raise QiskitError( - "Invalid {} ({}) must be less than {}.".format(value_name, value, cls._MAX_TOL)) + "Invalid {} ({}) must be less than {}.".format(value_name, value, cls._MAX_TOL) + ) @atol.setter def atol(cls, value): diff --git a/qiskit/quantum_info/operators/op_shape.py b/qiskit/quantum_info/operators/op_shape.py index fdc1374c30e2..df4d81b8b56c 100644 --- a/qiskit/quantum_info/operators/op_shape.py +++ b/qiskit/quantum_info/operators/op_shape.py @@ -26,8 +26,7 @@ class OpShape: """Multipartite matrix and vector shape class.""" - def __init__(self, dims_l=None, dims_r=None, - num_qargs_l=None, num_qargs_r=None): + def __init__(self, dims_l=None, dims_r=None, num_qargs_l=None, num_qargs_r=None): """Initialize an operator object.""" # The number of left and right qargs self._num_qargs_l = 0 # the number of left (output) subsystems @@ -36,8 +35,8 @@ def __init__(self, dims_l=None, dims_r=None, # Subsystem dimensions # This is a tuple of dimensions for each subsystem # If None each subsystem is assumed to be a dim=2 (qubit) - self._dims_l = None # Tuple of left (output) dimensions - self._dims_r = None # tuple of right (input) dimensions + self._dims_l = None # Tuple of left (output) dimensions + self._dims_r = None # tuple of right (input) dimensions # Set attributes if num_qargs_r: @@ -53,33 +52,35 @@ def __init__(self, dims_l=None, dims_r=None, def __repr__(self): if self._dims_l: - left = 'dims_l={}'.format(self._dims_l) + left = "dims_l={}".format(self._dims_l) elif self._num_qargs_l: - left = 'num_qargs_l={}'.format(self._num_qargs_l) + left = "num_qargs_l={}".format(self._num_qargs_l) else: - left = '' + left = "" if self._dims_r: - right = 'dims_r={}'.format(self._dims_r) + right = "dims_r={}".format(self._dims_r) elif self._num_qargs_r: - right = 'num_qargs_r={}'.format(self._num_qargs_r) + right = "num_qargs_r={}".format(self._num_qargs_r) else: - right = '' + right = "" if left and right: - inner = '{}, {}'.format(left, right) + inner = "{}, {}".format(left, right) elif left: inner = left else: inner = right - return 'OpShape({})'.format(inner) + return "OpShape({})".format(inner) def __eq__(self, other): """Check types and subsystem dimensions are equal""" if not isinstance(other, OpShape): return False - return (self._num_qargs_r == other._num_qargs_r and - self._num_qargs_l == other._num_qargs_l and - self._dims_r == other._dims_r and - self._dims_l == other._dims_l) + return ( + self._num_qargs_r == other._num_qargs_r + and self._num_qargs_l == other._num_qargs_l + and self._dims_r == other._dims_r + and self._dims_l == other._dims_l + ) def copy(self): """Make a deep copy of current operator.""" @@ -114,7 +115,7 @@ def shape(self): """Return a tuple of the matrix shape""" if not self._num_qargs_r: # Vector shape - return (self._dim_l, ) + return (self._dim_l,) # Matrix shape return self._dim_l, self._dim_r @@ -126,8 +127,7 @@ def tensor_shape(self): @property def is_square(self): """Return True if the left and right dimensions are equal.""" - return (self._num_qargs_l == self._num_qargs_r - and self._dims_l == self._dims_r) + return self._num_qargs_l == self._num_qargs_r and self._dims_l == self._dims_r def dims_r(self, qargs=None): """Return tuple of input dimension for specified subsystems.""" @@ -136,7 +136,7 @@ def dims_r(self, qargs=None): return tuple(self._dims_r[i] for i in qargs) return self._dims_r num = self._num_qargs_r if qargs is None else len(qargs) - return num * (2, ) + return num * (2,) def dims_l(self, qargs=None): """Return tuple of output dimension for specified subsystems.""" @@ -145,7 +145,7 @@ def dims_l(self, qargs=None): return tuple(self._dims_l[i] for i in qargs) return self._dims_l num = self._num_qargs_l if qargs is None else len(qargs) - return num * (2, ) + return num * (2,) @property def _dim_r(self): @@ -172,7 +172,8 @@ def _validate(self, shape, raise_exception=False): if ndim > 2: if raise_exception: raise QiskitError( - "Input shape is not 1 or 2-dimensional (shape = {})".format(shape)) + "Input shape is not 1 or 2-dimensional (shape = {})".format(shape) + ) return False if self._dims_l: @@ -180,7 +181,8 @@ def _validate(self, shape, raise_exception=False): if raise_exception: raise QiskitError( "Output dimensions do not match matrix shape " - "({} != {})".format(reduce(mul, self._dims_l), shape[0])) + "({} != {})".format(reduce(mul, self._dims_l), shape[0]) + ) return False elif shape[0] != 2 ** self._num_qargs_l: if raise_exception: @@ -193,7 +195,8 @@ def _validate(self, shape, raise_exception=False): if raise_exception: raise QiskitError( "Input dimensions do not match matrix shape " - "({} != {})".format(reduce(mul, self._dims_r), shape[1])) + "({} != {})".format(reduce(mul, self._dims_r), shape[1]) + ) return False elif shape[1] != 2 ** self._num_qargs_r: if raise_exception: @@ -201,21 +204,29 @@ def _validate(self, shape, raise_exception=False): return False elif self._dims_r or self._num_qargs_r: if raise_exception: - raise QiskitError('Input dimension should be empty for vector shape.') + raise QiskitError("Input dimension should be empty for vector shape.") return False return True @classmethod - def auto(cls, shape=None, dims_l=None, dims_r=None, dims=None, - num_qubits_l=None, num_qubits_r=None, num_qubits=None): + def auto( + cls, + shape=None, + dims_l=None, + dims_r=None, + dims=None, + num_qubits_l=None, + num_qubits_r=None, + num_qubits=None, + ): """Construct TensorShape with automatic checking of qubit dimensions""" if dims and (dims_l or dims_r): - raise QiskitError( - "`dims` kwarg cannot be used with `dims_l` or `dims_r`") + raise QiskitError("`dims` kwarg cannot be used with `dims_l` or `dims_r`") if num_qubits and (num_qubits_l or num_qubits_r): raise QiskitError( - "`num_qubits` kwarg cannot be used with `num_qubits_l` or `num_qubits_r`") + "`num_qubits` kwarg cannot be used with `num_qubits_l` or `num_qubits_r`" + ) if num_qubits: num_qubits_l = num_qubits @@ -225,8 +236,7 @@ def auto(cls, shape=None, dims_l=None, dims_r=None, dims=None, dims_r = dims if num_qubits_r and num_qubits_l: - matrix_shape = cls(num_qargs_l=num_qubits_r, - num_qargs_r=num_qubits_l) + matrix_shape = cls(num_qargs_l=num_qubits_r, num_qargs_r=num_qubits_l) else: ndim = len(shape) if shape else 0 if dims_r is None and num_qubits_r is None and ndim > 1: @@ -241,7 +251,7 @@ def auto(cls, shape=None, dims_l=None, dims_r=None, dims=None, num_qubits_r = int(log2(dims_r)) dims_r = None else: - dims_r = (dims_r, ) + dims_r = (dims_r,) elif dims_r is not None: if set(dims_r) == {2}: num_qubits_r = len(dims_r) @@ -255,7 +265,7 @@ def auto(cls, shape=None, dims_l=None, dims_r=None, dims=None, num_qubits_l = int(log2(dims_l)) dims_l = None else: - dims_l = (dims_l, ) + dims_l = (dims_l,) elif dims_l is not None: if set(dims_l) == {2}: num_qubits_l = len(dims_l) @@ -263,9 +273,9 @@ def auto(cls, shape=None, dims_l=None, dims_r=None, dims=None, else: dims_l = tuple(dims_l) - matrix_shape = cls(dims_l=dims_l, dims_r=dims_r, - num_qargs_l=num_qubits_l, - num_qargs_r=num_qubits_r) + matrix_shape = cls( + dims_l=dims_l, dims_r=dims_r, num_qargs_l=num_qubits_l, num_qargs_r=num_qubits_r + ) # Validate shape if shape: matrix_shape.validate_shape(shape) @@ -284,9 +294,9 @@ def subset(self, qargs=None, qargs_l=None, qargs_r=None): # Format integer qargs if isinstance(qargs_l, Integral): - qargs_l = (qargs_l, ) + qargs_l = (qargs_l,) if isinstance(qargs_r, Integral): - qargs_r = (qargs_r, ) + qargs_r = (qargs_r,) # Validate qargs if qargs_l and max(qargs_l) >= self._num_qargs_l: @@ -309,9 +319,9 @@ def subset(self, qargs=None, qargs_l=None, qargs_r=None): if self._dims_r: dims_l = self.dims_r(qargs) - return OpShape(dims_l=dims_l, dims_r=dims_r, - num_qargs_l=num_qargs_l, - num_qargs_r=num_qargs_r) + return OpShape( + dims_l=dims_l, dims_r=dims_r, num_qargs_l=num_qargs_l, num_qargs_r=num_qargs_r + ) def remove(self, qargs=None, qargs_l=None, qargs_r=None): """Return the reduced OpShape with specified qargs removed""" @@ -328,9 +338,9 @@ def remove(self, qargs=None, qargs_l=None, qargs_r=None): # Format integer qargs if isinstance(qargs_l, Integral): - qargs_l = (qargs_l, ) + qargs_l = (qargs_l,) if isinstance(qargs_r, Integral): - qargs_r = (qargs_r, ) + qargs_r = (qargs_r,) # Validate qargs if qargs_l and max(qargs_l) >= self._num_qargs_l: @@ -344,20 +354,18 @@ def remove(self, qargs=None, qargs_l=None, qargs_r=None): if qargs_l: num_qargs_l = self._num_qargs_l - len(qargs_l) if self._dims_l: - dims_l = self.dims_l(tuple(i for i in range(self._num_qargs_l) - if i not in qargs_l)) + dims_l = self.dims_l(tuple(i for i in range(self._num_qargs_l) if i not in qargs_l)) num_qargs_r = 0 dims_r = None if qargs_r: num_qargs_r = self._num_qargs_r - len(qargs_r) if self._dims_r: - dims_l = self.dims_r(tuple(i for i in range(self._num_qargs_r) - if i not in qargs_r)) + dims_l = self.dims_r(tuple(i for i in range(self._num_qargs_r) if i not in qargs_r)) - return OpShape(dims_l=dims_l, dims_r=dims_r, - num_qargs_l=num_qargs_l, - num_qargs_r=num_qargs_r) + return OpShape( + dims_l=dims_l, dims_r=dims_r, num_qargs_l=num_qargs_l, num_qargs_r=num_qargs_r + ) def reverse(self): """Reverse order of left and right qargs""" @@ -400,30 +408,28 @@ def _tensor(cls, a, b): else: dims_r = None num_qargs_r = b._num_qargs_r + a._num_qargs_r - return cls(dims_l=dims_l, dims_r=dims_r, - num_qargs_l=num_qargs_l, - num_qargs_r=num_qargs_r) + return cls(dims_l=dims_l, dims_r=dims_r, num_qargs_l=num_qargs_l, num_qargs_r=num_qargs_r) def compose(self, other, qargs=None, front=False): """Return composed OpShape.""" ret = OpShape() if not qargs: if front: - if (self._num_qargs_r != other._num_qargs_l - or self._dims_r != other._dims_l): + if self._num_qargs_r != other._num_qargs_l or self._dims_r != other._dims_l: raise QiskitError( "Left and right compose dimensions don't match " - "({} != {})".format(self.dims_r(), other.dims_l())) + "({} != {})".format(self.dims_r(), other.dims_l()) + ) ret._dims_l = self._dims_l ret._dims_r = other._dims_r ret._num_qargs_l = self._num_qargs_l ret._num_qargs_r = other._num_qargs_r else: - if (self._num_qargs_l != other._num_qargs_r - or self._dims_l != other._dims_r): + if self._num_qargs_l != other._num_qargs_r or self._dims_l != other._dims_r: raise QiskitError( "Left and right compose dimensions don't match " - "({} != {})".format(self.dims_l(), other.dims_r())) + "({} != {})".format(self.dims_l(), other.dims_r()) + ) ret._dims_l = other._dims_l ret._dims_r = self._dims_r ret._num_qargs_l = other._num_qargs_l @@ -434,13 +440,17 @@ def compose(self, other, qargs=None, front=False): ret._dims_l = self._dims_l ret._num_qargs_l = self._num_qargs_l if len(qargs) != other._num_qargs_l: - raise QiskitError("Number of qargs does not match ({} != {})".format( - len(qargs), other._num_qargs_l)) + raise QiskitError( + "Number of qargs does not match ({} != {})".format( + len(qargs), other._num_qargs_l + ) + ) if self._dims_r or other._dims_r: if self.dims_r(qargs) != other.dims_l(): raise QiskitError( "Subsystem dimension do not match on specified qargs " - "{} != {}".format(self.dims_r(qargs), other.dims_l())) + "{} != {}".format(self.dims_r(qargs), other.dims_l()) + ) dims_r = list(self.dims_r()) for i, dim in zip(qargs, other.dims_r()): dims_r[i] = dim @@ -451,13 +461,17 @@ def compose(self, other, qargs=None, front=False): ret._dims_r = self._dims_r ret._num_qargs_r = self._num_qargs_r if len(qargs) != other._num_qargs_r: - raise QiskitError("Number of qargs does not match ({} != {})".format( - len(qargs), other._num_qargs_r)) + raise QiskitError( + "Number of qargs does not match ({} != {})".format( + len(qargs), other._num_qargs_r + ) + ) if self._dims_l or other._dims_l: if self.dims_l(qargs) != other.dims_r(): raise QiskitError( "Subsystem dimension do not match on specified qargs " - "{} != {}".format(self.dims_l(qargs), other.dims_r())) + "{} != {}".format(self.dims_l(qargs), other.dims_r()) + ) dims_l = list(self.dims_l()) for i, dim in zip(qargs, other.dims_l()): dims_l[i] = dim @@ -475,25 +489,31 @@ def _validate_add(self, other, qargs=None): if qargs: if self._num_qargs_l != self._num_qargs_r: raise QiskitError( - "Cannot add using qargs if number of left and right " - "qargs are not equal.") + "Cannot add using qargs if number of left and right " "qargs are not equal." + ) if self.dims_l(qargs) != other.dims_l(): raise QiskitError( "Cannot add shapes width different left " "dimension on specified qargs {} != {}".format( - self.dims_l(qargs), other.dims_l())) + self.dims_l(qargs), other.dims_l() + ) + ) if self.dims_r(qargs) != other.dims_r(): raise QiskitError( "Cannot add shapes width different total right " "dimension on specified qargs{} != {}".format( - self.dims_r(qargs), other.dims_r())) + self.dims_r(qargs), other.dims_r() + ) + ) elif self != other: if self._dim_l != other._dim_l: raise QiskitError( "Cannot add shapes width different total left " - "dimension {} != {}".format(self._dim_l, other._dim_l)) + "dimension {} != {}".format(self._dim_l, other._dim_l) + ) if self._dim_r != other._dim_r: raise QiskitError( "Cannot add shapes width different total right " - "dimension {} != {}".format(self._dim_r, other._dim_r)) + "dimension {} != {}".format(self._dim_r, other._dim_r) + ) return self diff --git a/qiskit/quantum_info/operators/operator.py b/qiskit/quantum_info/operators/operator.py index 934a2084e57d..3359461169ce 100644 --- a/qiskit/quantum_info/operators/operator.py +++ b/qiskit/quantum_info/operators/operator.py @@ -84,14 +84,14 @@ def __init__(self, data, input_dims=None, output_dims=None): # conditional gates, measure, or reset will cause an # exception to be raised. self._data = self._init_instruction(data).data - elif hasattr(data, 'to_operator'): + elif hasattr(data, "to_operator"): # If the data object has a 'to_operator' attribute this is given # higher preference than the 'to_matrix' method for initializing # an Operator object. data = data.to_operator() self._data = data.data op_shape = data._op_shape - elif hasattr(data, 'to_matrix'): + elif hasattr(data, "to_matrix"): # If no 'to_operator' attribute exists we next look for a # 'to_matrix' attribute to a matrix that will be cast into # a complex numpy matrix. @@ -99,10 +99,12 @@ def __init__(self, data, input_dims=None, output_dims=None): else: raise QiskitError("Invalid input data format for Operator") - super().__init__(op_shape=op_shape, - input_dims=input_dims, - output_dims=output_dims, - shape=self._data.shape) + super().__init__( + op_shape=op_shape, + input_dims=input_dims, + output_dims=output_dims, + shape=self._data.shape, + ) def __array__(self, dtype=None): if dtype: @@ -110,19 +112,21 @@ def __array__(self, dtype=None): return self.data def __repr__(self): - prefix = 'Operator(' - pad = len(prefix) * ' ' - return '{}{},\n{}input_dims={}, output_dims={})'.format( - prefix, np.array2string( - self.data, separator=', ', prefix=prefix), - pad, self.input_dims(), self.output_dims()) + prefix = "Operator(" + pad = len(prefix) * " " + return "{}{},\n{}input_dims={}, output_dims={})".format( + prefix, + np.array2string(self.data, separator=", ", prefix=prefix), + pad, + self.input_dims(), + self.output_dims(), + ) def __eq__(self, other): """Test if two Operators are equal.""" if not super().__eq__(other): return False - return np.allclose( - self.data, other.data, rtol=self.rtol, atol=self.atol) + return np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) @property def data(self): @@ -162,27 +166,27 @@ def from_label(cls, label): """ # Check label is valid label_mats = { - 'I': IGate().to_matrix(), - 'X': XGate().to_matrix(), - 'Y': YGate().to_matrix(), - 'Z': ZGate().to_matrix(), - 'H': HGate().to_matrix(), - 'S': SGate().to_matrix(), - 'T': TGate().to_matrix(), - '0': np.array([[1, 0], [0, 0]], dtype=complex), - '1': np.array([[0, 0], [0, 1]], dtype=complex), - '+': np.array([[0.5, 0.5], [0.5, 0.5]], dtype=complex), - '-': np.array([[0.5, -0.5], [-0.5, 0.5]], dtype=complex), - 'r': np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=complex), - 'l': np.array([[0.5, 0.5j], [-0.5j, 0.5]], dtype=complex), + "I": IGate().to_matrix(), + "X": XGate().to_matrix(), + "Y": YGate().to_matrix(), + "Z": ZGate().to_matrix(), + "H": HGate().to_matrix(), + "S": SGate().to_matrix(), + "T": TGate().to_matrix(), + "0": np.array([[1, 0], [0, 0]], dtype=complex), + "1": np.array([[0, 0], [0, 1]], dtype=complex), + "+": np.array([[0.5, 0.5], [0.5, 0.5]], dtype=complex), + "-": np.array([[0.5, -0.5], [-0.5, 0.5]], dtype=complex), + "r": np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=complex), + "l": np.array([[0.5, 0.5j], [-0.5j, 0.5]], dtype=complex), } - if re.match(r'^[IXYZHST01rl\-+]+$', label) is None: - raise QiskitError('Label contains invalid characters.') + if re.match(r"^[IXYZHST01rl\-+]+$", label) is None: + raise QiskitError("Label contains invalid characters.") # Initialize an identity matrix and apply each gate num_qubits = len(label) op = Operator(np.eye(2 ** num_qubits, dtype=complex)) for qubit, char in enumerate(reversed(label)): - if char != 'I': + if char != "I": op = op.compose(label_mats[char], qargs=[qubit]) return op @@ -202,6 +206,7 @@ def to_instruction(self): """Convert to a UnitaryGate instruction.""" # pylint: disable=cyclic-import from qiskit.extensions.unitary import UnitaryGate + return UnitaryGate(self.data) def conjugate(self): @@ -219,7 +224,7 @@ def transpose(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, Operator): other = Operator(other) @@ -261,8 +266,8 @@ def compose(self, other, qargs=None, front=False): indices = [num_indices - 1 - qubit for qubit in qargs] final_shape = [np.product(output_dims), np.product(input_dims)] data = np.reshape( - Operator._einsum_matmul(tensor, mat, indices, shift, right_mul), - final_shape) + Operator._einsum_matmul(tensor, mat, indices, shift, right_mul), final_shape + ) ret = Operator(data, input_dims, output_dims) ret._op_shape = new_shape return ret @@ -325,7 +330,7 @@ def _add(self, other, qargs=None): from qiskit.quantum_info.operators.scalar_op import ScalarOp if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, Operator): other = Operator(other) @@ -377,8 +382,7 @@ def equiv(self, other, rtol=None, atol=None): atol = self.atol if rtol is None: rtol = self.rtol - return matrix_equal(self.data, other.data, ignore_phase=True, - rtol=rtol, atol=atol) + return matrix_equal(self.data, other.data, ignore_phase=True, rtol=rtol, atol=atol) def reverse_qargs(self): r"""Return an Operator with reversed subsystem ordering. @@ -395,9 +399,10 @@ def reverse_qargs(self): ret = copy.copy(self) axes = tuple(range(self._op_shape._num_qargs_l - 1, -1, -1)) axes = axes + tuple(len(axes) + i for i in axes) - ret._data = np.reshape(np.transpose( - np.reshape(self.data, self._op_shape.tensor_shape), axes), - self._op_shape.shape) + ret._data = np.reshape( + np.transpose(np.reshape(self.data, self._op_shape.tensor_shape), axes), + self._op_shape.shape, + ) ret._op_shape = self._op_shape.reverse() return ret @@ -422,8 +427,7 @@ def _einsum_matmul(cls, tensor, mat, indices, shift=0, right_mul=False): rank = tensor.ndim rank_mat = mat.ndim if rank_mat % 2 != 0: - raise QiskitError( - "Contracted matrix must have an even number of indices.") + raise QiskitError("Contracted matrix must have an even number of indices.") # Get einsum indices for tensor indices_tensor = list(range(rank)) for j, index in enumerate(indices): @@ -441,7 +445,7 @@ def _einsum_matmul(cls, tensor, mat, indices, shift=0, right_mul=False): def _init_instruction(cls, instruction): """Convert a QuantumCircuit or Instruction to an Operator.""" # Initialize an identity operator of the correct size of the circuit - if hasattr(instruction, '__array__'): + if hasattr(instruction, "__array__"): return Operator(np.array(instruction, dtype=complex)) dimension = 2 ** instruction.num_qubits @@ -456,9 +460,9 @@ def _init_instruction(cls, instruction): def _instruction_to_matrix(cls, obj): """Return Operator for instruction if defined or None otherwise.""" if not isinstance(obj, Instruction): - raise QiskitError('Input is not an instruction.') + raise QiskitError("Input is not an instruction.") mat = None - if hasattr(obj, 'to_matrix'): + if hasattr(obj, "to_matrix"): # If instruction is a gate first we see if it has a # `to_matrix` definition and if so use that. try: @@ -485,27 +489,33 @@ def _append_instruction(self, obj, qargs=None): # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if obj.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format(obj.name)) + raise QiskitError("Cannot apply Instruction: {}".format(obj.name)) if not isinstance(obj.definition, QuantumCircuit): - raise QiskitError('Instruction "{}" ' - 'definition is {} but expected QuantumCircuit.'.format( - obj.name, type(obj.definition))) + raise QiskitError( + 'Instruction "{}" ' + "definition is {} but expected QuantumCircuit.".format( + obj.name, type(obj.definition) + ) + ) if obj.definition.global_phase: dimension = 2 ** obj.num_qubits op = self.compose( ScalarOp(dimension, np.exp(1j * float(obj.definition.global_phase))), - qargs=qargs) + qargs=qargs, + ) self._data = op.data flat_instr = obj.definition - bit_indices = {bit: index - for bits in [flat_instr.qubits, flat_instr.clbits] - for index, bit in enumerate(bits)} + bit_indices = { + bit: index + for bits in [flat_instr.qubits, flat_instr.clbits] + for index, bit in enumerate(bits) + } for instr, qregs, cregs in flat_instr: if cregs: raise QiskitError( - 'Cannot apply instruction with classical registers: {}'.format( - instr.name)) + "Cannot apply instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register if qargs is None: new_qargs = [bit_indices[tup] for tup in qregs] diff --git a/qiskit/quantum_info/operators/pauli.py b/qiskit/quantum_info/operators/pauli.py index d4190362e1dc..bb11d59549c4 100644 --- a/qiskit/quantum_info/operators/pauli.py +++ b/qiskit/quantum_info/operators/pauli.py @@ -21,7 +21,7 @@ from qiskit.quantum_info.operators.symplectic.pauli import Pauli -def pauli_group(number_of_qubits, case='weight'): +def pauli_group(number_of_qubits, case="weight"): """DEPRECATED: Return the Pauli group with 4^n elements. This function is deprecated. Use :func:`~qiskit.quantum_info.pauli_basis` @@ -42,21 +42,23 @@ def pauli_group(number_of_qubits, case='weight'): QiskitError: case is not 'weight' or 'tensor' QiskitError: number_of_qubits is larger than 4 """ - warn('`insert_paulis` is deprecated and will be removed no earlier than ' - '3 months after the release date. For equivalent functionality to' - '`qiskit.quantum_info.pauli_group` instead. ' - '`pauli_group(n)` is equivalent to `pauli_basis(n, weight=True)`, ' - '`pauli_group(n, case="tensor") is equivalent to `pauli_basis(n)`', - DeprecationWarning, stacklevel=2) + warn( + "`insert_paulis` is deprecated and will be removed no earlier than " + "3 months after the release date. For equivalent functionality to" + "`qiskit.quantum_info.pauli_group` instead. " + "`pauli_group(n)` is equivalent to `pauli_basis(n, weight=True)`, " + '`pauli_group(n, case="tensor") is equivalent to `pauli_basis(n)`', + DeprecationWarning, + stacklevel=2, + ) if number_of_qubits < 5: temp_set = [] - if case == 'weight': - tmp = pauli_group(number_of_qubits, case='tensor') + if case == "weight": + tmp = pauli_group(number_of_qubits, case="tensor") # sort on the weight of the Pauli operator - return sorted(tmp, key=lambda x: -np.count_nonzero( - np.array(x.to_label(), 'c') == b'I')) - elif case == 'tensor': + return sorted(tmp, key=lambda x: -np.count_nonzero(np.array(x.to_label(), "c") == b"I")) + elif case == "tensor": # the Pauli set is in tensor order II IX IY IZ XI ... for k in range(4 ** number_of_qubits): z = np.zeros(number_of_qubits, dtype=bool) @@ -76,7 +78,8 @@ def pauli_group(number_of_qubits, case='weight'): temp_set.append(Pauli(z, x)) return temp_set else: - raise QiskitError("Only support 'weight' or 'tensor' cases " - "but you have {}.".format(case)) + raise QiskitError( + "Only support 'weight' or 'tensor' cases " "but you have {}.".format(case) + ) raise QiskitError("Only support number of qubits is less than 5") diff --git a/qiskit/quantum_info/operators/predicates.py b/qiskit/quantum_info/operators/predicates.py index 17621e9e8ca0..feddaa06ca11 100644 --- a/qiskit/quantum_info/operators/predicates.py +++ b/qiskit/quantum_info/operators/predicates.py @@ -22,11 +22,7 @@ RTOL_DEFAULT = 1e-5 -def matrix_equal(mat1, - mat2, - ignore_phase=False, - rtol=RTOL_DEFAULT, - atol=ATOL_DEFAULT): +def matrix_equal(mat1, mat2, ignore_phase=False, rtol=RTOL_DEFAULT, atol=ATOL_DEFAULT): """Test if two arrays are equal. The final comparison is implemented using Numpy.allclose. See its @@ -46,7 +42,9 @@ def matrix_equal(mat1, Returns: bool: True if the matrices are equal or False otherwise. - """.format(RTOL_DEFAULT, ATOL_DEFAULT) + """.format( + RTOL_DEFAULT, ATOL_DEFAULT + ) if atol is None: atol = ATOL_DEFAULT @@ -136,10 +134,7 @@ def is_positive_semidefinite_matrix(mat, rtol=RTOL_DEFAULT, atol=ATOL_DEFAULT): return True -def is_identity_matrix(mat, - ignore_phase=False, - rtol=RTOL_DEFAULT, - atol=ATOL_DEFAULT): +def is_identity_matrix(mat, ignore_phase=False, rtol=RTOL_DEFAULT, atol=ATOL_DEFAULT): """Test if an array is an identity matrix.""" if atol is None: atol = ATOL_DEFAULT diff --git a/qiskit/quantum_info/operators/random.py b/qiskit/quantum_info/operators/random.py index 10ccfc88c3d8..20b7d0aa9cfd 100644 --- a/qiskit/quantum_info/operators/random.py +++ b/qiskit/quantum_info/operators/random.py @@ -52,6 +52,7 @@ def random_unitary(dims, seed=None): dim = np.product(dims) from scipy import stats + mat = stats.unitary_group.rvs(dim, random_state=random_state) return Operator(mat, input_dims=dims, output_dims=dims) @@ -87,15 +88,12 @@ def random_hermitian(dims, traceless=False, seed=None): mat = np.zeros((dim, dim), dtype=complex) else: # Generate diagonal part of matrix for Gaussian N(0, 1) - mat = np.diag(stats.norm.rvs( - scale=1, size=dim, random_state=rng).astype(complex)) + mat = np.diag(stats.norm.rvs(scale=1, size=dim, random_state=rng).astype(complex)) # Generate lower triangular values from Gaussian N(0, 0.5) num_tril = (dim * (dim - 1)) // 2 - real_tril = stats.norm.rvs( - scale=0.5, size=num_tril, random_state=rng) - imag_tril = stats.norm.rvs( - scale=0.5, size=num_tril, random_state=rng) + real_tril = stats.norm.rvs(scale=0.5, size=num_tril, random_state=rng) + imag_tril = stats.norm.rvs(scale=0.5, size=num_tril, random_state=rng) # Get lower triangular indices rows, cols = np.tril_indices(dim, -1) mat[(rows, cols)] = real_tril + 1j * imag_tril @@ -103,10 +101,7 @@ def random_hermitian(dims, traceless=False, seed=None): return Operator(mat, input_dims=dims, output_dims=dims) -def random_quantum_channel(input_dims=None, - output_dims=None, - rank=None, - seed=None): +def random_quantum_channel(input_dims=None, output_dims=None, rank=None, seed=None): """Return a random CPTP quantum channel. This constructs the Stinespring operator for the quantum channel by @@ -128,8 +123,8 @@ def random_quantum_channel(input_dims=None, # Determine total input and output dimensions if input_dims is None and output_dims is None: raise QiskitError( - 'No dimensions specified: input_dims and output_dims cannot' - ' both be None.') + "No dimensions specified: input_dims and output_dims cannot" " both be None." + ) if input_dims is None: input_dims = output_dims elif output_dims is None: @@ -147,9 +142,7 @@ def random_quantum_channel(input_dims=None, from scipy import stats # Generate a random unitary matrix - unitary = stats.unitary_group.rvs( - max(rank * d_out, d_in), random_state=seed) + unitary = stats.unitary_group.rvs(max(rank * d_out, d_in), random_state=seed) # Truncate columns to produce an isometry - return Stinespring( - unitary[:, :d_in], input_dims=input_dims, output_dims=output_dims) + return Stinespring(unitary[:, :d_in], input_dims=input_dims, output_dims=output_dims) diff --git a/qiskit/quantum_info/operators/scalar_op.py b/qiskit/quantum_info/operators/scalar_op.py index d8ee0d2a97b7..5406b8ba7b52 100644 --- a/qiskit/quantum_info/operators/scalar_op.py +++ b/qiskit/quantum_info/operators/scalar_op.py @@ -34,6 +34,7 @@ class ScalarOp(LinearOp): kinds of operator subclasses by using the :meth:`compose`, :meth:`dot`, :meth:`tensor`, :meth:`expand` methods. """ + def __init__(self, dims=None, coeff=1): """Initialize an operator object. @@ -56,8 +57,7 @@ def __array__(self, dtype=None): return self.to_matrix() def __repr__(self): - return 'ScalarOp({}, coeff={})'.format( - self.input_dims(), self.coeff) + return "ScalarOp({}, coeff={})".format(self.input_dims(), self.coeff) @property def coeff(self): @@ -88,13 +88,13 @@ def to_matrix(self): def to_operator(self): """Convert to an Operator object.""" - return Operator(self.to_matrix(), - input_dims=self.input_dims(), - output_dims=self.output_dims()) + return Operator( + self.to_matrix(), input_dims=self.input_dims(), output_dims=self.output_dims() + ) def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, BaseOperator): other = Operator(other) @@ -127,8 +127,7 @@ def compose(self, other, qargs=None, front=False): # Note that this will raise an error if the other operator does # not support initialization from a ScalarOp or the ScalarOps # `to_operator` method). - return other.__class__(self).compose( - other, qargs=qargs, front=front) + return other.__class__(self).compose(other, qargs=qargs, front=front) def power(self, n): """Return the power of the ScalarOp. @@ -186,7 +185,7 @@ def _add(self, other, qargs=None): QiskitError: if other has incompatible dimensions. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, BaseOperator): other = Operator(other) @@ -195,7 +194,7 @@ def _add(self, other, qargs=None): # Next if we are adding two ScalarOps we return a ScalarOp if isinstance(other, ScalarOp): - return ScalarOp(self.input_dims(), coeff=self.coeff+other.coeff) + return ScalarOp(self.input_dims(), coeff=self.coeff + other.coeff) # If qargs are specified we have to pad the other BaseOperator # with identities on remaining subsystems. We do this by diff --git a/qiskit/quantum_info/operators/symplectic/base_pauli.py b/qiskit/quantum_info/operators/symplectic/base_pauli.py index 116b0260865b..1d1db2739016 100644 --- a/qiskit/quantum_info/operators/symplectic/base_pauli.py +++ b/qiskit/quantum_info/operators/symplectic/base_pauli.py @@ -91,21 +91,25 @@ def compose(self, other, qargs=None, front=False, inplace=False): Raises: QiskitError: if number of qubits of other does not match qargs. - """.format(cls=type(self).__name__) + """.format( + cls=type(self).__name__ + ) # Validation if qargs is None and other.num_qubits != self.num_qubits: raise QiskitError( - "other {} must be on the same number of qubits.".format( - type(self).__name__)) + "other {} must be on the same number of qubits.".format(type(self).__name__) + ) if qargs and other.num_qubits != len(qargs): raise QiskitError( - "Number of qubits of the other {} does not match qargs.".format( - type(self).__name__)) + "Number of qubits of the other {} does not match qargs.".format(type(self).__name__) + ) if other._num_paulis not in [1, self._num_paulis]: - raise QiskitError("Incompatible BasePaulis. Second list must " - "either have 1 or the same number of Paulis.") + raise QiskitError( + "Incompatible BasePaulis. Second list must " + "either have 1 or the same number of Paulis." + ) # Compute phase shift if qargs is not None: @@ -152,7 +156,9 @@ def _multiply(self, other): Raises: QiskitError: if the phase is not in the set ``[1, -1j, -1, 1j]``. - """.format(cls=type(self).__name__) + """.format( + cls=type(self).__name__ + ) if isinstance(other, (np.ndarray, list, tuple)): phase = np.array([self._phase_from_complex(phase) for phase in other]) else: @@ -191,11 +197,13 @@ def commutes(self, other, qargs=None): if qargs is not None and len(qargs) != other.num_qubits: raise QiskitError( "Number of qubits of other Pauli does not match number of " - "qargs ({} != {}).".format(other.num_qubits, len(qargs))) + "qargs ({} != {}).".format(other.num_qubits, len(qargs)) + ) if qargs is None and self.num_qubits != other.num_qubits: raise QiskitError( "Number of qubits of other Pauli does not match the current " - "Pauli ({} != {}).".format(other.num_qubits, self.num_qubits)) + "Pauli ({} != {}).".format(other.num_qubits, self.num_qubits) + ) if qargs is not None: inds = list(qargs) x1, z1 = self._x[:, inds], self._z[:, inds] @@ -224,11 +232,15 @@ def evolve(self, other, qargs=None): if qargs is not None and len(qargs) != other.num_qubits: raise QiskitError( "Incorrect number of qubits for Clifford circuit ({} != {}).".format( - other.num_qubits, len(qargs))) + other.num_qubits, len(qargs) + ) + ) if qargs is None and self.num_qubits != other.num_qubits: raise QiskitError( "Incorrect number of qubits for Clifford circuit ({} != {}).".format( - other.num_qubits, self.num_qubits)) + other.num_qubits, self.num_qubits + ) + ) # Evolve via Pauli if isinstance(other, BasePauli): @@ -300,8 +312,7 @@ def _from_array(z, x, phase=0): raise QiskitError("z and x vectors are different size.") # Convert group phase convention to internal ZX-phase conversion. - base_phase = np.mod(np.sum(np.logical_and(base_x, base_z), - axis=1, dtype=int) + phase, 4) + base_phase = np.mod(np.sum(np.logical_and(base_x, base_z), axis=1, dtype=int) + phase, 4) return base_z, base_x, base_phase @staticmethod @@ -333,7 +344,7 @@ def _to_matrix(z, x, phase=0, group_phase=False, sparse=False): phase += np.sum(x & z) phase %= 4 - dim = 2**num_qubits + dim = 2 ** num_qubits twos_array = 1 << np.arange(num_qubits) x_indices = np.asarray(x).dot(twos_array) z_indices = np.asarray(z).dot(twos_array) @@ -341,26 +352,24 @@ def _to_matrix(z, x, phase=0, group_phase=False, sparse=False): indptr = np.arange(dim + 1, dtype=np.uint) indices = indptr ^ x_indices if phase: - coeff = (-1j)**phase + coeff = (-1j) ** phase else: coeff = 1 - data = np.array([coeff * (-1) ** (bin(i).count('1') % 2) - for i in z_indices & indptr]) + data = np.array([coeff * (-1) ** (bin(i).count("1") % 2) for i in z_indices & indptr]) if sparse: # Return sparse matrix from scipy.sparse import csr_matrix - return csr_matrix((data, indices, indptr), shape=(dim, dim), - dtype=complex) + + return csr_matrix((data, indices, indptr), shape=(dim, dim), dtype=complex) # Build dense matrix using csr format mat = np.zeros((dim, dim), dtype=complex) for i in range(dim): - mat[i][indices[indptr[i]:indptr[i + 1]]] = data[indptr[i]:indptr[i + 1]] + mat[i][indices[indptr[i] : indptr[i + 1]]] = data[indptr[i] : indptr[i + 1]] return mat @staticmethod - def _to_label(z, x, phase, group_phase=False, - full_group=True, return_phase=False): + def _to_label(z, x, phase, group_phase=False, full_group=True, return_phase=False): """Return the label string for a Pauli. Args: @@ -387,18 +396,18 @@ def _to_label(z, x, phase, group_phase=False, for the label from the full Pauli group. """ num_qubits = z.size - coeff_labels = {0: '', 1: '-i', 2: '-', 3: 'i'} - label = '' + coeff_labels = {0: "", 1: "-i", 2: "-", 3: "i"} + label = "" for i in range(num_qubits): if not z[num_qubits - 1 - i]: if not x[num_qubits - 1 - i]: - label += 'I' + label += "I" else: - label += 'X' + label += "X" elif not x[num_qubits - 1 - i]: - label += 'Z' + label += "Z" else: - label += 'Y' + label += "Y" if not group_phase: phase -= 1 phase %= 4 @@ -434,32 +443,26 @@ def _append_circuit(self, circuit, qargs=None): # Basis Clifford Gates basis_1q = { - 'i': _evolve_i, - 'id': _evolve_i, - 'iden': _evolve_i, - 'x': _evolve_x, - 'y': _evolve_y, - 'z': _evolve_z, - 'h': _evolve_h, - 's': _evolve_s, - 'sdg': _evolve_sdg, - 'sinv': _evolve_sdg - } - basis_2q = { - 'cx': _evolve_cx, - 'cz': _evolve_cz, - 'cy': _evolve_cy, - 'swap': _evolve_swap + "i": _evolve_i, + "id": _evolve_i, + "iden": _evolve_i, + "x": _evolve_x, + "y": _evolve_y, + "z": _evolve_z, + "h": _evolve_h, + "s": _evolve_s, + "sdg": _evolve_sdg, + "sinv": _evolve_sdg, } + basis_2q = {"cx": _evolve_cx, "cz": _evolve_cz, "cy": _evolve_cy, "swap": _evolve_swap} # Non-Clifford gates - non_clifford = ['t', 'tdg', 'ccx', 'ccz'] + non_clifford = ["t", "tdg", "ccx", "ccz"] if isinstance(gate, str): # Check if gate is a valid Clifford basis gate string if gate not in basis_1q and gate not in basis_2q: - raise QiskitError( - "Invalid Clifford gate name string {}".format(gate)) + raise QiskitError("Invalid Clifford gate name string {}".format(gate)) name = gate else: # Assume gate is an Instruction @@ -467,8 +470,7 @@ def _append_circuit(self, circuit, qargs=None): # Apply gate if it is a Clifford basis gate if name in non_clifford: - raise QiskitError( - "Cannot update Pauli with non-Clifford gate {}".format(name)) + raise QiskitError("Cannot update Pauli with non-Clifford gate {}".format(name)) if name in basis_1q: if len(qargs) != 1: raise QiskitError("Invalid qubits for 1-qubit gate.") @@ -481,22 +483,26 @@ def _append_circuit(self, circuit, qargs=None): # If not a Clifford basis gate we try to unroll the gate and # raise an exception if unrolling reaches a non-Clifford gate. if gate.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format(gate.name)) + raise QiskitError("Cannot apply Instruction: {}".format(gate.name)) if not isinstance(gate.definition, QuantumCircuit): raise QiskitError( - '{} instruction definition is {}; expected QuantumCircuit'.format( - gate.name, type(gate.definition))) + "{} instruction definition is {}; expected QuantumCircuit".format( + gate.name, type(gate.definition) + ) + ) flat_instr = gate.definition - bit_indices = {bit: index - for bits in [flat_instr.qubits, flat_instr.clbits] - for index, bit in enumerate(bits)} + bit_indices = { + bit: index + for bits in [flat_instr.qubits, flat_instr.clbits] + for index, bit in enumerate(bits) + } for instr, qregs, cregs in flat_instr: if cregs: raise QiskitError( - 'Cannot apply Instruction with classical registers: {}'.format( - instr.name)) + "Cannot apply Instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register new_qubits = [qargs[bit_indices[tup]] for tup in qregs] self._append_circuit(instr, new_qubits) @@ -511,6 +517,7 @@ def _append_circuit(self, circuit, qargs=None): # Evolution by Clifford gates # --------------------------------------------------------------------- + def _evolve_h(base_pauli, qubit): """Update P -> H.P.H""" x = base_pauli._x[:, qubit].copy() diff --git a/qiskit/quantum_info/operators/symplectic/clifford.py b/qiskit/quantum_info/operators/symplectic/clifford.py index 43799adcbf74..2a9a94671aaa 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford.py +++ b/qiskit/quantum_info/operators/symplectic/clifford.py @@ -100,6 +100,7 @@ class Clifford(BaseOperator, AdjointMixin): Phys. Rev. A 70, 052328 (2004). `arXiv:quant-ph/0406196 `_ """ + def __array__(self, dtype=None): if dtype: return np.asarray(self.to_matrix(), dtype=dtype) @@ -117,8 +118,7 @@ def __init__(self, data, validate=True): elif isinstance(data, ScalarOp): if not data.num_qubits or not data.is_unitary(): raise QiskitError("Can only initialize from N-qubit identity ScalarOp.") - self._table = StabilizerTable( - np.eye(2 * data.num_qubits, dtype=bool)) + self._table = StabilizerTable(np.eye(2 * data.num_qubits, dtype=bool)) # Initialize from a QuantumCircuit or Instruction object elif isinstance(data, (QuantumCircuit, Instruction)): @@ -131,19 +131,19 @@ def __init__(self, data, validate=True): # Validate table is a symplectic matrix if validate and not Clifford._is_symplectic(self._table.array): raise QiskitError( - 'Invalid Clifford. Input StabilizerTable is not a valid' - ' symplectic matrix.') + "Invalid Clifford. Input StabilizerTable is not a valid" " symplectic matrix." + ) # Initialize BaseOperator super().__init__(num_qubits=self._table.num_qubits) def __repr__(self): - return 'Clifford({})'.format(repr(self.table)) + return "Clifford({})".format(repr(self.table)) def __str__(self): - return 'Clifford: Stabilizer = {}, Destabilizer = {}'.format( - str(self.stabilizer.to_labels()), - str(self.destabilizer.to_labels())) + return "Clifford: Stabilizer = {}, Destabilizer = {}".format( + str(self.stabilizer.to_labels()), str(self.destabilizer.to_labels()) + ) def __eq__(self, other): """Check if two Clifford tables are equal""" @@ -179,18 +179,18 @@ def table(self, value): @property def stabilizer(self): """Return the stabilizer block of the StabilizerTable.""" - return StabilizerTable(self._table[self.num_qubits:2*self.num_qubits]) + return StabilizerTable(self._table[self.num_qubits : 2 * self.num_qubits]) @stabilizer.setter def stabilizer(self, value): """Set the value of stabilizer block of the StabilizerTable""" - inds = slice(self.num_qubits, 2*self.num_qubits) + inds = slice(self.num_qubits, 2 * self.num_qubits) self._table.__setitem__(inds, value) @property def destabilizer(self): """Return the destabilizer block of the StabilizerTable.""" - return StabilizerTable(self._table[0:self.num_qubits]) + return StabilizerTable(self._table[0 : self.num_qubits]) @destabilizer.setter def destabilizer(self, value): @@ -214,13 +214,13 @@ def is_unitary(self): # --------------------------------------------------------------------- def conjugate(self): - return Clifford._conjugate_transpose(self, 'C') + return Clifford._conjugate_transpose(self, "C") def adjoint(self): - return Clifford._conjugate_transpose(self, 'A') + return Clifford._conjugate_transpose(self, "A") def transpose(self): - return Clifford._conjugate_transpose(self, 'T') + return Clifford._conjugate_transpose(self, "T") def tensor(self, other): if not isinstance(other, Clifford): @@ -235,17 +235,17 @@ def expand(self, other): @classmethod def _tensor(cls, a, b): # Pad stabilizers and destabilizers - destab = (b.destabilizer.expand(a.num_qubits * 'I') + - a.destabilizer.tensor(b.num_qubits * 'I')) - stab = (b.stabilizer.expand(a.num_qubits * 'I') + - a.stabilizer.tensor(b.num_qubits * 'I')) + destab = b.destabilizer.expand(a.num_qubits * "I") + a.destabilizer.tensor( + b.num_qubits * "I" + ) + stab = b.stabilizer.expand(a.num_qubits * "I") + a.stabilizer.tensor(b.num_qubits * "I") # Add the padded table return Clifford(destab + stab, validate=False) def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) # If other is a QuantumCircuit we can more efficiently compose # using the _append_circuit method to update each gate recursively # to the current Clifford, rather than converting to a Clifford first @@ -331,14 +331,14 @@ def to_dict(self): """Return dictionary representation of Clifford object.""" return { "stabilizer": self.stabilizer.to_labels(), - "destabilizer": self.destabilizer.to_labels() + "destabilizer": self.destabilizer.to_labels(), } @staticmethod def from_dict(obj): """Load a Clifford from a dictionary""" - destabilizer = StabilizerTable.from_labels(obj.get('destabilizer')) - stabilizer = StabilizerTable.from_labels(obj.get('stabilizer')) + destabilizer = StabilizerTable.from_labels(obj.get("destabilizer")) + stabilizer = StabilizerTable.from_labels(obj.get("stabilizer")) return Clifford(destabilizer + stabilizer) def to_matrix(self): @@ -441,11 +441,15 @@ def from_label(label): """ # Check label is valid label_gates = { - 'I': IGate(), 'X': XGate(), 'Y': YGate(), - 'Z': ZGate(), 'H': HGate(), 'S': SGate() + "I": IGate(), + "X": XGate(), + "Y": YGate(), + "Z": ZGate(), + "H": HGate(), + "S": SGate(), } - if re.match(r'^[IXYZHS\-+]+$', label) is None: - raise QiskitError('Label contains invalid characters.') + if re.match(r"^[IXYZHS\-+]+$", label) is None: + raise QiskitError("Label contains invalid characters.") # Initialize an identity matrix and apply each gate num_qubits = len(label) op = Clifford(np.eye(2 * num_qubits, dtype=bool)) @@ -486,7 +490,7 @@ def _conjugate_transpose(clifford, method): Clifford: the modified clifford. """ ret = clifford.copy() - if method in ['A', 'T']: + if method in ["A", "T"]: # Apply inverse # Update table tmp = ret.destabilizer.X.copy() @@ -496,10 +500,9 @@ def _conjugate_transpose(clifford, method): ret.stabilizer.Z = tmp.T # Update phase ret.table.phase ^= clifford.dot(ret).table.phase - if method in ['C', 'T']: + if method in ["C", "T"]: # Apply conjugate - ret.table.phase ^= np.mod(np.sum( - ret.table.X & ret.table.Z, axis=1), 2).astype(bool) + ret.table.phase ^= np.mod(np.sum(ret.table.X & ret.table.Z, axis=1), 2).astype(bool) return ret def _pad_with_identity(self, clifford, qargs): @@ -507,8 +510,7 @@ def _pad_with_identity(self, clifford, qargs): if qargs is None: return clifford - padded = Clifford(StabilizerTable( - np.eye(2 * self.num_qubits, dtype=bool)), validate=False) + padded = Clifford(StabilizerTable(np.eye(2 * self.num_qubits, dtype=bool)), validate=False) inds = list(qargs) + [self.num_qubits + i for i in qargs] diff --git a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py index ed5d1d38f0f1..9fa3a1d432e6 100644 --- a/qiskit/quantum_info/operators/symplectic/clifford_circuits.py +++ b/qiskit/quantum_info/operators/symplectic/clifford_circuits.py @@ -45,17 +45,23 @@ def _append_circuit(clifford, circuit, qargs=None): # Basis Clifford Gates basis_1q = { - 'i': _append_i, 'id': _append_i, 'iden': _append_i, - 'x': _append_x, 'y': _append_y, 'z': _append_z, 'h': _append_h, - 's': _append_s, 'sdg': _append_sdg, 'sinv': _append_sdg, - 'v': _append_v, 'w': _append_w - } - basis_2q = { - 'cx': _append_cx, 'cz': _append_cz, 'swap': _append_swap + "i": _append_i, + "id": _append_i, + "iden": _append_i, + "x": _append_x, + "y": _append_y, + "z": _append_z, + "h": _append_h, + "s": _append_s, + "sdg": _append_sdg, + "sinv": _append_sdg, + "v": _append_v, + "w": _append_w, } + basis_2q = {"cx": _append_cx, "cz": _append_cz, "swap": _append_swap} # Non-clifford gates - non_clifford = ['t', 'tdg', 'ccx', 'ccz'] + non_clifford = ["t", "tdg", "ccx", "ccz"] if isinstance(gate, str): # Check if gate is a valid Clifford basis gate string @@ -68,8 +74,7 @@ def _append_circuit(clifford, circuit, qargs=None): # Apply gate if it is a Clifford basis gate if name in non_clifford: - raise QiskitError( - "Cannot update Clifford with non-Clifford gate {}".format(name)) + raise QiskitError("Cannot update Clifford with non-Clifford gate {}".format(name)) if name in basis_1q: if len(qargs) != 1: raise QiskitError("Invalid qubits for 1-qubit gate.") @@ -84,16 +89,19 @@ def _append_circuit(clifford, circuit, qargs=None): # TODO: We could also check u3 params to see if they # are a single qubit Clifford gate rather than raise an exception. if gate.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format(gate.name)) + raise QiskitError("Cannot apply Instruction: {}".format(gate.name)) if not isinstance(gate.definition, QuantumCircuit): - raise QiskitError('{} instruction definition is {}; expected QuantumCircuit'.format( - gate.name, type(gate.definition))) + raise QiskitError( + "{} instruction definition is {}; expected QuantumCircuit".format( + gate.name, type(gate.definition) + ) + ) qubit_indices = {bit: idx for idx, bit in enumerate(gate.definition.qubits)} for instr, qregs, cregs in gate.definition: if cregs: raise QiskitError( - 'Cannot apply Instruction with classical registers: {}'.format( - instr.name)) + "Cannot apply Instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register new_qubits = [qargs[qubit_indices[tup]] for tup in qregs] _append_circuit(clifford, instr, new_qubits) @@ -104,6 +112,7 @@ def _append_circuit(clifford, circuit, qargs=None): # Helper functions for applying basis gates # --------------------------------------------------------------------- + def _append_i(clifford, qubit): """Apply an I gate to a Clifford. diff --git a/qiskit/quantum_info/operators/symplectic/pauli.py b/qiskit/quantum_info/operators/symplectic/pauli.py index 8541394cac2e..022dcd942b8d 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli.py +++ b/qiskit/quantum_info/operators/symplectic/pauli.py @@ -170,7 +170,8 @@ def __init__(self, data=None, x=None, *, z=None, label=None): if len(data) not in [2, 3]: raise QiskitError( "Invalid input tuple for Pauli, input tuple must be" - " `(z, x, phase)` or `(z, x)`") + " `(z, x, phase)` or `(z, x)`" + ) base_z, base_x, base_phase = self._from_array(*data) elif isinstance(data, str): base_z, base_x, base_phase = self._from_label(data) @@ -200,8 +201,8 @@ def __repr__(self): def __str__(self): """Print representation.""" if self.__truncate__ and self.num_qubits > self.__truncate__: - front = self[-self.__truncate__:].to_label() - return front + '...' + front = self[-self.__truncate__ :].to_label() + return front + "..." return self.to_label() def __array__(self, dtype=None): @@ -226,10 +227,12 @@ def __eq__(self, other): """Test if two Paulis are equal.""" if not isinstance(other, Pauli): return False - return (len(self) == len(other) - and np.all(np.mod(self._phase, 4) == np.mod(other._phase, 4)) - and np.all(self._z == other._z) - and np.all(self._x == other._x)) + return ( + len(self) == len(other) + and np.all(np.mod(self._phase, 4) == np.mod(other._phase, 4)) + and np.all(self._z == other._z) + and np.all(self._x == other._x) + ) def equiv(self, other): """Return True if Pauli's are equivalent up to group phase. @@ -321,7 +324,8 @@ def delete(self, qubits): if max(qubits) > self.num_qubits - 1: raise QiskitError( "Qubit index is larger than the number of qubits " - "({}>{}).".format(max(qubits), self.num_qubits - 1)) + "({}>{}).".format(max(qubits), self.num_qubits - 1) + ) if len(qubits) == self.num_qubits: raise QiskitError("Cannot delete all qubits of Pauli") z = np.delete(self._z, qubits, axis=1) @@ -346,8 +350,7 @@ def insert(self, qubits, value): # Initialize empty operator ret_qubits = self.num_qubits + value.num_qubits - ret = Pauli((np.zeros(ret_qubits, dtype=bool), - np.zeros(ret_qubits, dtype=bool))) + ret = Pauli((np.zeros(ret_qubits, dtype=bool), np.zeros(ret_qubits, dtype=bool))) if isinstance(qubits, (int, np.integer)): if value.num_qubits == 1: qubits = [qubits] @@ -356,12 +359,13 @@ def insert(self, qubits, value): if len(qubits) != value.num_qubits: raise QiskitError( "Number of indices does not match number of qubits for " - "the inserted Pauli ({}!={})".format(len(qubits), - value.num_qubits)) + "the inserted Pauli ({}!={})".format(len(qubits), value.num_qubits) + ) if max(qubits) > ret.num_qubits - 1: raise QiskitError( "Index is too larger for combined Pauli number of qubits " - "({}>{}).".format(max(qubits), ret.num_qubits - 1)) + "({}>{}).".format(max(qubits), ret.num_qubits - 1) + ) # Qubit positions for original op self_qubits = [i for i in range(ret.num_qubits) if i not in qubits] ret[self_qubits] = self @@ -404,14 +408,12 @@ def to_matrix(self, sparse=False): def to_instruction(self): """Convert to Pauli circuit instruction.""" from math import pi - pauli, phase = self._to_label(self.z, - self.x, - self._phase[0], - full_group=False, - return_phase=True) + + pauli, phase = self._to_label( + self.z, self.x, self._phase[0], full_group=False, return_phase=True + ) if len(pauli) == 1: - gate = {'I': IGate(), 'X': XGate(), - 'Y': YGate(), 'Z': ZGate()}[pauli] + gate = {"I": IGate(), "X": XGate(), "Y": YGate(), "Z": ZGate()}[pauli] else: gate = PauliGate(pauli) if not phase: @@ -455,13 +457,10 @@ def compose(self, other, qargs=None, front=False, inplace=False): ``A.dot(B) == A.compose(B, front=True)``. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, Pauli): other = Pauli(other) - return Pauli(super().compose(other, - qargs=qargs, - front=front, - inplace=inplace)) + return Pauli(super().compose(other, qargs=qargs, front=front, inplace=inplace)) # pylint: disable=arguments-differ def dot(self, other, qargs=None, inplace=False): @@ -519,7 +518,7 @@ def commutes(self, other, qargs=None): bool: True if Pauli's commute, False if they anti-commute. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, BasePauli): other = Pauli(other) ret = super().commutes(other, qargs=qargs) @@ -558,7 +557,7 @@ def evolve(self, other, qargs=None): from qiskit.quantum_info.operators.symplectic.clifford import Clifford if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) # Convert Clifford to quantum circuits if isinstance(other, Clifford): @@ -588,14 +587,14 @@ def _from_label(label): QiskitError: if Pauli string is not valid. """ # Split string into coefficient and Pauli - span = re.search(r'[IXYZ]+', label).span() + span = re.search(r"[IXYZ]+", label).span() pauli, coeff = _split_pauli_label(label) - coeff = label[:span[0]] + coeff = label[: span[0]] # Convert coefficient to phase phase = 0 if not coeff else _phase_from_label(coeff) if phase is None: - raise QiskitError('Pauli string is not valid.') + raise QiskitError("Pauli string is not valid.") # Convert to Symplectic representation num_qubits = len(pauli) @@ -603,11 +602,11 @@ def _from_label(label): base_x = np.zeros((1, num_qubits), dtype=bool) base_phase = np.array([phase], dtype=int) for i, char in enumerate(pauli): - if char == 'X': + if char == "X": base_x[0, num_qubits - 1 - i] = True - elif char == 'Z': + elif char == "Z": base_z[0, num_qubits - 1 - i] = True - elif char == 'Y': + elif char == "Y": base_x[0, num_qubits - 1 - i] = True base_z[0, num_qubits - 1 - i] = True base_phase += 1 @@ -617,12 +616,12 @@ def _from_label(label): def _from_scalar_op(cls, op): """Convert a ScalarOp to BasePauli data.""" if op.num_qubits is None: - raise QiskitError('{} is not an N-qubit identity'.format(op)) + raise QiskitError("{} is not an N-qubit identity".format(op)) base_z = np.zeros((1, op.num_qubits), dtype=bool) base_x = np.zeros((1, op.num_qubits), dtype=bool) base_phase = np.mod( - cls._phase_from_complex(op.coeff) + - np.sum(np.logical_and(base_z, base_x), axis=1), 4) + cls._phase_from_complex(op.coeff) + np.sum(np.logical_and(base_z, base_x), axis=1), 4 + ) return base_z, base_x, base_phase @classmethod @@ -650,28 +649,29 @@ def _from_circuit(cls, instr): if isinstance(instr, Instruction): # Convert other instructions to circuit definition if instr.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format( - instr.name)) + raise QiskitError("Cannot apply Instruction: {}".format(instr.name)) # Convert to circuit instr = instr.definition # Initialize identity Pauli ret = Pauli( - BasePauli(np.zeros((1, instr.num_qubits), dtype=bool), - np.zeros((1, instr.num_qubits), dtype=bool), - np.zeros(1, dtype=int))) + BasePauli( + np.zeros((1, instr.num_qubits), dtype=bool), + np.zeros((1, instr.num_qubits), dtype=bool), + np.zeros(1, dtype=int), + ) + ) # Add circuit global phase if specified if instr.global_phase: - ret.phase = cls._phase_from_complex( - np.exp(1j * float(instr.global_phase))) + ret.phase = cls._phase_from_complex(np.exp(1j * float(instr.global_phase))) # Recursively apply instructions for dinstr, qregs, cregs in instr.data: if cregs: raise QiskitError( - 'Cannot apply instruction with classical registers: {}'. - format(dinstr.name)) + "Cannot apply instruction with classical registers: {}".format(dinstr.name) + ) if not isinstance(dinstr, Barrier): next_instr = BasePauli(*cls._from_circuit(dinstr)) if next_instr is not None: @@ -685,9 +685,10 @@ def _from_circuit(cls, instr): @classmethod @deprecate_function( - 'Initializing Pauli from `Pauli(label=l)` kwarg is deprecated as of ' - 'version 0.17.0 and will be removed no earlier than 3 months after ' - 'the release date. Use `Pauli(l)` instead.') + "Initializing Pauli from `Pauli(label=l)` kwarg is deprecated as of " + "version 0.17.0 and will be removed no earlier than 3 months after " + "the release date. Use `Pauli(l)` instead." + ) def _from_label_deprecated(cls, label): # Deprecated wrapper of `_from_label` so that a deprecation warning # can be displaced during initialization with deprecated kwarg @@ -695,9 +696,10 @@ def _from_label_deprecated(cls, label): @classmethod @deprecate_function( - 'Initializing Pauli from `Pauli(z=z, x=x)` kwargs is deprecated as of ' - 'version 0.17.0 and will be removed no earlier than 3 months after ' - 'the release date. Use tuple initialization `Pauli((z, x))` instead.') + "Initializing Pauli from `Pauli(z=z, x=x)` kwargs is deprecated as of " + "version 0.17.0 and will be removed no earlier than 3 months after " + "the release date. Use tuple initialization `Pauli((z, x))` instead." + ) def _from_array_deprecated(cls, z, x): # Deprecated wrapper of `_from_array` so that a deprecation warning # can be displaced during initialization with deprecated kwarg @@ -712,8 +714,9 @@ def _make_np_bool(arr): @staticmethod @deprecate_function( - '`from_label` is deprecated and will be removed no earlier than ' - '3 months after the release date. Use Pauli(label) instead.') + "`from_label` is deprecated and will be removed no earlier than " + "3 months after the release date. Use Pauli(label) instead." + ) def from_label(label): """DEPRECATED: Construct a Pauli from a string label. @@ -731,13 +734,14 @@ def from_label(label): """ if isinstance(label, tuple): # Legacy usage from aqua - label = ''.join(label) + label = "".join(label) return Pauli(label) @staticmethod @deprecate_function( - 'sgn_prod is deprecated and will be removed no earlier than ' - '3 months after the release date. Use `dot` instead.') + "sgn_prod is deprecated and will be removed no earlier than " + "3 months after the release date. Use `dot` instead." + ) def sgn_prod(p1, p2): r""" DEPRECATED: Multiply two Paulis and track the phase. @@ -756,11 +760,11 @@ def sgn_prod(p1, p2): complex: the sign of the multiplication, 1, -1, 1j or -1j """ pauli = p1.dot(p2) - return pauli[:], (-1j)**pauli.phase + return pauli[:], (-1j) ** pauli.phase @deprecate_function( - '`to_spmatrix` is deprecated and will be removed no earlier than ' - '3 months after the release date. Use `to_matrix(sparse=True)` instead.' + "`to_spmatrix` is deprecated and will be removed no earlier than " + "3 months after the release date. Use `to_matrix(sparse=True)` instead." ) def to_spmatrix(self): r""" @@ -776,10 +780,11 @@ def to_spmatrix(self): return self.to_matrix(sparse=True) @deprecate_function( - '`kron` is deprecated and will be removed no earlier than ' - '3 months after the release date of Qiskit Terra 0.17.0. ' - 'Use `expand` instead, but note this does not change ' - 'the operator in-place.') + "`kron` is deprecated and will be removed no earlier than " + "3 months after the release date of Qiskit Terra 0.17.0. " + "Use `expand` instead, but note this does not change " + "the operator in-place." + ) def kron(self, other): r"""DEPRECATED: Kronecker product of two paulis. @@ -801,9 +806,10 @@ def kron(self, other): return self @deprecate_function( - '`update_z` is deprecated and will be removed no earlier than ' - '3 months after the release date. Use `Pauli.z = val` or ' - '`Pauli.z[indices] = val` instead.') + "`update_z` is deprecated and will be removed no earlier than " + "3 months after the release date. Use `Pauli.z = val` or " + "`Pauli.z[indices] = val` instead." + ) def update_z(self, z, indices=None): """ DEPRECATED: Update partial or entire z. @@ -824,12 +830,12 @@ def update_z(self, z, indices=None): z = self._make_np_bool(z) if indices is None: if len(self.z) != len(z): - raise QiskitError("During updating whole z, you can not " - "change the number of qubits.") + raise QiskitError( + "During updating whole z, you can not " "change the number of qubits." + ) self.z = z else: - if not isinstance(indices, list) and not isinstance( - indices, np.ndarray): + if not isinstance(indices, list) and not isinstance(indices, np.ndarray): indices = [indices] for p, idx in enumerate(indices): self.z[idx] = z[p] @@ -837,9 +843,10 @@ def update_z(self, z, indices=None): return self @deprecate_function( - '`update_z` is deprecated and will be removed no earlier than ' - '3 months after the release date. Use `Pauli.x = val` or ' - '`Pauli.x[indices] = val` instead.') + "`update_z` is deprecated and will be removed no earlier than " + "3 months after the release date. Use `Pauli.x = val` or " + "`Pauli.x[indices] = val` instead." + ) def update_x(self, x, indices=None): """ DEPRECATED: Update partial or entire x. @@ -861,12 +868,11 @@ def update_x(self, x, indices=None): if indices is None: if len(self.x) != len(x): raise QiskitError( - "During updating whole x, you can not change " - "the number of qubits.") + "During updating whole x, you can not change " "the number of qubits." + ) self.x = x else: - if not isinstance(indices, list) and not isinstance( - indices, np.ndarray): + if not isinstance(indices, list) and not isinstance(indices, np.ndarray): indices = [indices] for p, idx in enumerate(indices): self.x[idx] = x[p] @@ -874,9 +880,10 @@ def update_x(self, x, indices=None): return self @deprecate_function( - '`insert_paulis` is deprecated and will be removed no earlier than ' - '3 months after the release date. For similar functionality use ' - '`Pauli.insert` instead.') + "`insert_paulis` is deprecated and will be removed no earlier than " + "3 months after the release date. For similar functionality use " + "`Pauli.insert` instead." + ) def insert_paulis(self, indices=None, paulis=None, pauli_labels=None): """ DEPRECATED: Insert or append pauli to the targeted indices. @@ -905,12 +912,11 @@ def insert_paulis(self, indices=None, paulis=None, pauli_labels=None): """ if pauli_labels is not None: if paulis is not None: - raise QiskitError( - "Please only provide either `paulis` or `pauli_labels`") + raise QiskitError("Please only provide either `paulis` or `pauli_labels`") if isinstance(pauli_labels, str): pauli_labels = list(pauli_labels) # since pauli label is in reversed order. - label = ''.join(pauli_labels[::-1]) + label = "".join(pauli_labels[::-1]) paulis = self.from_label(label) # Insert and update self @@ -930,8 +936,9 @@ def insert_paulis(self, indices=None, paulis=None, pauli_labels=None): return self @deprecate_function( - '`append_paulis` is deprecated and will be removed no earlier than ' - '3 months after the release date. Use `Pauli.expand` instead.') + "`append_paulis` is deprecated and will be removed no earlier than " + "3 months after the release date. Use `Pauli.expand` instead." + ) def append_paulis(self, paulis=None, pauli_labels=None): """ DEPRECATED: Append pauli at the end. @@ -943,14 +950,13 @@ def append_paulis(self, paulis=None, pauli_labels=None): Returns: Pauli: self """ - return self.insert_paulis(None, - paulis=paulis, - pauli_labels=pauli_labels) + return self.insert_paulis(None, paulis=paulis, pauli_labels=pauli_labels) @deprecate_function( - '`append_paulis` is deprecated and will be removed no earlier than ' - '3 months after the release date. For equivalent functionality ' - 'use `Pauli.delete` instead.') + "`append_paulis` is deprecated and will be removed no earlier than " + "3 months after the release date. For equivalent functionality " + "use `Pauli.delete` instead." + ) def delete_qubits(self, indices): """ DEPRECATED: Delete pauli at the indices. @@ -973,8 +979,9 @@ def delete_qubits(self, indices): @classmethod @deprecate_function( - '`pauli_single` is deprecated and will be removed no earlier than ' - '3 months after the release date.') + "`pauli_single` is deprecated and will be removed no earlier than " + "3 months after the release date." + ) def pauli_single(cls, num_qubits, index, pauli_label): """ DEPRECATED: Generate single qubit pauli at index with pauli_label with length num_qubits. @@ -988,8 +995,7 @@ def pauli_single(cls, num_qubits, index, pauli_label): Pauli: single qubit pauli """ tmp = Pauli(pauli_label) - ret = Pauli((np.zeros(num_qubits, dtype=bool), - np.zeros(num_qubits, dtype=bool))) + ret = Pauli((np.zeros(num_qubits, dtype=bool), np.zeros(num_qubits, dtype=bool))) ret.x[index] = tmp.x[0] ret.z[index] = tmp.z[0] ret.phase = tmp.phase @@ -997,9 +1003,10 @@ def pauli_single(cls, num_qubits, index, pauli_label): @classmethod @deprecate_function( - '`random` is deprecated and will be removed no earlier than ' - '3 months after the release date. ' - 'Use `qiskit.quantum_info.random_pauli` instead') + "`random` is deprecated and will be removed no earlier than " + "3 months after the release date. " + "Use `qiskit.quantum_info.random_pauli` instead" + ) def random(cls, num_qubits, seed=None): """DEPRECATED: Return a random Pauli on number of qubits. @@ -1014,6 +1021,7 @@ def random(cls, num_qubits, seed=None): """ # pylint: disable=cyclic-import from qiskit.quantum_info.operators.symplectic.random import random_pauli + return random_pauli(num_qubits, group_phase=False, seed=seed) @@ -1021,23 +1029,25 @@ def random(cls, num_qubits, seed=None): # Label parsing helper functions # --------------------------------------------------------------------- + def _split_pauli_label(label): """Split Pauli label into unsigned group label and coefficient label""" - span = re.search(r'[IXYZ]+', label).span() - pauli = label[span[0]:] - coeff = label[:span[0]] + span = re.search(r"[IXYZ]+", label).span() + pauli = label[span[0] :] + coeff = label[: span[0]] if span[1] != len(label): - invalid = set(re.sub(r'[IXYZ]+', '', label[span[0]:])) - raise QiskitError("Pauli string contains invalid characters " - "{} ∉ ['I', 'X', 'Y', 'Z']".format(invalid)) + invalid = set(re.sub(r"[IXYZ]+", "", label[span[0] :])) + raise QiskitError( + "Pauli string contains invalid characters " "{} ∉ ['I', 'X', 'Y', 'Z']".format(invalid) + ) return pauli, coeff def _phase_from_label(label): """Return the phase from a label""" # Returns None if label is invalid - label = label.replace('+', '', 1).replace('1', '', 1).replace('j', 'i', 1) - phases = {'': 0, '-i': 1, '-': 2, 'i': 3} + label = label.replace("+", "", 1).replace("1", "", 1).replace("j", "i", 1) + phases = {"": 0, "-i": 1, "-": 2, "i": 3} if label not in phases: raise QiskitError("Invalid Pauli phase label '{}'".format(label)) return phases.get(label) diff --git a/qiskit/quantum_info/operators/symplectic/pauli_table.py b/qiskit/quantum_info/operators/symplectic/pauli_table.py index ce1567e1ba58..8396cec1a8c6 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli_table.py +++ b/qiskit/quantum_info/operators/symplectic/pauli_table.py @@ -150,8 +150,7 @@ def __init__(self, data): elif isinstance(data, ScalarOp): # Initialize an N-qubit identity if data.num_qubits is None: - raise QiskitError( - '{} is not an N-qubit identity'.format(data)) + raise QiskitError("{} is not an N-qubit identity".format(data)) self._array = np.zeros((1, 2 * data.num_qubits), dtype=bool) else: raise QiskitError("Invalid input data for PauliTable.") @@ -169,13 +168,12 @@ def __init__(self, data): def __repr__(self): """Display representation.""" - prefix = 'PauliTable(' - return '{}{})'.format(prefix, np.array2string( - self._array, separator=',', prefix=prefix)) + prefix = "PauliTable(" + return "{}{})".format(prefix, np.array2string(self._array, separator=",", prefix=prefix)) def __str__(self): """String representation.""" - return 'PauliTable: {}'.format(self.to_labels()) + return "PauliTable: {}".format(self.to_labels()) def __eq__(self, other): """Test if two Pauli tables are equal.""" @@ -202,20 +200,20 @@ def array(self, value): @property def X(self): """The X block of the :attr:`array`.""" - return self._array[:, 0:self.num_qubits] + return self._array[:, 0 : self.num_qubits] @X.setter def X(self, val): - self._array[:, 0:self.num_qubits] = val + self._array[:, 0 : self.num_qubits] = val @property def Z(self): """The Z block of the :attr:`array`.""" - return self._array[:, self.num_qubits:2*self.num_qubits] + return self._array[:, self.num_qubits : 2 * self.num_qubits] @Z.setter def Z(self, val): - self._array[:, self.num_qubits:2*self.num_qubits] = val + self._array[:, self.num_qubits : 2 * self.num_qubits] = val # --------------------------------------------------------------------- # Size Properties @@ -278,14 +276,18 @@ def delete(self, ind, qubit=False): # Row deletion if not qubit: if max(ind) >= self.size: - raise QiskitError("Indices {} are not all less than the size" - " of the PauliTable ({})".format(ind, self.size)) + raise QiskitError( + "Indices {} are not all less than the size" + " of the PauliTable ({})".format(ind, self.size) + ) return PauliTable(np.delete(self._array, ind, axis=0)) # Column (qubit) deletion if max(ind) >= self.num_qubits: - raise QiskitError("Indices {} are not all less than the number of" - " qubits in the PauliTable ({})".format(ind, self.num_qubits)) + raise QiskitError( + "Indices {} are not all less than the number of" + " qubits in the PauliTable ({})".format(ind, self.num_qubits) + ) cols = ind + [self.num_qubits + i for i in ind] return PauliTable(np.delete(self._array, cols, axis=1)) @@ -316,14 +318,18 @@ def insert(self, ind, value, qubit=False): # Row insertion if not qubit: if ind > self.size: - raise QiskitError("Index {} is larger than the number of rows in the" - " PauliTable ({}).".format(ind, self.num_qubits)) + raise QiskitError( + "Index {} is larger than the number of rows in the" + " PauliTable ({}).".format(ind, self.num_qubits) + ) return PauliTable(np.insert(self.array, ind, value.array, axis=0)) # Column insertion if ind > self.num_qubits: - raise QiskitError("Index {} is greater than number of qubits" - " in the PauliTable ({})".format(ind, self.num_qubits)) + raise QiskitError( + "Index {} is greater than number of qubits" + " in the PauliTable ({})".format(ind, self.num_qubits) + ) if value.size == 1: # Pad blocks to correct size value_x = np.vstack(self.size * [value.X]) @@ -334,12 +340,24 @@ def insert(self, ind, value, qubit=False): value_z = value.Z else: # Blocks are incorrect size - raise QiskitError("Input PauliTable must have a single row, or" - " the same number of rows as the Pauli Table" - " ({}).".format(self.size)) + raise QiskitError( + "Input PauliTable must have a single row, or" + " the same number of rows as the Pauli Table" + " ({}).".format(self.size) + ) # Build new array by blocks - return PauliTable(np.hstack((self.X[:, :ind], value_x, self.X[:, ind:], - self.Z[:, :ind], value_z, self.Z[:, ind:]))) + return PauliTable( + np.hstack( + ( + self.X[:, :ind], + value_x, + self.X[:, ind:], + self.Z[:, :ind], + value_z, + self.Z[:, ind:], + ) + ) + ) def argsort(self, weight=False): """Return indices for sorting the rows of the table. @@ -370,7 +388,7 @@ def argsort(self, weight=False): # are use the 'stable' sort method indices = np.arange(self.size) for i in range(self.num_qubits): - sort_inds = order[:, i].argsort(kind='stable') + sort_inds = order[:, i].argsort(kind="stable") order = order[sort_inds] indices = indices[sort_inds] if weight: @@ -379,7 +397,7 @@ def argsort(self, weight=False): # If using weights we implement a final sort by total number # of non-identity Paulis if weight: - indices = indices[weights.argsort(kind='stable')] + indices = indices[weights.argsort(kind="stable")] return indices def sort(self, weight=False): @@ -459,8 +477,7 @@ def unique(self, return_index=False, return_counts=False): original array. Only provided if ``return_counts`` is True. """ if return_counts: - _, index, counts = np.unique(self.array, return_index=True, - return_counts=True, axis=0) + _, index, counts = np.unique(self.array, return_index=True, return_counts=True, axis=0) else: _, index = np.unique(self.array, return_index=True, axis=0) # Sort the index so we return unique rows in the original array order @@ -468,11 +485,11 @@ def unique(self, return_index=False, return_counts=False): index = index[sort_inds] unique = self[index] # Concatenate return tuples - ret = (unique, ) + ret = (unique,) if return_index: - ret += (index, ) + ret += (index,) if return_counts: - ret += (counts[sort_inds], ) + ret += (counts[sort_inds],) if len(ret) == 1: return ret[0] return ret @@ -574,7 +591,7 @@ def compose(self, other, qargs=None, front=True): QiskitError: if other cannot be converted to a PauliTable. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, PauliTable): other = PauliTable(other) if qargs is None and other.num_qubits != self.num_qubits: @@ -648,36 +665,32 @@ def _add(self, other, qargs=None): PauliTable: the concatenated table self + other. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, PauliTable): other = PauliTable(other) self._op_shape._validate_add(other._op_shape, qargs) - if qargs is None or (sorted(qargs) == qargs - and len(qargs) == self.num_qubits): + if qargs is None or (sorted(qargs) == qargs and len(qargs) == self.num_qubits): return PauliTable(np.vstack((self._array, other._array))) # Pad other with identity and then add - padded = PauliTable( - np.zeros((1, 2 * self.num_qubits), dtype=bool)) + padded = PauliTable(np.zeros((1, 2 * self.num_qubits), dtype=bool)) padded = padded.compose(other, qargs=qargs) return PauliTable(np.vstack((self._array, padded._array))) def __add__(self, other): - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) return self._add(other, qargs=qargs) def conjugate(self): """Not implemented.""" - raise NotImplementedError( - "{} does not support conjugatge".format(type(self))) + raise NotImplementedError("{} does not support conjugatge".format(type(self))) def transpose(self): """Not implemented.""" - raise NotImplementedError( - "{} does not support transpose".format(type(self))) + raise NotImplementedError("{} does not support transpose".format(type(self))) # --------------------------------------------------------------------- # Utility methods @@ -750,10 +763,10 @@ def _commutes_with_all(self, other, anti=False): if not isinstance(other, PauliTable): other = PauliTable(other) comms = PauliTable._commutes(self, other[0]) - inds, = np.where(comms == int(not anti)) + (inds,) = np.where(comms == int(not anti)) for pauli in other[1:]: comms = PauliTable._commutes(self[inds], pauli) - new_inds, = np.where(comms == int(not anti)) + (new_inds,) = np.where(comms == int(not anti)) if new_inds.size == 0: # No commuting rows return new_inds @@ -777,8 +790,8 @@ def _commutes(pauli_table, pauli): # Multiply array by Pauli, and set entries where inputs # where I to I tmp = PauliTable(pauli_table.array ^ pauli.array) - tmp.X = (tmp.X & non_iden) - tmp.Z = (tmp.Z & non_iden) + tmp.X = tmp.X & non_iden + tmp.Z = tmp.Z & non_iden # Find total number of non I Pauli's remaining in table # if there are an even number the row commutes with the # input Pauli, otherwise it anti-commutes @@ -789,12 +802,11 @@ def _block_stack(array1, array2): """Stack two arrays along their first axis.""" sz1 = len(array1) sz2 = len(array2) - out_shape1 = (sz1 * sz2, ) + array1.shape[1:] - out_shape2 = (sz1 * sz2, ) + array2.shape[1:] + out_shape1 = (sz1 * sz2,) + array1.shape[1:] + out_shape2 = (sz1 * sz2,) + array2.shape[1:] if sz2 > 1: # Stack blocks for output table - ret1 = np.reshape(np.stack(sz2 * [array1], axis=1), - out_shape1) + ret1 = np.reshape(np.stack(sz2 * [array1], axis=1), out_shape1) else: ret1 = array1 if sz1 > 1: @@ -866,7 +878,7 @@ def to_labels(self, array=False): Returns: list or array: The rows of the PauliTable in label form. """ - ret = np.zeros(self.size, dtype='> 1) & 0x55555555) i = (i & 0x33333333) + ((i >> 2) & 0x33333333) - return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xffffffff) >> 24 + return (((i + (i >> 4) & 0xF0F0F0F) * 0x1010101) & 0xFFFFFFFF) >> 24 symp = np.asarray(pauli, dtype=bool) num_qubits = symp.size // 2 x = symp[0:num_qubits] - z = symp[num_qubits:2*num_qubits] + z = symp[num_qubits : 2 * num_qubits] dim = 2 ** num_qubits twos_array = 1 << np.arange(num_qubits) @@ -1009,13 +1023,13 @@ def count1(i): if sparse: # Return sparse matrix from scipy.sparse import csr_matrix - return csr_matrix((data, indices, indptr), shape=(dim, dim), - dtype=dtype) + + return csr_matrix((data, indices, indptr), shape=(dim, dim), dtype=dtype) # Build dense matrix using csr format mat = np.zeros((dim, dim), dtype=dtype) for i in range(dim): - mat[i][indices[indptr[i]:indptr[i+1]]] = data[indptr[i]:indptr[i+1]] + mat[i][indices[indptr[i] : indptr[i + 1]]] = data[indptr[i] : indptr[i + 1]] return mat # --------------------------------------------------------------------- @@ -1032,13 +1046,16 @@ def label_iter(self): Returns: LabelIterator: label iterator object for the PauliTable. """ + class LabelIterator(CustomIterator): """Label representation iteration and item access.""" + def __repr__(self): return "".format(hex(id(self))) def __getitem__(self, key): return self.obj._to_label(self.obj.array[key]) + return LabelIterator(self) def matrix_iter(self, sparse=False): @@ -1056,13 +1073,16 @@ def matrix_iter(self, sparse=False): Returns: MatrixIterator: matrix iterator object for the PauliTable. """ + class MatrixIterator(CustomIterator): """Matrix representation iteration and item access.""" + def __repr__(self): return "".format(hex(id(self))) def __getitem__(self, key): return self.obj._to_matrix(self.obj.array[key], sparse=sparse) + return MatrixIterator(self) diff --git a/qiskit/quantum_info/operators/symplectic/pauli_utils.py b/qiskit/quantum_info/operators/symplectic/pauli_utils.py index 96ad71fda268..8b2c66f3f815 100644 --- a/qiskit/quantum_info/operators/symplectic/pauli_utils.py +++ b/qiskit/quantum_info/operators/symplectic/pauli_utils.py @@ -28,11 +28,9 @@ def pauli_basis(num_qubits, weight=False): Returns: PauliTable: the PauliTable for the basis """ - pauli_1q = PauliTable(np.array([[False, False], - [True, False], - [True, True], - [False, True]], - dtype=bool)) + pauli_1q = PauliTable( + np.array([[False, False], [True, False], [True, True], [False, True]], dtype=bool) + ) if num_qubits == 1: return pauli_1q pauli = pauli_1q diff --git a/qiskit/quantum_info/operators/symplectic/random.py b/qiskit/quantum_info/operators/symplectic/random.py index 0ce482834685..7f0926dfd6f7 100644 --- a/qiskit/quantum_info/operators/symplectic/random.py +++ b/qiskit/quantum_info/operators/symplectic/random.py @@ -239,15 +239,19 @@ def _inverse_tril(mat, block_inverse_threshold): if dim <= 5: inv = mat.copy() - inv[2, 0] = (mat[2, 0] ^ (mat[1, 0] & mat[2, 1])) + inv[2, 0] = mat[2, 0] ^ (mat[1, 0] & mat[2, 1]) if dim > 3: - inv[3, 1] = (mat[3, 1] ^ (mat[2, 1] & mat[3, 2])) + inv[3, 1] = mat[3, 1] ^ (mat[2, 1] & mat[3, 2]) inv[3, 0] = mat[3, 0] ^ (mat[3, 2] & mat[2, 0]) ^ (mat[1, 0] & inv[3, 1]) if dim > 4: inv[4, 2] = (mat[4, 2] ^ (mat[3, 2] & mat[4, 3])) & 1 inv[4, 1] = mat[4, 1] ^ (mat[4, 3] & mat[3, 1]) ^ (mat[2, 1] & inv[4, 2]) - inv[4, 0] = mat[4, 0] ^ (mat[1, 0] & inv[4, 1]) ^ ( - mat[2, 0] & inv[4, 2]) ^ (mat[3, 0] & mat[4, 3]) + inv[4, 0] = ( + mat[4, 0] + ^ (mat[1, 0] & inv[4, 1]) + ^ (mat[2, 0] & inv[4, 2]) + ^ (mat[3, 0] & mat[4, 3]) + ) return inv % 2 # For higher dimensions we use Numpy's inverse function diff --git a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py index de338b49d52c..a3090826a004 100644 --- a/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py +++ b/qiskit/quantum_info/operators/symplectic/sparse_pauli_op.py @@ -64,10 +64,11 @@ def __init__(self, data, coeffs=None): # Initialize Coeffs self._coeffs = np.asarray(coeffs, dtype=complex) - if self._coeffs.shape != (self._table.size, ): - raise QiskitError("coeff vector is incorrect shape for number" - " of Paulis {} != {}".format(self._coeffs.shape, - self._table.size)) + if self._coeffs.shape != (self._table.size,): + raise QiskitError( + "coeff vector is incorrect shape for number" + " of Paulis {} != {}".format(self._coeffs.shape, self._table.size) + ) # Initialize LinearOp super().__init__(num_qubits=self._table.num_qubits) @@ -77,18 +78,22 @@ def __array__(self, dtype=None): return self.to_matrix() def __repr__(self): - prefix = 'SparsePauliOp(' - pad = len(prefix) * ' ' - return '{}{},\n{}coeffs={})'.format( - prefix, np.array2string( - self.table.array, separator=', ', prefix=prefix), - pad, np.array2string(self.coeffs, separator=', ')) + prefix = "SparsePauliOp(" + pad = len(prefix) * " " + return "{}{},\n{}coeffs={})".format( + prefix, + np.array2string(self.table.array, separator=", ", prefix=prefix), + pad, + np.array2string(self.coeffs, separator=", "), + ) def __eq__(self, other): """Check if two SparsePauliOp operators are equal""" - return (super().__eq__(other) - and np.allclose(self.coeffs, other.coeffs) - and self.table == other.table) + return ( + super().__eq__(other) + and np.allclose(self.coeffs, other.coeffs) + and self.table == other.table + ) # --------------------------------------------------------------------- # Data accessors @@ -171,7 +176,7 @@ def adjoint(self): def compose(self, other, qargs=None, front=False): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, SparsePauliOp): other = SparsePauliOp(other) @@ -227,7 +232,7 @@ def _tensor(cls, a, b): def _add(self, other, qargs=None): if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, SparsePauliOp): other = SparsePauliOp(other) @@ -276,8 +281,12 @@ def is_unitary(self, atol=None, rtol=None): # Compose with adjoint val = self.compose(self.adjoint()).simplify() # See if the result is an identity - return (val.size == 1 and np.isclose(val.coeffs[0], 1.0, atol=atol, rtol=rtol) - and not np.any(val.table.X) and not np.any(val.table.Z)) + return ( + val.size == 1 + and np.isclose(val.coeffs[0], 1.0, atol=atol, rtol=rtol) + and not np.any(val.table.X) + and not np.any(val.table.Z) + ) def simplify(self, atol=None, rtol=None): """Simplify PauliTable by combining duplicates and removing zeros. @@ -297,21 +306,21 @@ def simplify(self, atol=None, rtol=None): if rtol is None: rtol = self.rtol - table, indexes = np.unique(self.table.array, - return_inverse=True, axis=0) + table, indexes = np.unique(self.table.array, return_inverse=True, axis=0) coeffs = np.zeros(len(table), dtype=complex) for i, val in zip(indexes, self.coeffs): coeffs[i] += val # Delete zero coefficient rows # TODO: Add atol/rtol for zero comparison - non_zero = [i for i in range(coeffs.size) - if not np.isclose(coeffs[i], 0, atol=atol, rtol=rtol)] + non_zero = [ + i for i in range(coeffs.size) if not np.isclose(coeffs[i], 0, atol=atol, rtol=rtol) + ] table = table[non_zero] coeffs = coeffs[non_zero] # Check edge case that we deleted all Paulis # In this case we return an identity Pauli with a zero coefficient if coeffs.size == 0: - table = np.zeros((1, 2*self.num_qubits), dtype=bool) + table = np.zeros((1, 2 * self.num_qubits), dtype=bool) coeffs = np.array([0j]) return SparsePauliOp(table, coeffs) @@ -378,7 +387,7 @@ def from_list(obj): num_qubits = len(PauliTable._from_label(obj[0][0])) size = len(obj) coeffs = np.zeros(size, dtype=complex) - labels = np.zeros(size, dtype='".format( - hex(id(self))) + return "".format(hex(id(self))) def __getitem__(self, key): coeff = self.obj.coeffs[key] @@ -475,15 +483,16 @@ def matrix_iter(self, sparse=False): Returns: MatrixIterator: matrix iterator object for the PauliTable. """ + class MatrixIterator(CustomIterator): """Matrix representation iteration and item access.""" + def __repr__(self): return "".format(hex(id(self))) def __getitem__(self, key): coeff = self.obj.coeffs[key] - mat = PauliTable._to_matrix(self.obj.table.array[key], - sparse=sparse) + mat = PauliTable._to_matrix(self.obj.table.array[key], sparse=sparse) return coeff * mat return MatrixIterator(self) diff --git a/qiskit/quantum_info/operators/symplectic/stabilizer_table.py b/qiskit/quantum_info/operators/symplectic/stabilizer_table.py index 79f60a10caf6..7a13d733a430 100644 --- a/qiskit/quantum_info/operators/symplectic/stabilizer_table.py +++ b/qiskit/quantum_info/operators/symplectic/stabilizer_table.py @@ -203,16 +203,15 @@ def __init__(self, data, phase=None): self._phase = np.ones(self.size, dtype=bool) else: self._phase = np.asarray(phase, dtype=bool) - if self._phase.shape != (self.size, ): + if self._phase.shape != (self.size,): raise QiskitError("Phase vector is incorrect shape.") def __repr__(self): - return 'StabilizerTable(\n{},\nphase={})'.format( - repr(self._array), repr(self._phase)) + return "StabilizerTable(\n{},\nphase={})".format(repr(self._array), repr(self._phase)) def __str__(self): """String representation""" - return 'StabilizerTable: {}'.format(self.to_labels()) + return "StabilizerTable: {}".format(self.to_labels()) def __eq__(self, other): """Test if two StabilizerTables are equal""" @@ -222,8 +221,7 @@ def __eq__(self, other): def copy(self): """Return a copy of the StabilizerTable.""" - return StabilizerTable(self._array.copy(), - self._phase.copy()) + return StabilizerTable(self._array.copy(), self._phase.copy()) # --------------------------------------------------------------------- # PauliTable and phase access @@ -295,10 +293,13 @@ def delete(self, ind, qubit=False): if isinstance(ind, (int, np.integer)): ind = [ind] if max(ind) >= self.size: - raise QiskitError("Indices {} are not all less than the size" - " of the SatbilizerTable ({})".format(ind, self.size)) - return StabilizerTable(np.delete(self._array, ind, axis=0), - np.delete(self._phase, ind, axis=0)) + raise QiskitError( + "Indices {} are not all less than the size" + " of the SatbilizerTable ({})".format(ind, self.size) + ) + return StabilizerTable( + np.delete(self._array, ind, axis=0), np.delete(self._phase, ind, axis=0) + ) def insert(self, ind, value, qubit=False): """Insert stabilizers's into the table. @@ -435,11 +436,9 @@ def unique(self, return_index=False, return_counts=False): original array. Only provided if ``return_counts`` is True. """ # Combine array and phases into single array for sorting - stack = np.hstack([self._array, - self._phase.reshape((self.size, 1))]) + stack = np.hstack([self._array, self._phase.reshape((self.size, 1))]) if return_counts: - _, index, counts = np.unique(stack, return_index=True, - return_counts=True, axis=0) + _, index, counts = np.unique(stack, return_index=True, return_counts=True, axis=0) else: _, index = np.unique(stack, return_index=True, axis=0) # Sort the index so we return unique rows in the original array order @@ -447,11 +446,11 @@ def unique(self, return_index=False, return_counts=False): index = index[sort_inds] unique = self[index] # Concatenate return tuples - ret = (unique, ) + ret = (unique,) if return_index: - ret += (index, ) + ret += (index,) if return_counts: - ret += (counts[sort_inds], ) + ret += (counts[sort_inds],) if len(ret) == 1: return ret[0] return ret @@ -572,7 +571,7 @@ def compose(self, other, qargs=None, front=False): QiskitError: if other cannot be converted to a StabilizerTable. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, StabilizerTable): other = StabilizerTable(other) if qargs is None and other.num_qubits != self.num_qubits: @@ -671,26 +670,25 @@ def _add(self, other, qargs=None): StabilizerTable: the concatenated table self + other. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) if not isinstance(other, StabilizerTable): other = StabilizerTable(other) self._op_shape._validate_add(other._op_shape, qargs) - if qargs is None or (sorted(qargs) == qargs - and len(qargs) == self.num_qubits): - return StabilizerTable(np.vstack((self._array, other._array)), - np.hstack((self._phase, other._phase))) + if qargs is None or (sorted(qargs) == qargs and len(qargs) == self.num_qubits): + return StabilizerTable( + np.vstack((self._array, other._array)), np.hstack((self._phase, other._phase)) + ) # Pad other with identity and then add - padded = StabilizerTable( - np.zeros((1, 2 * self.num_qubits), dtype=bool)) + padded = StabilizerTable(np.zeros((1, 2 * self.num_qubits), dtype=bool)) padded = padded.compose(other, qargs=qargs) return StabilizerTable( - np.vstack((self._array, padded._array)), - np.hstack((self._phase, padded._phase))) + np.vstack((self._array, padded._array)), np.hstack((self._phase, padded._phase)) + ) def _multiply(self, other): """Multiply (XOR) phase vector of the StabilizerTable. @@ -710,8 +708,7 @@ def _multiply(self, other): """ # Numeric (integer) value case if not isinstance(other, bool) and other not in [1, -1]: - raise QiskitError( - "Can only multiply a Stabilizer value by +1 or -1 phase.") + raise QiskitError("Can only multiply a Stabilizer value by +1 or -1 phase.") # We have to be careful we don't cast True <-> +1 when # we store -1 phase as boolen True value @@ -869,7 +866,7 @@ def to_labels(self, array=False): Returns: list or array: The rows of the StabilizerTable in label form. """ - ret = np.zeros(self.size, dtype='".format(hex(id(self))) def __getitem__(self, key): - return self.obj._to_label(self.obj.array[key], - self.obj.phase[key]) + return self.obj._to_label(self.obj.array[key], self.obj.phase[key]) + return LabelIterator(self) def matrix_iter(self, sparse=False): @@ -1037,15 +1038,16 @@ def matrix_iter(self, sparse=False): Returns: MatrixIterator: matrix iterator object for the StabilizerTable. """ + class MatrixIterator(CustomIterator): """Matrix representation iteration and item access.""" + def __repr__(self): return "".format(hex(id(self))) def __getitem__(self, key): - return self.obj._to_matrix(self.obj.array[key], - self.obj.phase[key], - sparse=sparse) + return self.obj._to_matrix(self.obj.array[key], self.obj.phase[key], sparse=sparse) + return MatrixIterator(self) diff --git a/qiskit/quantum_info/random.py b/qiskit/quantum_info/random.py index 3ef228995da2..f2ea1e743938 100644 --- a/qiskit/quantum_info/random.py +++ b/qiskit/quantum_info/random.py @@ -13,14 +13,15 @@ """Methods for generating random quantum information objects.""" # pylint: disable=unused-import -from qiskit.quantum_info.operators.random import (random_unitary, - random_quantum_channel, - random_hermitian, - random_pauli, - random_clifford, - random_pauli_table, - random_stabilizer_table, - random_cnotdihedral) +from qiskit.quantum_info.operators.random import ( + random_unitary, + random_quantum_channel, + random_hermitian, + random_pauli, + random_clifford, + random_pauli_table, + random_stabilizer_table, + random_cnotdihedral, +) -from qiskit.quantum_info.states.random import (random_statevector, - random_density_matrix) +from qiskit.quantum_info.states.random import random_statevector, random_density_matrix diff --git a/qiskit/quantum_info/states/__init__.py b/qiskit/quantum_info/states/__init__.py index 64f2db32f9ae..bd74c99b983d 100644 --- a/qiskit/quantum_info/states/__init__.py +++ b/qiskit/quantum_info/states/__init__.py @@ -15,5 +15,11 @@ from .statevector import Statevector from .densitymatrix import DensityMatrix from .utils import partial_trace, shannon_entropy -from .measures import (state_fidelity, purity, entropy, concurrence, - mutual_information, entanglement_of_formation) +from .measures import ( + state_fidelity, + purity, + entropy, + concurrence, + mutual_information, + entanglement_of_formation, +) diff --git a/qiskit/quantum_info/states/densitymatrix.py b/qiskit/quantum_info/states/densitymatrix.py index 4ff72be18049..2b81426e4eca 100644 --- a/qiskit/quantum_info/states/densitymatrix.py +++ b/qiskit/quantum_info/states/densitymatrix.py @@ -77,7 +77,7 @@ def __init__(self, data, dims=None): # If the data is a circuit or an instruction use the classmethod # to construct the DensityMatrix object self._data = DensityMatrix.from_instruction(data)._data - elif hasattr(data, 'to_operator'): + elif hasattr(data, "to_operator"): # If the data object has a 'to_operator' attribute this is given # higher preference than the 'to_matrix' method for initializing # an Operator object. @@ -85,7 +85,7 @@ def __init__(self, data, dims=None): self._data = op.data if dims is None: dims = op.output_dims() - elif hasattr(data, 'to_matrix'): + elif hasattr(data, "to_matrix"): # If no 'to_operator' attribute exists we next look for a # 'to_matrix' attribute to a matrix that will be cast into # a complex numpy matrix. @@ -102,10 +102,8 @@ def __init__(self, data, dims=None): elif ndim == 2 and shape[1] == 1: self._data = np.reshape(self._data, shape[0]) else: - raise QiskitError( - "Invalid DensityMatrix input: not a square matrix.") - super().__init__(op_shape=OpShape.auto( - shape=self._data.shape, dims_l=dims, dims_r=dims)) + raise QiskitError("Invalid DensityMatrix input: not a square matrix.") + super().__init__(op_shape=OpShape.auto(shape=self._data.shape, dims_l=dims, dims_r=dims)) def __array__(self, dtype=None): if dtype: @@ -114,15 +112,18 @@ def __array__(self, dtype=None): def __eq__(self, other): return super().__eq__(other) and np.allclose( - self._data, other._data, rtol=self.rtol, atol=self.atol) + self._data, other._data, rtol=self.rtol, atol=self.atol + ) def __repr__(self): - prefix = 'DensityMatrix(' - pad = len(prefix) * ' ' - return '{}{},\n{}dims={})'.format( - prefix, np.array2string( - self._data, separator=', ', prefix=prefix), - pad, self._op_shape.dims_l()) + prefix = "DensityMatrix(" + pad = len(prefix) * " " + return "{}{},\n{}dims={})".format( + prefix, + np.array2string(self._data, separator=", ", prefix=prefix), + pad, + self._op_shape.dims_l(), + ) def draw(self, output=None, **drawer_args): """Return a visualization of the Statevector. @@ -163,6 +164,7 @@ def draw(self, output=None, **drawer_args): """ # pylint: disable=cyclic-import from qiskit.visualization.state_visualization import state_drawer + return state_drawer(self, output=output, **drawer_args) def _ipython_display_(self): @@ -171,6 +173,7 @@ def _ipython_display_(self): print(out) else: from IPython.display import display + display(out) @property @@ -307,14 +310,14 @@ def evolve(self, other, qargs=None): specified QuantumState subsystem dimensions. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) # Evolution by a circuit or instruction if isinstance(other, (QuantumCircuit, Instruction)): return self._evolve_instruction(other, qargs=qargs) # Evolution by a QuantumChannel - if hasattr(other, 'to_quantumchannel'): + if hasattr(other, "to_quantumchannel"): return other.to_quantumchannel()._evolve(self, qargs=qargs) if isinstance(other, QuantumChannel): return other._evolve(self, qargs=qargs) @@ -339,22 +342,23 @@ def reverse_qargs(self): ret = copy.copy(self) axes = tuple(range(self._op_shape._num_qargs_l - 1, -1, -1)) axes = axes + tuple(len(axes) + i for i in axes) - ret._data = np.reshape(np.transpose( - np.reshape(self.data, self._op_shape.tensor_shape), axes), - self._op_shape.shape) + ret._data = np.reshape( + np.transpose(np.reshape(self.data, self._op_shape.tensor_shape), axes), + self._op_shape.shape, + ) ret._op_shape = self._op_shape.reverse() return ret def _expectation_value_pauli(self, pauli, qargs=None): """Compute the expectation value of a Pauli. - Args: - pauli (Pauli): a Pauli operator to evaluate expval of. - qargs (None or list): subsystems to apply operator on. + Args: + pauli (Pauli): a Pauli operator to evaluate expval of. + qargs (None or list): subsystems to apply operator on. - Returns: - complex: the expectation value. - """ + Returns: + complex: the expectation value. + """ n_pauli = len(pauli) if qargs is None: qubits = np.arange(n_pauli) @@ -367,14 +371,15 @@ def _expectation_value_pauli(self, pauli, qargs=None): if x_mask + z_mask == 0: return pauli_phase * self.trace() - data = np.ravel(self.data, order='F') + data = np.ravel(self.data, order="F") if x_mask == 0: return pauli_phase * density_expval_pauli_no_x(data, self.num_qubits, z_mask) x_max = qubits[pauli.x][-1] y_phase = (-1j) ** np.sum(pauli.x & pauli.z) return pauli_phase * density_expval_pauli_with_x( - data, self.num_qubits, z_mask, x_mask, y_phase, x_max) + data, self.num_qubits, z_mask, x_mask, y_phase, x_max + ) def expectation_value(self, oper, qargs=None): """Compute the expectation value of an operator. @@ -390,8 +395,12 @@ def expectation_value(self, oper, qargs=None): return self._expectation_value_pauli(oper, qargs) if isinstance(oper, SparsePauliOp): - return sum([coeff * self._expectation_value_pauli(Pauli((z, x)), qargs) - for z, x, coeff in zip(oper.table.Z, oper.table.X, oper.coeffs)]) + return sum( + [ + coeff * self._expectation_value_pauli(Pauli((z, x)), qargs) + for z, x, coeff in zip(oper.table.Z, oper.table.X, oper.coeffs) + ] + ) if not isinstance(oper, Operator): oper = Operator(oper) @@ -455,7 +464,8 @@ def probabilities(self, qargs=None, decimals=None): print('Swapped probs: {}'.format(probs_swapped)) """ probs = self._subsystem_probabilities( - np.abs(self.data.diagonal()), self._op_shape.dims_l(), qargs=qargs) + np.abs(self.data.diagonal()), self._op_shape.dims_l(), qargs=qargs + ) if decimals is not None: probs = probs.round(decimals=decimals) return probs @@ -525,6 +535,7 @@ def from_label(cls, label): of the label is larger than an explicitly specified num_qubits. """ from qiskit.quantum_info.states.statevector import Statevector + return DensityMatrix(Statevector.from_label(label)) @staticmethod @@ -578,9 +589,9 @@ def from_instruction(cls, instruction): instruction = instruction.to_instruction() # Initialize an the statevector in the all |0> state num_qubits = instruction.num_qubits - init = np.zeros((2**num_qubits, 2**num_qubits), dtype=complex) + init = np.zeros((2 ** num_qubits, 2 ** num_qubits), dtype=complex) init[0, 0] = 1 - vec = DensityMatrix(init, dims=num_qubits * (2, )) + vec = DensityMatrix(init, dims=num_qubits * (2,)) vec._append_instruction(instruction) return vec @@ -643,10 +654,9 @@ def to_dict(self, decimals=None): rho = DensityMatrix(mat, dims=(2, 10)) print(rho.to_dict()) """ - return self._matrix_to_dict(self.data, - self._op_shape.dims_l(), - decimals=decimals, - string_labels=True) + return self._matrix_to_dict( + self.data, self._op_shape.dims_l(), decimals=decimals, string_labels=True + ) def _evolve_operator(self, other, qargs=None): """Evolve density matrix by an operator""" @@ -674,8 +684,7 @@ def _evolve_operator(self, other, qargs=None): # Right multiply by mat ** dagger adj = other.adjoint() mat_adj = np.reshape(adj.data, adj._op_shape.tensor_shape) - tensor = Operator._einsum_matmul(tensor, mat_adj, indices, num_indices, - True) + tensor = Operator._einsum_matmul(tensor, mat_adj, indices, num_indices, True) # Replace evolved dimensions ret._data = np.reshape(tensor, new_shape.shape) ret._op_shape = new_shape @@ -709,17 +718,19 @@ def _append_instruction(self, other, qargs=None): # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if other.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format( - other.name)) + raise QiskitError("Cannot apply Instruction: {}".format(other.name)) if not isinstance(other.definition, QuantumCircuit): - raise QiskitError('{} instruction definition is {}; expected QuantumCircuit'.format( - other.name, type(other.definition))) + raise QiskitError( + "{} instruction definition is {}; expected QuantumCircuit".format( + other.name, type(other.definition) + ) + ) qubit_indices = {bit: idx for idx, bit in enumerate(other.definition.qubits)} for instr, qregs, cregs in other.definition: if cregs: raise QiskitError( - 'Cannot apply instruction with classical registers: {}'. - format(instr.name)) + "Cannot apply instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register if qargs is None: new_qargs = [qubit_indices[tup] for tup in qregs] @@ -750,6 +761,7 @@ def to_statevector(self, atol=None, rtol=None): QiskitError: if the state is not pure. """ from qiskit.quantum_info.states.statevector import Statevector + if atol is None: atol = self.atol if rtol is None: @@ -761,8 +773,7 @@ def to_statevector(self, atol=None, rtol=None): evals, evecs = np.linalg.eig(self._data) nonzero_evals = evals[abs(evals) > atol] - if len(nonzero_evals) != 1 or not np.isclose(nonzero_evals[0], 1, - atol=atol, rtol=rtol): + if len(nonzero_evals) != 1 or not np.isclose(nonzero_evals[0], 1, atol=atol, rtol=rtol): raise QiskitError("Density matrix is not a pure state") psi = evecs[:, np.argmax(evals)] # eigenvectors returned in columns. diff --git a/qiskit/quantum_info/states/measures.py b/qiskit/quantum_info/states/measures.py index e3aa48502879..6e37788d1d00 100644 --- a/qiskit/quantum_info/states/measures.py +++ b/qiskit/quantum_info/states/measures.py @@ -18,8 +18,12 @@ from qiskit.exceptions import QiskitError from qiskit.quantum_info.states.statevector import Statevector from qiskit.quantum_info.states.densitymatrix import DensityMatrix -from qiskit.quantum_info.states.utils import (partial_trace, shannon_entropy, - _format_state, _funm_svd) +from qiskit.quantum_info.states.utils import ( + partial_trace, + shannon_entropy, + _format_state, + _funm_svd, +) def state_fidelity(state1, state2, validate=True): @@ -57,7 +61,7 @@ def state_fidelity(state1, state2, validate=True): if isinstance(state1, Statevector): if isinstance(state2, Statevector): # Fidelity of two Statevectors - fid = np.abs(arr2.conj().dot(arr1))**2 + fid = np.abs(arr2.conj().dot(arr1)) ** 2 else: # Fidelity of Statevector(1) and DensityMatrix(2) fid = arr1.conj().dot(arr2).dot(arr1) @@ -68,7 +72,7 @@ def state_fidelity(state1, state2, validate=True): # Fidelity of two DensityMatrices s1sq = _funm_svd(arr1, np.sqrt) s2sq = _funm_svd(arr2, np.sqrt) - fid = np.linalg.norm(s1sq.dot(s2sq), ord='nuc')**2 + fid = np.linalg.norm(s1sq.dot(s2sq), ord="nuc") ** 2 # Convert to py float rather than return np.float return float(np.real(fid)) @@ -119,7 +123,7 @@ def entropy(state, base=2): if isinstance(state, Statevector): return 0 # Density matrix case - evals = np.maximum(np.real(la.eigvals(state.data)), 0.) + evals = np.maximum(np.real(la.eigvals(state.data)), 0.0) return shannon_entropy(evals, base=base) @@ -148,11 +152,10 @@ def mutual_information(state, base=2): """ state = _format_state(state, validate=True) if len(state.dims()) != 2: - raise QiskitError('Input must be a bipartite quantum state.') + raise QiskitError("Input must be a bipartite quantum state.") rho_a = partial_trace(state, [1]) rho_b = partial_trace(state, [0]) - return entropy(rho_a, base=base) + entropy( - rho_b, base=base) - entropy(state, base=base) + return entropy(rho_a, base=base) + entropy(rho_b, base=base) - entropy(state, base=base) def concurrence(state): @@ -198,18 +201,18 @@ def concurrence(state): # Pure state concurrence dims = state.dims() if len(dims) != 2: - raise QiskitError('Input is not a bipartite quantum state.') + raise QiskitError("Input is not a bipartite quantum state.") qargs = [0] if dims[0] > dims[1] else [1] rho = partial_trace(state, qargs) return float(np.sqrt(2 * (1 - np.real(purity(rho))))) # If input is a density matrix it must be a 2-qubit state if state.dim != 4: - raise QiskitError('Input density matrix must be a 2-qubit state.') + raise QiskitError("Input density matrix must be a 2-qubit state.") rho = DensityMatrix(state).data yy_mat = np.fliplr(np.diag([-1, 1, 1, -1])) sigma = rho.dot(yy_mat).dot(rho.conj()).dot(yy_mat) w = np.sort(np.real(la.eigvals(sigma))) - w = np.sqrt(np.maximum(w, 0.)) + w = np.sqrt(np.maximum(w, 0.0)) return max(0.0, w[-1] - np.sum(w[0:-1])) @@ -236,13 +239,13 @@ def entanglement_of_formation(state): # entropy dims = state.dims() if len(dims) != 2: - raise QiskitError('Input is not a bipartite quantum state.') + raise QiskitError("Input is not a bipartite quantum state.") qargs = [0] if dims[0] > dims[1] else [1] return entropy(partial_trace(state, qargs), base=2) # If input is a density matrix it must be a 2-qubit state if state.dim != 4: - raise QiskitError('Input density matrix must be a 2-qubit state.') + raise QiskitError("Input density matrix must be a 2-qubit state.") conc = concurrence(state) val = (1 + np.sqrt(1 - (conc ** 2))) / 2 return shannon_entropy([val, 1 - val]) diff --git a/qiskit/quantum_info/states/quantum_state.py b/qiskit/quantum_info/states/quantum_state.py index 95940dd7d47e..a2b4a712936a 100644 --- a/qiskit/quantum_info/states/quantum_state.py +++ b/qiskit/quantum_info/states/quantum_state.py @@ -148,8 +148,7 @@ def _add(self, other): Raises: NotImplementedError: if subclass does not support addition. """ - raise NotImplementedError( - "{} does not support addition".format(type(self))) + raise NotImplementedError("{} does not support addition".format(type(self))) def _multiply(self, other): """Return the scalar multipled state other * self. @@ -164,8 +163,7 @@ def _multiply(self, other): NotImplementedError: if subclass does not support scala multiplication. """ - raise NotImplementedError( - "{} does not support scalar multiplication".format(type(self))) + raise NotImplementedError("{} does not support scalar multiplication".format(type(self))) @abstractmethod def evolve(self, other, qargs=None): @@ -237,9 +235,8 @@ def probabilities_dict(self, qargs=None, decimals=None): dict: The measurement probabilities in dict (ket) form. """ return self._vector_to_dict( - self.probabilities(qargs=qargs, decimals=decimals), - self.dims(qargs), - string_labels=True) + self.probabilities(qargs=qargs, decimals=decimals), self.dims(qargs), string_labels=True + ) def sample_memory(self, shots, qargs=None): """Sample a list of qubit measurement outcomes in the computational basis. @@ -268,7 +265,8 @@ def sample_memory(self, shots, qargs=None): # Generate list of possible outcome string labels labels = self._index_to_ket_array( - np.arange(len(probs)), self.dims(qargs), string_labels=True) + np.arange(len(probs)), self.dims(qargs), string_labels=True + ) return self._rng.choice(labels, p=probs, size=shots) def sample_counts(self, shots, qargs=None): @@ -324,8 +322,7 @@ def measure(self, qargs=None): sample = self._rng.choice(len(probs), p=probs, size=1) # Format outcome - outcome = self._index_to_ket_array( - sample, self.dims(qargs), string_labels=True)[0] + outcome = self._index_to_ket_array(sample, self.dims(qargs), string_labels=True)[0] # Convert to projector for state update proj = np.zeros(len(probs), dtype=complex) @@ -334,9 +331,7 @@ def measure(self, qargs=None): # Update state object # TODO: implement a more efficient state update method for # diagonal matrix multiplication - ret = self.evolve( - Operator(np.diag(proj), input_dims=dims, output_dims=dims), - qargs=qargs) + ret = self.evolve(Operator(np.diag(proj), input_dims=dims, output_dims=dims), qargs=qargs) return outcome, ret @@ -365,7 +360,7 @@ def _index_to_ket_array(inds, dims, string_labels=False): str_kets = char_kets[0] for row in char_kets[1:]: if max_dim > 10: - str_kets = np.char.add(',', str_kets) + str_kets = np.char.add(",", str_kets) str_kets = np.char.add(row, str_kets) return str_kets.T @@ -391,11 +386,10 @@ def _vector_to_dict(vec, dims, decimals=None, string_labels=False): """ # Get indices of non-zero elements vals = vec if decimals is None else vec.round(decimals=decimals) - inds, = vals.nonzero() + (inds,) = vals.nonzero() # Convert to ket tuple based on subsystem dimensions - kets = QuantumState._index_to_ket_array( - inds, dims, string_labels=string_labels) + kets = QuantumState._index_to_ket_array(inds, dims, string_labels=string_labels) # Make dict of tuples if string_labels: @@ -423,21 +417,26 @@ def _matrix_to_dict(mat, dims, decimals=None, string_labels=False): """ # Get indices of non-zero elements vals = mat if decimals is None else mat.round(decimals=decimals) - inds_row, inds_col, = vals.nonzero() + ( + inds_row, + inds_col, + ) = vals.nonzero() # Convert to ket tuple based on subsystem dimensions - bras = QuantumState._index_to_ket_array( - inds_row, dims, string_labels=string_labels) - kets = QuantumState._index_to_ket_array( - inds_col, dims, string_labels=string_labels) + bras = QuantumState._index_to_ket_array(inds_row, dims, string_labels=string_labels) + kets = QuantumState._index_to_ket_array(inds_col, dims, string_labels=string_labels) # Make dict of tuples if string_labels: - return {'{}|{}'.format(ket, bra): val for ket, bra, val in zip( - kets, bras, vals[inds_row, inds_col])} + return { + "{}|{}".format(ket, bra): val + for ket, bra, val in zip(kets, bras, vals[inds_row, inds_col]) + } - return {(tuple(ket), tuple(bra)): val for ket, bra, val in zip( - kets, bras, vals[inds_row, inds_col])} + return { + (tuple(ket), tuple(bra)): val + for ket, bra, val in zip(kets, bras, vals[inds_row, inds_col]) + } @staticmethod def _subsystem_probabilities(probs, dims, qargs=None): @@ -475,10 +474,11 @@ def __and__(self, other): return self.evolve(other) @deprecate_function( - 'Using `psi @ U` as shorthand for `psi.evolve(U)` is deprecated' - ' as of version 0.17.0 and will be removed no earlier than 3 months' - ' after the release date. It has been superceded by the `&` operator' - ' (`psi & U == psi.evolve(U)`) instead.') + "Using `psi @ U` as shorthand for `psi.evolve(U)` is deprecated" + " as of version 0.17.0 and will be removed no earlier than 3 months" + " after the release date. It has been superceded by the `&` operator" + " (`psi & U == psi.evolve(U)`) instead." + ) def __matmul__(self, other): # Check for subsystem case return by __call__ method if isinstance(other, tuple) and len(other) == 2: diff --git a/qiskit/quantum_info/states/random.py b/qiskit/quantum_info/states/random.py index 7537c014be2a..2128db69e970 100644 --- a/qiskit/quantum_info/states/random.py +++ b/qiskit/quantum_info/states/random.py @@ -54,8 +54,7 @@ def random_statevector(dims, seed=None): return Statevector(np.sqrt(x / sumx) * np.exp(1j * phases), dims=dims) -def random_density_matrix(dims, rank=None, method='Hilbert-Schmidt', - seed=None): +def random_density_matrix(dims, rank=None, method="Hilbert-Schmidt", seed=None): """Generator a random DensityMatrix. Args: @@ -79,12 +78,12 @@ def random_density_matrix(dims, rank=None, method='Hilbert-Schmidt', if rank is None: rank = dim # Use full rank - if method == 'Hilbert-Schmidt': + if method == "Hilbert-Schmidt": rho = _random_density_hs(dim, rank, seed) - elif method == 'Bures': + elif method == "Bures": rho = _random_density_bures(dim, rank, seed) else: - raise QiskitError('Error: unrecognized method {}'.format(method)) + raise QiskitError("Error: unrecognized method {}".format(method)) return DensityMatrix(rho, dims=dims) @@ -107,8 +106,7 @@ def _ginibre_matrix(nrow, ncol, seed): else: rng = default_rng(seed) - ginibre = rng.normal( - size=(nrow, ncol)) + rng.normal(size=(nrow, ncol)) * 1j + ginibre = rng.normal(size=(nrow, ncol)) + rng.normal(size=(nrow, ncol)) * 1j return ginibre diff --git a/qiskit/quantum_info/states/statevector.py b/qiskit/quantum_info/states/statevector.py index 58a6b6c51845..47063c2f5574 100644 --- a/qiskit/quantum_info/states/statevector.py +++ b/qiskit/quantum_info/states/statevector.py @@ -95,8 +95,7 @@ def __init__(self, data, dims=None): shape = self._data.shape elif ndim != 2 or shape[1] != 1: raise QiskitError("Invalid input: not a vector or column-vector.") - super().__init__(op_shape=OpShape.auto( - shape=shape, dims_l=dims, num_qubits_r=0)) + super().__init__(op_shape=OpShape.auto(shape=shape, dims_l=dims, num_qubits_r=0)) def __array__(self, dtype=None): if dtype: @@ -105,15 +104,18 @@ def __array__(self, dtype=None): def __eq__(self, other): return super().__eq__(other) and np.allclose( - self._data, other._data, rtol=self.rtol, atol=self.atol) + self._data, other._data, rtol=self.rtol, atol=self.atol + ) def __repr__(self): - prefix = 'Statevector(' - pad = len(prefix) * ' ' - return '{}{},\n{}dims={})'.format( - prefix, np.array2string( - self._data, separator=', ', prefix=prefix), - pad, self._op_shape.dims_l()) + prefix = "Statevector(" + pad = len(prefix) * " " + return "{}{},\n{}dims={})".format( + prefix, + np.array2string(self._data, separator=", ", prefix=prefix), + pad, + self._op_shape.dims_l(), + ) def draw(self, output=None, **drawer_args): """Return a visualization of the Statevector. @@ -158,6 +160,7 @@ def draw(self, output=None, **drawer_args): """ # pylint: disable=cyclic-import from qiskit.visualization.state_visualization import state_drawer + return state_drawer(self, output=output, **drawer_args) def _ipython_display_(self): @@ -166,6 +169,7 @@ def _ipython_display_(self): print(out) else: from IPython.display import display + display(out) @property @@ -295,7 +299,7 @@ def evolve(self, other, qargs=None): specified Statevector subsystem dimensions. """ if qargs is None: - qargs = getattr(other, 'qargs', None) + qargs = getattr(other, "qargs", None) # Get return vector ret = copy.copy(self) @@ -347,8 +351,7 @@ def equiv(self, other, rtol=None, atol=None): atol = self.atol if rtol is None: rtol = self.rtol - return matrix_equal(self.data, other.data, ignore_phase=True, - rtol=rtol, atol=atol) + return matrix_equal(self.data, other.data, ignore_phase=True, rtol=rtol, atol=atol) def reverse_qargs(self): r"""Return a Statevector with reversed subsystem ordering. @@ -364,21 +367,22 @@ def reverse_qargs(self): """ ret = copy.copy(self) axes = tuple(range(self._op_shape._num_qargs_l - 1, -1, -1)) - ret._data = np.reshape(np.transpose( - np.reshape(self.data, self._op_shape.tensor_shape), axes), - self._op_shape.shape) + ret._data = np.reshape( + np.transpose(np.reshape(self.data, self._op_shape.tensor_shape), axes), + self._op_shape.shape, + ) ret._op_shape = self._op_shape.reverse() return ret def _expectation_value_pauli(self, pauli, qargs=None): """Compute the expectation value of a Pauli. - Args: - pauli (Pauli): a Pauli operator to evaluate expval of. - qargs (None or list): subsystems to apply operator on. + Args: + pauli (Pauli): a Pauli operator to evaluate expval of. + qargs (None or list): subsystems to apply operator on. - Returns: - complex: the expectation value. + Returns: + complex: the expectation value. """ n_pauli = len(pauli) if qargs is None: @@ -400,7 +404,8 @@ def _expectation_value_pauli(self, pauli, qargs=None): y_phase = (-1j) ** np.sum(pauli.x & pauli.z) return pauli_phase * expval_pauli_with_x( - self.data, self.num_qubits, z_mask, x_mask, y_phase, x_max) + self.data, self.num_qubits, z_mask, x_mask, y_phase, x_max + ) def expectation_value(self, oper, qargs=None): """Compute the expectation value of an operator. @@ -416,8 +421,12 @@ def expectation_value(self, oper, qargs=None): return self._expectation_value_pauli(oper, qargs) if isinstance(oper, SparsePauliOp): - return sum([coeff * self._expectation_value_pauli(Pauli((z, x)), qargs) - for z, x, coeff in zip(oper.table.Z, oper.table.X, oper.coeffs)]) + return sum( + [ + coeff * self._expectation_value_pauli(Pauli((z, x)), qargs) + for z, x, coeff in zip(oper.table.Z, oper.table.X, oper.coeffs) + ] + ) val = self.evolve(oper, qargs=qargs) conj = self.conjugate() @@ -480,7 +489,8 @@ def probabilities(self, qargs=None, decimals=None): print('Swapped probs: {}'.format(probs_swapped)) """ probs = self._subsystem_probabilities( - np.abs(self.data) ** 2, self._op_shape.dims_l(), qargs=qargs) + np.abs(self.data) ** 2, self._op_shape.dims_l(), qargs=qargs + ) if decimals is not None: probs = probs.round(decimals=decimals) return probs @@ -529,9 +539,7 @@ def reset(self, qargs=None): # compose with reset projection reset = np.dot(reset, np.diag(proj)) - return self.evolve( - Operator(reset, input_dims=dims, output_dims=dims), - qargs=qargs) + return self.evolve(Operator(reset, input_dims=dims, output_dims=dims), qargs=qargs) @classmethod def from_label(cls, label): @@ -568,22 +576,22 @@ def from_label(cls, label): specified num_qubits. """ # Check label is valid - if re.match(r'^[01rl\-+]+$', label) is None: - raise QiskitError('Label contains invalid characters.') + if re.match(r"^[01rl\-+]+$", label) is None: + raise QiskitError("Label contains invalid characters.") # We can prepare Z-eigenstates by converting the computational # basis bit-string to an integer and preparing that unit vector # However, for X-basis states, we will prepare a Z-eigenstate first # then apply Hadamard gates to rotate 0 and 1s to + and -. z_label = label xy_states = False - if re.match('^[01]+$', label) is None: + if re.match("^[01]+$", label) is None: # We have X or Y eigenstates so replace +,r with 0 and # -,l with 1 and prepare the corresponding Z state xy_states = True - z_label = z_label.replace('+', '0') - z_label = z_label.replace('r', '0') - z_label = z_label.replace('-', '1') - z_label = z_label.replace('l', '1') + z_label = z_label.replace("+", "0") + z_label = z_label.replace("r", "0") + z_label = z_label.replace("-", "1") + z_label = z_label.replace("l", "1") # Initialize Z eigenstate vector num_qubits = len(label) data = np.zeros(1 << num_qubits, dtype=complex) @@ -596,9 +604,9 @@ def from_label(cls, label): # Apply S.H to qubits in Y eigenstates y_mat = np.dot(np.diag([1, 1j]), x_mat) for qubit, char in enumerate(reversed(label)): - if char in ['+', '-']: + if char in ["+", "-"]: state = state.evolve(x_mat, qargs=[qubit]) - elif char in ['r', 'l']: + elif char in ["r", "l"]: state = state.evolve(y_mat, qargs=[qubit]) return state @@ -714,10 +722,9 @@ def to_dict(self, decimals=None): psi = Statevector(vec, dims=(2, 10)) print(psi.to_dict()) """ - return self._vector_to_dict(self.data, - self._op_shape.dims_l(), - decimals=decimals, - string_labels=True) + return self._vector_to_dict( + self.data, self._op_shape.dims_l(), decimals=decimals, string_labels=True + ) @staticmethod def _evolve_operator(statevec, oper, qargs=None): @@ -741,13 +748,12 @@ def _evolve_operator(statevec, oper, qargs=None): # Reshape input for contraction statevec._data = np.reshape( - np.transpose(np.reshape(statevec.data, statevec._op_shape.tensor_shape), - axes), contract_shape) + np.transpose(np.reshape(statevec.data, statevec._op_shape.tensor_shape), axes), + contract_shape, + ) # Contract and reshape output - statevec._data = np.reshape(np.dot(oper.data, statevec._data), - new_shape.tensor_shape) - statevec._data = np.reshape(np.transpose(statevec._data, axes_inv), - new_shape.shape[0]) + statevec._data = np.reshape(np.dot(oper.data, statevec._data), new_shape.tensor_shape) + statevec._data = np.reshape(np.transpose(statevec._data, axes_inv), new_shape.shape[0]) # Update dimension statevec._op_shape = new_shape @@ -776,18 +782,21 @@ def _evolve_instruction(statevec, obj, qargs=None): # circuit decomposition definition if it exists, otherwise we # cannot compose this gate and raise an error. if obj.definition is None: - raise QiskitError('Cannot apply Instruction: {}'.format(obj.name)) + raise QiskitError("Cannot apply Instruction: {}".format(obj.name)) if not isinstance(obj.definition, QuantumCircuit): - raise QiskitError('{} instruction definition is {}; expected QuantumCircuit'.format( - obj.name, type(obj.definition))) + raise QiskitError( + "{} instruction definition is {}; expected QuantumCircuit".format( + obj.name, type(obj.definition) + ) + ) if obj.definition.global_phase: statevec._data *= np.exp(1j * float(obj.definition.global_phase)) qubits = {qubit: i for i, qubit in enumerate(obj.definition.qubits)} for instr, qregs, cregs in obj.definition: if cregs: raise QiskitError( - 'Cannot apply instruction with classical registers: {}'.format( - instr.name)) + "Cannot apply instruction with classical registers: {}".format(instr.name) + ) # Get the integer position of the flat register if qargs is None: new_qargs = [qubits[tup] for tup in qregs] diff --git a/qiskit/quantum_info/states/utils.py b/qiskit/quantum_info/states/utils.py index d8d4ffcaa42c..6094e4d4bd2f 100644 --- a/qiskit/quantum_info/states/utils.py +++ b/qiskit/quantum_info/states/utils.py @@ -56,21 +56,18 @@ def partial_trace(state, qargs): if isinstance(state, Statevector): trace_systems = len(state._op_shape.dims_l()) - 1 - np.array(qargs) arr = state._data.reshape(state._op_shape.tensor_shape) - rho = np.tensordot(arr, arr.conj(), - axes=(trace_systems, trace_systems)) + rho = np.tensordot(arr, arr.conj(), axes=(trace_systems, trace_systems)) rho = np.reshape(rho, traced_shape.shape) return DensityMatrix(rho, dims=traced_shape._dims_l) # Density matrix case # Trace first subsystem to avoid coping whole density matrix dims = state.dims(qargs) - tr_op = SuperOp(np.eye(dims[0]).reshape(1, dims[0] ** 2), - input_dims=[dims[0]], output_dims=[1]) + tr_op = SuperOp(np.eye(dims[0]).reshape(1, dims[0] ** 2), input_dims=[dims[0]], output_dims=[1]) ret = state.evolve(tr_op, [qargs[0]]) # Trace over remaining subsystems for qarg, dim in zip(qargs[1:], dims[1:]): - tr_op = SuperOp(np.eye(dim).reshape(1, dim ** 2), - input_dims=[dim], output_dims=[1]) + tr_op = SuperOp(np.eye(dim).reshape(1, dim ** 2), input_dims=[dim], output_dims=[1]) ret = ret.evolve(tr_op, [qarg]) # Remove traced over subsystems which are listed as dimension 1 ret._op_shape = traced_shape @@ -98,16 +95,21 @@ def shannon_entropy(pvec, base=2): float: The Shannon entropy H(pvec). """ if base == 2: + def logfn(x): - return - x * np.log2(x) + return -x * np.log2(x) + elif base == np.e: + def logfn(x): - return - x * np.log(x) + return -x * np.log(x) + else: + def logfn(x): return -x * np.log(x) / np.log(base) - h_val = 0. + h_val = 0.0 for x in pvec: if 0 < x < 1: h_val += logfn(x) diff --git a/qiskit/quantum_info/synthesis/clifford_decompose.py b/qiskit/quantum_info/synthesis/clifford_decompose.py index eddaf8fd4bbf..ca237d72af38 100644 --- a/qiskit/quantum_info/synthesis/clifford_decompose.py +++ b/qiskit/quantum_info/synthesis/clifford_decompose.py @@ -19,8 +19,15 @@ from qiskit.exceptions import QiskitError from qiskit.circuit import QuantumCircuit from qiskit.quantum_info.operators.symplectic.clifford_circuits import ( - _append_z, _append_x, _append_h, _append_s, _append_v, _append_w, - _append_cx, _append_swap) + _append_z, + _append_x, + _append_h, + _append_s, + _append_v, + _append_w, + _append_cx, + _append_swap, +) def decompose_clifford(clifford, method=None): @@ -53,10 +60,10 @@ def decompose_clifford(clifford, method=None): """ num_qubits = clifford.num_qubits - if method == 'AG': + if method == "AG": return decompose_clifford_ag(clifford) - if method == 'greedy': + if method == "greedy": return decompose_clifford_greedy(clifford) if num_qubits <= 3: @@ -69,17 +76,17 @@ def decompose_clifford(clifford, method=None): # Synthesis based on Bravyi & Maslov decomposition # --------------------------------------------------------------------- + def decompose_clifford_bm(clifford): """Decompose a clifford""" num_qubits = clifford.num_qubits clifford_name = str(clifford) if num_qubits == 1: - return _decompose_clifford_1q( - clifford.table.array, clifford.table.phase) + return _decompose_clifford_1q(clifford.table.array, clifford.table.phase) # Inverse of final decomposed circuit - inv_circuit = QuantumCircuit(num_qubits, name='inv_circ') + inv_circuit = QuantumCircuit(num_qubits, name="inv_circ") # CNOT cost of clifford cost = _cx_cost(clifford) @@ -108,6 +115,7 @@ def decompose_clifford_bm(clifford): # Synthesis based on Aaronson & Gottesman decomposition # --------------------------------------------------------------------- + def decompose_clifford_ag(clifford): """Decompose a Clifford operator into a QuantumCircuit. @@ -119,12 +127,10 @@ def decompose_clifford_ag(clifford): """ # Use 1-qubit decomposition method if clifford.num_qubits == 1: - return _decompose_clifford_1q( - clifford.table.array, clifford.table.phase) + return _decompose_clifford_1q(clifford.table.array, clifford.table.phase) # Compose a circuit which we will convert to an instruction - circuit = QuantumCircuit(clifford.num_qubits, - name=str(clifford)) + circuit = QuantumCircuit(clifford.num_qubits, name=str(clifford)) # Make a copy of Clifford as we are going to do row reduction to # reduce it to an identity @@ -155,9 +161,10 @@ def decompose_clifford_ag(clifford): # 1-qubit Clifford decomposition # --------------------------------------------------------------------- + def _decompose_clifford_1q(pauli, phase): """Decompose a single-qubit clifford""" - circuit = QuantumCircuit(1, name='temp') + circuit = QuantumCircuit(1, name="temp") # Add phase correction destab_phase, stab_phase = phase @@ -167,38 +174,38 @@ def _decompose_clifford_1q(pauli, phase): circuit.x(0) elif destab_phase and stab_phase: circuit.y(0) - destab_phase_label = '-' if destab_phase else '+' - stab_phase_label = '-' if stab_phase else '+' + destab_phase_label = "-" if destab_phase else "+" + stab_phase_label = "-" if stab_phase else "+" destab_x, destab_z = pauli[0] stab_x, stab_z = pauli[1] # Z-stabilizer if stab_z and not stab_x: - stab_label = 'Z' + stab_label = "Z" if destab_z: - destab_label = 'Y' + destab_label = "Y" circuit.s(0) else: - destab_label = 'X' + destab_label = "X" # X-stabilizer elif not stab_z and stab_x: - stab_label = 'X' + stab_label = "X" if destab_x: - destab_label = 'Y' + destab_label = "Y" circuit.sdg(0) else: - destab_label = 'Z' + destab_label = "Z" circuit.h(0) # Y-stabilizer else: - stab_label = 'Y' + stab_label = "Y" if destab_z: - destab_label = 'Z' + destab_label = "Z" else: - destab_label = 'X' + destab_label = "X" circuit.s(0) circuit.h(0) circuit.s(0) @@ -214,6 +221,7 @@ def _decompose_clifford_1q(pauli, phase): # Helper functions for Bravyi & Maslov decomposition # --------------------------------------------------------------------- + def _reduce_cost(clifford, inv_circuit, cost): """Two-qubit cost reduction step""" num_qubits = clifford.num_qubits @@ -293,8 +301,7 @@ def _cx_cost3(clifford): mask[[q2, q2 + n]] = 1 isLocX = np.array_equal(U[q1, :] & mask, U[q1, :]) isLocZ = np.array_equal(U[q1 + n, :] & mask, U[q1 + n, :]) - isLocY = np.array_equal((U[q1, :] ^ U[q1 + n, :]) & mask, - (U[q1, :] ^ U[q1 + n, :])) + isLocY = np.array_equal((U[q1, :] ^ U[q1 + n, :]) & mask, (U[q1, :] ^ U[q1 + n, :])) R1[q1, q2] = 1 * (isLocX or isLocZ or isLocY) + 1 * (isLocX and isLocZ and isLocY) diag1 = np.sort(np.diag(R1)).tolist() @@ -309,21 +316,31 @@ def _cx_cost3(clifford): if diag1 == [1, 1, 2]: return 1 - if (diag1 == [0, 1, 1] - or (diag1 == [1, 1, 1] and nz2 < 9) - or (diag1 == [0, 0, 2] and diag2 == [1, 1, 2])): + if ( + diag1 == [0, 1, 1] + or (diag1 == [1, 1, 1] and nz2 < 9) + or (diag1 == [0, 0, 2] and diag2 == [1, 1, 2]) + ): return 2 - if ((diag1 == [1, 1, 1] and nz2 == 9) - or (diag1 == [0, 0, 1] and ( - nz1 == 1 or diag2 == [2, 2, 2] or (diag2 == [1, 1, 2] and nz2 < 9))) - or (diag1 == [0, 0, 2] and diag2 == [0, 0, 2]) - or (diag2 == [1, 2, 2] and nz1 == 0)): + if ( + (diag1 == [1, 1, 1] and nz2 == 9) + or ( + diag1 == [0, 0, 1] + and (nz1 == 1 or diag2 == [2, 2, 2] or (diag2 == [1, 1, 2] and nz2 < 9)) + ) + or (diag1 == [0, 0, 2] and diag2 == [0, 0, 2]) + or (diag2 == [1, 2, 2] and nz1 == 0) + ): return 3 - if (diag2 == [0, 0, 1] or (diag1 == [0, 0, 0] and ( + if diag2 == [0, 0, 1] or ( + diag1 == [0, 0, 0] + and ( (diag2 == [1, 1, 1] and nz2 == 9 and nz1 == 3) - or (diag2 == [0, 1, 1] and nz2 == 8 and nz1 == 2)))): + or (diag2 == [0, 1, 1] and nz2 == 8 and nz1 == 2) + ) + ): return 5 if nz1 == 3 and nz2 == 3: @@ -336,6 +353,7 @@ def _cx_cost3(clifford): # Helper functions for Aaronson & Gottesman decomposition # --------------------------------------------------------------------- + def _set_qubit_x_true(clifford, circuit, qubit): """Set destabilizer.X[qubit, qubit] to be True. @@ -408,7 +426,7 @@ def _set_row_z_zero(clifford, circuit, qubit): z = clifford.stabilizer.Z[qubit] # check whether Zs need to be set to zero: - if np.any(z[qubit + 1:]): + if np.any(z[qubit + 1 :]): for i in range(qubit + 1, clifford.num_qubits): if z[i]: _append_cx(clifford, i, qubit) @@ -433,6 +451,7 @@ def _set_row_z_zero(clifford, circuit, qubit): # Synthesis based on Bravyi et. al. greedy clifford compiler # --------------------------------------------------------------------- + def decompose_clifford_greedy(clifford): """Decompose a Clifford operator into a QuantumCircuit. @@ -489,8 +508,9 @@ def decompose_clifford_greedy(clifford): cliff_oz = cliff_oz.compose(clifford_cpy_inv) # Compute the decoupling operator of cliff_ox and cliff_oz - decouple_circ, decouple_cliff = _calc_decoupling(cliff_ox, cliff_oz, qubit_list, - min_qubit, num_qubits) + decouple_circ, decouple_cliff = _calc_decoupling( + cliff_ox, cliff_oz, qubit_list, min_qubit, num_qubits + ) circ = circ.compose(decouple_circ) # Now the clifford acts trivially on min_qubit @@ -519,27 +539,35 @@ def decompose_clifford_greedy(clifford): # divided into 5 equivalence classes under the action of single-qubit Cliffords # Class A - canonical representative is 'XZ' -A_class = [[[False, True], [True, True]], # 'XY' - [[False, True], [True, False]], # 'XZ' - [[True, True], [False, True]], # 'YX' - [[True, True], [True, False]], # 'YZ' - [[True, False], [False, True]], # 'ZX' - [[True, False], [True, True]]] # 'ZY' +A_class = [ + [[False, True], [True, True]], # 'XY' + [[False, True], [True, False]], # 'XZ' + [[True, True], [False, True]], # 'YX' + [[True, True], [True, False]], # 'YZ' + [[True, False], [False, True]], # 'ZX' + [[True, False], [True, True]], +] # 'ZY' # Class B - canonical representative is 'XX' -B_class = [[[True, False], [True, False]], # 'ZZ' - [[False, True], [False, True]], # 'XX' - [[True, True], [True, True]]] # 'YY' +B_class = [ + [[True, False], [True, False]], # 'ZZ' + [[False, True], [False, True]], # 'XX' + [[True, True], [True, True]], +] # 'YY' # Class C - canonical representative is 'XI' -C_class = [[[True, False], [False, False]], # 'ZI' - [[False, True], [False, False]], # 'XI' - [[True, True], [False, False]]] # 'YI' +C_class = [ + [[True, False], [False, False]], # 'ZI' + [[False, True], [False, False]], # 'XI' + [[True, True], [False, False]], +] # 'YI' # Class D - canonical representative is 'IZ' -D_class = [[[False, False], [False, True]], # 'IX' - [[False, False], [True, False]], # 'IZ' - [[False, False], [True, True]]] # 'IY' +D_class = [ + [[False, False], [False, True]], # 'IX' + [[False, False], [True, False]], # 'IZ' + [[False, False], [True, True]], +] # 'IY' # Class E - only 'II' E_class = [[[False, False], [False, False]]] # 'II' @@ -606,21 +634,27 @@ def _calc_decoupling(cliff_ox, cliff_oz, qubit_list, min_qubit, num_qubits): typeq = _from_pair_cliffs_to_type(cliff_ox, cliff_oz, qubit) - if typeq in [[[True, True], [False, False]], # 'YI' - [[True, True], [True, True]], # 'YY' - [[True, True], [True, False]]]: # 'YZ': + if typeq in [ + [[True, True], [False, False]], # 'YI' + [[True, True], [True, True]], # 'YY' + [[True, True], [True, False]], + ]: # 'YZ': circ.s(qubit) _append_s(decouple_cliff, qubit) - elif typeq in [[[True, False], [False, False]], # 'ZI' - [[True, False], [True, False]], # 'ZZ' - [[True, False], [False, True]], # 'ZX' - [[False, False], [False, True]]]: # 'IX' + elif typeq in [ + [[True, False], [False, False]], # 'ZI' + [[True, False], [True, False]], # 'ZZ' + [[True, False], [False, True]], # 'ZX' + [[False, False], [False, True]], + ]: # 'IX' circ.h(qubit) _append_h(decouple_cliff, qubit) - elif typeq in [[[False, False], [True, True]], # 'IY' - [[True, False], [True, True]]]: # 'ZY' + elif typeq in [ + [[False, False], [True, True]], # 'IY' + [[True, False], [True, True]], + ]: # 'ZY' circ.s(qubit) circ.h(qubit) _append_s(decouple_cliff, qubit) diff --git a/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py b/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py index 39e53b266e33..4653dd15de5c 100644 --- a/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py +++ b/qiskit/quantum_info/synthesis/cnotdihedral_decompose.py @@ -66,8 +66,10 @@ def decompose_cnotdihedral_2_qubits(elem): circuit = QuantumCircuit(elem.num_qubits) if elem.num_qubits > 2: - raise QiskitError("Cannot decompose a CNOT-Dihedral element with more than 2 qubits. " - "use decompose_cnotdihedral_general function instead.") + raise QiskitError( + "Cannot decompose a CNOT-Dihedral element with more than 2 qubits. " + "use decompose_cnotdihedral_general function instead." + ) if elem.num_qubits == 1: if elem.poly.weight_0 != 0 or elem.linear != [[1]]: @@ -78,7 +80,7 @@ def decompose_cnotdihedral_2_qubits(elem): circuit.p((tpow0 * np.pi / 4), [0]) if xpow0 == 1: circuit.x(0) - if (tpow0 == 0 and xpow0 == 0): + if tpow0 == 0 and xpow0 == 0: circuit.id(0) return circuit @@ -105,13 +107,12 @@ def decompose_cnotdihedral_2_qubits(elem): circuit.p((tpow1 * np.pi / 4), [1]) if xpow1 == 1: circuit.x(1) - if (tpow0 == 0 and tpow1 == 0 and xpow0 == 0 and xpow1 == 0): + if tpow0 == 0 and tpow1 == 0 and xpow0 == 0 and xpow1 == 0: circuit.id(0) circuit.id(1) # CS-like class - if ((weight_2 == [2] and xpow0 == xpow1) or - (weight_2 == [6] and xpow0 != xpow1)): + if (weight_2 == [2] and xpow0 == xpow1) or (weight_2 == [6] and xpow0 != xpow1): tpow0 = (weight_1[0] - 2 * xpow1 - 4 * xpow0 * xpow1) % 8 tpow1 = (weight_1[1] - 2 * xpow0 - 4 * xpow0 * xpow1) % 8 if tpow0 > 0: @@ -130,8 +131,7 @@ def decompose_cnotdihedral_2_qubits(elem): circuit.cx(0, 1) # CSdg-like class - if ((weight_2 == [6] and xpow0 == xpow1) or - (weight_2 == [2] and xpow0 != xpow1)): + if (weight_2 == [6] and xpow0 == xpow1) or (weight_2 == [2] and xpow0 != xpow1): tpow0 = (weight_1[0] - 6 * xpow1 - 4 * xpow0 * xpow1) % 8 tpow1 = (weight_1[1] - 6 * xpow0 - 4 * xpow0 * xpow1) % 8 if tpow0 > 0: @@ -330,7 +330,7 @@ def decompose_cnotdihedral_general(elem): for i in range(num_qubits): # set i-th element to be 1 if not elem_cpy.linear[i][i]: - for j in range(i+1, num_qubits): + for j in range(i + 1, num_qubits): if elem_cpy.linear[j][i]: # swap qubits i and j circuit.cx(j, i) circuit.cx(i, j) @@ -346,25 +346,27 @@ def decompose_cnotdihedral_general(elem): circuit.cx(i, j) elem_cpy._append_cx(i, j) - if not (elem_cpy.shift == np.zeros(num_qubits)).all() or \ - not (elem_cpy.linear == np.eye(num_qubits)).all(): + if ( + not (elem_cpy.shift == np.zeros(num_qubits)).all() + or not (elem_cpy.linear == np.eye(num_qubits)).all() + ): raise QiskitError("Cannot do Gauss elimination on linear part.") # Initialize new_elem to an identity CNOTDihderal element new_elem = elem_cpy.copy() new_elem.poly.weight_0 = 0 new_elem.poly.weight_1 = np.zeros(num_qubits, dtype=np.int8) - new_elem.poly.weight_2 = np.zeros(int(num_qubits * (num_qubits - 1) / 2), - dtype=np.int8) - new_elem.poly.weight_3 = np.zeros(int(num_qubits * (num_qubits - 1) * - (num_qubits - 2) / 6), dtype=np.int8) + new_elem.poly.weight_2 = np.zeros(int(num_qubits * (num_qubits - 1) / 2), dtype=np.int8) + new_elem.poly.weight_3 = np.zeros( + int(num_qubits * (num_qubits - 1) * (num_qubits - 2) / 6), dtype=np.int8 + ) new_circuit = QuantumCircuit(num_qubits) # Do cx and phase gates to construct all monomials of weight 3 for i in range(num_qubits): - for j in range(i+1, num_qubits): - for k in range(j+1, num_qubits): + for j in range(i + 1, num_qubits): + for k in range(j + 1, num_qubits): if elem_cpy.poly.get_term([i, j, k]) != 0: new_elem._append_cx(i, k) new_elem._append_cx(j, k) @@ -379,7 +381,7 @@ def decompose_cnotdihedral_general(elem): # Do cx and phase gates to construct all monomials of weight 2 for i in range(num_qubits): - for j in range(i+1, num_qubits): + for j in range(i + 1, num_qubits): tpow1 = elem_cpy.poly.get_term([i, j]) tpow2 = new_elem.poly.get_term([i, j]) tpow = ((tpow2 - tpow1) / 2) % 4 diff --git a/qiskit/quantum_info/synthesis/local_invariance.py b/qiskit/quantum_info/synthesis/local_invariance.py index 1b3780556dda..2aaaccf21515 100644 --- a/qiskit/quantum_info/synthesis/local_invariance.py +++ b/qiskit/quantum_info/synthesis/local_invariance.py @@ -20,11 +20,11 @@ INVARIANT_TOL = 1e-12 # Bell "Magic" basis -MAGIC = 1.0/sqrt(2)*np.array([ - [1, 0, 0, 1j], - [0, 1j, 1, 0], - [0, 1j, -1, 0], - [1, 0, 0, -1j]], dtype=complex) +MAGIC = ( + 1.0 + / sqrt(2) + * np.array([[1, 0, 0, 1j], [0, 1j, 1, 0], [0, 1j, -1, 0], [1, 0, 0, -1j]], dtype=complex) +) def two_qubit_local_invariants(U): @@ -45,7 +45,7 @@ def two_qubit_local_invariants(U): """ U = np.asarray(U) if U.shape != (4, 4): - raise ValueError('Unitary must correspond to a two-qubit gate.') + raise ValueError("Unitary must correspond to a two-qubit gate.") # Transform to bell basis Um = MAGIC.conj().T.dot(U.dot(MAGIC)) @@ -57,8 +57,8 @@ def two_qubit_local_invariants(U): m_tr2 *= m_tr2 # Table II of Ref. 1 or Eq. 28 of Ref. 2. - G1 = m_tr2/(16*det_um) - G2 = (m_tr2 - np.trace(M.dot(M)))/(4*det_um) + G1 = m_tr2 / (16 * det_um) + G2 = (m_tr2 - np.trace(M.dot(M))) / (4 * det_um) # Here we split the real and imag pieces of G1 into two so as # to better equate to the Weyl chamber coordinates (c0,c1,c2) @@ -82,7 +82,11 @@ def local_equivalence(weyl): but we multiply weyl coordinates by 2 since we are working in the reduced chamber. """ - g0_equiv = np.prod(np.cos(2*weyl)**2)-np.prod(np.sin(2*weyl)**2) - g1_equiv = np.prod(np.sin(4*weyl))/4 - g2_equiv = 4*np.prod(np.cos(2*weyl)**2)-4*np.prod(np.sin(2*weyl)**2)-np.prod(np.cos(4*weyl)) + g0_equiv = np.prod(np.cos(2 * weyl) ** 2) - np.prod(np.sin(2 * weyl) ** 2) + g1_equiv = np.prod(np.sin(4 * weyl)) / 4 + g2_equiv = ( + 4 * np.prod(np.cos(2 * weyl) ** 2) + - 4 * np.prod(np.sin(2 * weyl) ** 2) + - np.prod(np.cos(4 * weyl)) + ) return np.round([g0_equiv, g1_equiv, g2_equiv], 12) + 0.0 diff --git a/qiskit/quantum_info/synthesis/one_qubit_decompose.py b/qiskit/quantum_info/synthesis/one_qubit_decompose.py index 6759217363fc..8db9d562c609 100644 --- a/qiskit/quantum_info/synthesis/one_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/one_qubit_decompose.py @@ -21,27 +21,36 @@ from qiskit.circuit.quantumcircuit import QuantumCircuit from qiskit.circuit.quantumregister import QuantumRegister -from qiskit.circuit.library.standard_gates import (UGate, PhaseGate, U3Gate, - U2Gate, U1Gate, RXGate, RYGate, - RZGate, RGate, SXGate, - XGate) +from qiskit.circuit.library.standard_gates import ( + UGate, + PhaseGate, + U3Gate, + U2Gate, + U1Gate, + RXGate, + RYGate, + RZGate, + RGate, + SXGate, + XGate, +) from qiskit.exceptions import QiskitError from qiskit.quantum_info.operators.predicates import is_unitary_matrix DEFAULT_ATOL = 1e-12 ONE_QUBIT_EULER_BASIS_GATES = { - 'U3': ['u3'], - 'U321': ['u3', 'u2', 'u1'], - 'U': ['u'], - 'PSX': ['p', 'sx'], - 'U1X': ['u1', 'rx'], - 'RR': ['r'], - 'ZYZ': ['rz', 'ry'], - 'ZXZ': ['rz', 'rx'], - 'XYX': ['rx', 'ry'], - 'ZSXX': ['rz', 'sx', 'x'], - 'ZSX': ['rz', 'sx'], + "U3": ["u3"], + "U321": ["u3", "u2", "u1"], + "U": ["u"], + "PSX": ["p", "sx"], + "U1X": ["u1", "rx"], + "RR": ["r"], + "ZYZ": ["rz", "ry"], + "ZXZ": ["rz", "rx"], + "XYX": ["rx", "ry"], + "ZSXX": ["rz", "sx", "x"], + "ZSX": ["rz", "sx"], } @@ -103,7 +112,7 @@ class OneQubitEulerDecomposer: :math:`R\left(\theta+\pi,\frac{\pi}{2}-\lambda\right)` """ - def __init__(self, basis='U3'): + def __init__(self, basis="U3"): """Initialize decomposer Supported bases are: 'U', 'PSX', 'ZSXX', 'ZSX', 'U321', 'U3', 'U1X', 'RR', 'ZYZ', 'ZXZ', @@ -117,10 +126,7 @@ def __init__(self, basis='U3'): """ self.basis = basis # sets: self._basis, self._params, self._circuit - def __call__(self, - unitary, - simplify=True, - atol=DEFAULT_ATOL): + def __call__(self, unitary, simplify=True, atol=DEFAULT_ATOL): """Decompose single qubit gate into a circuit. Args: @@ -134,12 +140,12 @@ def __call__(self, Raises: QiskitError: if input is invalid or synthesis fails. """ - if hasattr(unitary, 'to_operator'): + if hasattr(unitary, "to_operator"): # If input is a BaseOperator subclass this attempts to convert # the object to an Operator so that we can extract the underlying # numpy matrix from `Operator.data`. unitary = unitary.to_operator().data - elif hasattr(unitary, 'to_matrix'): + elif hasattr(unitary, "to_matrix"): # If input is Gate subclass or some other class object that has # a to_matrix method this will call that method. unitary = unitary.to_matrix() @@ -148,18 +154,14 @@ def __call__(self, # Check input is a 2-qubit unitary if unitary.shape != (2, 2): - raise QiskitError("OneQubitEulerDecomposer: " - "expected 2x2 input matrix") + raise QiskitError("OneQubitEulerDecomposer: " "expected 2x2 input matrix") if not is_unitary_matrix(unitary): - raise QiskitError("OneQubitEulerDecomposer: " - "input matrix is not unitary.") + raise QiskitError("OneQubitEulerDecomposer: " "input matrix is not unitary.") return self._decompose(unitary, simplify=simplify, atol=atol) def _decompose(self, unitary, simplify=True, atol=DEFAULT_ATOL): theta, phi, lam, phase = self._params(unitary) - circuit = self._circuit(theta, phi, lam, phase, - simplify=simplify, - atol=atol) + circuit = self._circuit(theta, phi, lam, phase, simplify=simplify, atol=atol) return circuit @property @@ -171,17 +173,17 @@ def basis(self): def basis(self, basis): """Set the decomposition basis.""" basis_methods = { - 'U321': (self._params_u3, self._circuit_u321), - 'U3': (self._params_u3, self._circuit_u3), - 'U': (self._params_u3, self._circuit_u), - 'PSX': (self._params_u1x, self._circuit_psx), - 'ZSX': (self._params_u1x, self._circuit_zsx), - 'ZSXX': (self._params_u1x, self._circuit_zsxx), - 'U1X': (self._params_u1x, self._circuit_u1x), - 'RR': (self._params_zyz, self._circuit_rr), - 'ZYZ': (self._params_zyz, self._circuit_zyz), - 'ZXZ': (self._params_zxz, self._circuit_zxz), - 'XYX': (self._params_xyx, self._circuit_xyx) + "U321": (self._params_u3, self._circuit_u321), + "U3": (self._params_u3, self._circuit_u3), + "U": (self._params_u3, self._circuit_u), + "PSX": (self._params_u1x, self._circuit_psx), + "ZSX": (self._params_u1x, self._circuit_zsx), + "ZSXX": (self._params_u1x, self._circuit_zsxx), + "U1X": (self._params_u1x, self._circuit_u1x), + "RR": (self._params_zyz, self._circuit_rr), + "ZYZ": (self._params_zyz, self._circuit_zyz), + "ZXZ": (self._params_zxz, self._circuit_zxz), + "XYX": (self._params_xyx, self._circuit_xyx), } if basis not in basis_methods: raise QiskitError("OneQubitEulerDecomposer: unsupported basis {}".format(basis)) @@ -216,7 +218,7 @@ def _params_zyz(mat): """Return the Euler angles and phase for the ZYZ basis.""" # We rescale the input matrix to be special unitary (det(U) = 1) # This ensures that the quaternion representation is real - coeff = la.det(mat)**(-0.5) + coeff = la.det(mat) ** (-0.5) phase = -cmath.phase(coeff) su_mat = coeff * mat # U in SU(2) # OpenQASM SU(2) parameterization: @@ -227,8 +229,8 @@ def _params_zyz(mat): theta = 2 * math.atan2(abs(su_mat[1, 0]), abs(su_mat[0, 0])) phiplambda2 = cmath.phase(su_mat[1, 1]) phimlambda2 = cmath.phase(su_mat[1, 0]) - phi = (phiplambda2 + phimlambda2) - lam = (phiplambda2 - phimlambda2) + phi = phiplambda2 + phimlambda2 + lam = phiplambda2 - phimlambda2 return theta, phi, lam, phase @staticmethod @@ -243,18 +245,21 @@ def _params_xyx(mat): # We use the fact that # Rx(a).Ry(b).Rx(c) = H.Rz(a).Ry(-b).Rz(c).H mat_zyz = 0.5 * np.array( - [[ - mat[0, 0] + mat[0, 1] + mat[1, 0] + mat[1, 1], - mat[0, 0] - mat[0, 1] + mat[1, 0] - mat[1, 1] + [ + [ + mat[0, 0] + mat[0, 1] + mat[1, 0] + mat[1, 1], + mat[0, 0] - mat[0, 1] + mat[1, 0] - mat[1, 1], + ], + [ + mat[0, 0] + mat[0, 1] - mat[1, 0] - mat[1, 1], + mat[0, 0] - mat[0, 1] - mat[1, 0] + mat[1, 1], + ], ], - [ - mat[0, 0] + mat[0, 1] - mat[1, 0] - mat[1, 1], - mat[0, 0] - mat[0, 1] - mat[1, 0] + mat[1, 1] - ]], - dtype=complex) + dtype=complex, + ) theta, phi, lam, phase = OneQubitEulerDecomposer._params_zyz(mat_zyz) - newphi, newlam = _mod_2pi(phi+np.pi), _mod_2pi(lam+np.pi) - return theta, newphi, newlam, phase + (newphi + newlam - phi - lam)/2 + newphi, newlam = _mod_2pi(phi + np.pi), _mod_2pi(lam + np.pi) + return theta, newphi, newlam, phase + (newphi + newlam - phi - lam) / 2 @staticmethod def _params_u3(mat): @@ -276,14 +281,9 @@ def _params_u1x(mat): return theta, phi, lam, phase - 0.5 * (theta + phi + lam) @staticmethod - def _circuit_zyz(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - gphase = phase - (phi+lam)/2 - qr = QuantumRegister(1, 'qr') + def _circuit_zyz(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + gphase = phase - (phi + lam) / 2 + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr) if not simplify: atol = -1.0 @@ -291,33 +291,28 @@ def _circuit_zyz(theta, tot = _mod_2pi(phi + lam, atol) if abs(tot) > atol: circuit._append(RZGate(tot), [qr[0]], []) - gphase += tot/2 + gphase += tot / 2 circuit.global_phase = gphase return circuit if abs(theta - np.pi) < atol: gphase += phi - lam, phi = lam-phi, 0 + lam, phi = lam - phi, 0 lam = _mod_2pi(lam, atol) if abs(lam) > atol: - gphase += lam/2 + gphase += lam / 2 circuit._append(RZGate(lam), [qr[0]], []) circuit._append(RYGate(theta), [qr[0]], []) phi = _mod_2pi(phi, atol) if abs(phi) > atol: - gphase += phi/2 + gphase += phi / 2 circuit._append(RZGate(phi), [qr[0]], []) circuit.global_phase = gphase return circuit @staticmethod - def _circuit_zxz(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - gphase = phase - (phi+lam)/2 - qr = QuantumRegister(1, 'qr') + def _circuit_zxz(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + gphase = phase - (phi + lam) / 2 + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr) if not simplify: atol = -1.0 @@ -325,7 +320,7 @@ def _circuit_zxz(theta, tot = _mod_2pi(phi + lam) if abs(tot) > atol: circuit._append(RZGate(tot), [qr[0]], []) - gphase += tot/2 + gphase += tot / 2 circuit.global_phase = gphase return circuit if abs(theta - np.pi) < atol: @@ -333,25 +328,20 @@ def _circuit_zxz(theta, lam, phi = lam - phi, 0 lam = _mod_2pi(lam, atol) if abs(lam) > atol: - gphase += lam/2 + gphase += lam / 2 circuit._append(RZGate(lam), [qr[0]], []) circuit._append(RXGate(theta), [qr[0]], []) phi = _mod_2pi(phi, atol) if abs(phi) > atol: - gphase += phi/2 + gphase += phi / 2 circuit._append(RZGate(phi), [qr[0]], []) circuit.global_phase = gphase return circuit @staticmethod - def _circuit_xyx(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - gphase = phase - (phi+lam)/2 - qr = QuantumRegister(1, 'qr') + def _circuit_xyx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + gphase = phase - (phi + lam) / 2 + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr) if not simplify: atol = -1.0 @@ -359,32 +349,27 @@ def _circuit_xyx(theta, tot = _mod_2pi(phi + lam, atol) if abs(tot) > atol: circuit._append(RXGate(tot), [qr[0]], []) - gphase += tot/2 + gphase += tot / 2 circuit.global_phase = gphase return circuit if abs(theta - np.pi) < atol: gphase += phi - lam, phi = lam-phi, 0 + lam, phi = lam - phi, 0 lam = _mod_2pi(lam, atol) if abs(lam) > atol: - gphase += lam/2 + gphase += lam / 2 circuit._append(RXGate(lam), [qr[0]], []) circuit._append(RYGate(theta), [qr[0]], []) phi = _mod_2pi(phi, atol) if abs(phi) > atol: - gphase += phi/2 + gphase += phi / 2 circuit._append(RXGate(phi), [qr[0]], []) circuit.global_phase = gphase return circuit @staticmethod - def _circuit_u3(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - qr = QuantumRegister(1, 'qr') + def _circuit_u3(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr, global_phase=phase) phi = _mod_2pi(phi, atol) lam = _mod_2pi(lam, atol) @@ -393,13 +378,8 @@ def _circuit_u3(theta, return circuit @staticmethod - def _circuit_u321(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - qr = QuantumRegister(1, 'qr') + def _circuit_u321(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr, global_phase=phase) if not simplify: atol = -1.0 @@ -407,20 +387,15 @@ def _circuit_u321(theta, tot = _mod_2pi(phi + lam, atol) if abs(tot) > atol: circuit._append(U1Gate(tot), [qr[0]], []) - elif abs(theta - np.pi/2) < atol: + elif abs(theta - np.pi / 2) < atol: circuit._append(U2Gate(_mod_2pi(phi, atol), _mod_2pi(lam, atol)), [qr[0]], []) else: circuit._append(U3Gate(theta, _mod_2pi(phi, atol), _mod_2pi(lam, atol)), [qr[0]], []) return circuit @staticmethod - def _circuit_u(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - qr = QuantumRegister(1, 'qr') + def _circuit_u(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr, global_phase=phase) if not simplify: atol = -1.0 @@ -433,26 +408,26 @@ def _circuit_u(theta, @staticmethod def _circuit_psx_gen(theta, phi, lam, phase, atol, pfun, xfun, xpifun=None): """Generic X90, phase decomposition""" - qr = QuantumRegister(1, 'qr') + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr, global_phase=phase) # Check for decomposition into minimimal number required SX pulses if np.abs(theta) < atol: # Zero SX gate decomposition pfun(circuit, qr, lam + phi) return circuit - if abs(theta - np.pi/2) < atol: + if abs(theta - np.pi / 2) < atol: # Single SX gate decomposition - pfun(circuit, qr, lam - np.pi/2) + pfun(circuit, qr, lam - np.pi / 2) xfun(circuit, qr) - pfun(circuit, qr, phi + np.pi/2) + pfun(circuit, qr, phi + np.pi / 2) return circuit # General two-SX gate decomposition # Shift theta and phi so decomposition is # P(phi).SX.P(theta).SX.P(lam) - if abs(theta-np.pi) < atol: + if abs(theta - np.pi) < atol: circuit.global_phase += lam - phi, lam = phi-lam, 0 - circuit.global_phase -= np.pi/2 + phi, lam = phi - lam, 0 + circuit.global_phase -= np.pi / 2 pfun(circuit, qr, lam) if xpifun and abs(_mod_2pi(theta + np.pi)) < atol: xpifun(circuit, qr) @@ -465,12 +440,7 @@ def _circuit_psx_gen(theta, phi, lam, phase, atol, pfun, xfun, xpifun=None): return circuit @staticmethod - def _circuit_psx(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): + def _circuit_psx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): if not simplify: atol = -1.0 @@ -485,12 +455,7 @@ def fnx(circuit, qr): return OneQubitEulerDecomposer._circuit_psx_gen(theta, phi, lam, phase, atol, fnz, fnx) @staticmethod - def _circuit_zsx(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): + def _circuit_zsx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): if not simplify: atol = -1.0 @@ -498,7 +463,7 @@ def fnz(circuit, qr, phi): phi = _mod_2pi(phi, atol) if abs(phi) > atol: circuit._append(RZGate(phi), [qr[0]], []) - circuit.global_phase += phi/2 + circuit.global_phase += phi / 2 def fnx(circuit, qr): circuit._append(SXGate(), [qr[0]], []) @@ -506,12 +471,7 @@ def fnx(circuit, qr): return OneQubitEulerDecomposer._circuit_psx_gen(theta, phi, lam, phase, atol, fnz, fnx) @staticmethod - def _circuit_u1x(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): + def _circuit_u1x(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): if not simplify: atol = -1.0 @@ -521,18 +481,13 @@ def fnz(circuit, qr, phi): circuit._append(U1Gate(phi), [qr[0]], []) def fnx(circuit, qr): - circuit.global_phase += np.pi/4 + circuit.global_phase += np.pi / 4 circuit._append(RXGate(np.pi / 2), [qr[0]], []) return OneQubitEulerDecomposer._circuit_psx_gen(theta, phi, lam, phase, atol, fnz, fnx) @staticmethod - def _circuit_zsxx(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): + def _circuit_zsxx(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): if not simplify: atol = -1.0 @@ -540,7 +495,7 @@ def fnz(circuit, qr, phi): phi = _mod_2pi(phi, atol) if abs(phi) > atol: circuit._append(RZGate(phi), [qr[0]], []) - circuit.global_phase += phi/2 + circuit.global_phase += phi / 2 def fnx(circuit, qr): circuit._append(SXGate(), [qr[0]], []) @@ -548,17 +503,13 @@ def fnx(circuit, qr): def fnxpi(circuit, qr): circuit._append(XGate(), [qr[0]], []) - return OneQubitEulerDecomposer._circuit_psx_gen(theta, phi, lam, phase, atol, - fnz, fnx, fnxpi) + return OneQubitEulerDecomposer._circuit_psx_gen( + theta, phi, lam, phase, atol, fnz, fnx, fnxpi + ) @staticmethod - def _circuit_rr(theta, - phi, - lam, - phase, - simplify=True, - atol=DEFAULT_ATOL): - qr = QuantumRegister(1, 'qr') + def _circuit_rr(theta, phi, lam, phase, simplify=True, atol=DEFAULT_ATOL): + qr = QuantumRegister(1, "qr") circuit = QuantumCircuit(qr, global_phase=phase) if not simplify: atol = -1.0 @@ -572,7 +523,7 @@ def _circuit_rr(theta, def _mod_2pi(angle: float, atol: float = 0): """Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π""" - wrapped = (angle+np.pi) % (2*np.pi) - np.pi + wrapped = (angle + np.pi) % (2 * np.pi) - np.pi if abs(wrapped - np.pi) < atol: wrapped = -np.pi return wrapped diff --git a/qiskit/quantum_info/synthesis/quaternion.py b/qiskit/quantum_info/synthesis/quaternion.py index 2645468c4d2e..df58907f1b9e 100644 --- a/qiskit/quantum_info/synthesis/quaternion.py +++ b/qiskit/quantum_info/synthesis/quaternion.py @@ -19,8 +19,7 @@ class Quaternion: - """A class representing a Quaternion. - """ + """A class representing a Quaternion.""" def __init__(self, data): self.data = np.asarray(data, dtype=float) @@ -38,17 +37,16 @@ def __mul__(self, r): if isinstance(r, Quaternion): q = self out_data = np.zeros(4, dtype=float) - out_data[0] = r(0)*q(0) - r(1)*q(1) - r(2)*q(2) - r(3)*q(3) - out_data[1] = r(0)*q(1) + r(1)*q(0) - r(2)*q(3) + r(3)*q(2) - out_data[2] = r(0)*q(2) + r(1)*q(3) + r(2)*q(0) - r(3)*q(1) - out_data[3] = r(0)*q(3) - r(1)*q(2) + r(2)*q(1) + r(3)*q(0) + out_data[0] = r(0) * q(0) - r(1) * q(1) - r(2) * q(2) - r(3) * q(3) + out_data[1] = r(0) * q(1) + r(1) * q(0) - r(2) * q(3) + r(3) * q(2) + out_data[2] = r(0) * q(2) + r(1) * q(3) + r(2) * q(0) - r(3) * q(1) + out_data[3] = r(0) * q(3) - r(1) * q(2) + r(2) * q(1) + r(3) * q(0) return Quaternion(out_data) else: - raise Exception('Multiplication by other not supported.') + raise Exception("Multiplication by other not supported.") def norm(self): - """ Norm of quaternion. - """ + """Norm of quaternion.""" return la.norm(self.data) def normalize(self, inplace=False): @@ -77,11 +75,14 @@ def to_matrix(self): ndarray: Rotation matrix. """ w, x, y, z = self.normalize().data - mat = np.array([ - [1-2*y**2-2*z**2, 2*x*y-2*z*w, 2*x*z+2*y*w], - [2*x*y+2*z*w, 1-2*x**2-2*z**2, 2*y*z-2*x*w], - [2*x*z-2*y*w, 2*y*z+2*x*w, 1-2*x**2-2*y**2] - ], dtype=float) + mat = np.array( + [ + [1 - 2 * y ** 2 - 2 * z ** 2, 2 * x * y - 2 * z * w, 2 * x * z + 2 * y * w], + [2 * x * y + 2 * z * w, 1 - 2 * x ** 2 - 2 * z ** 2, 2 * y * z - 2 * x * w], + [2 * x * z - 2 * y * w, 2 * y * z + 2 * x * w, 1 - 2 * x ** 2 - 2 * y ** 2], + ], + dtype=float, + ) return mat def to_zyz(self): @@ -120,20 +121,20 @@ def from_axis_rotation(cls, angle, axis): ValueError: Invalid input axis. """ out = np.zeros(4, dtype=float) - if axis == 'x': + if axis == "x": out[1] = 1 - elif axis == 'y': + elif axis == "y": out[2] = 1 - elif axis == 'z': + elif axis == "z": out[3] = 1 else: - raise ValueError('Invalid axis input.') - out *= math.sin(angle/2.0) - out[0] = math.cos(angle/2.0) + raise ValueError("Invalid axis input.") + out *= math.sin(angle / 2.0) + out[0] = math.cos(angle / 2.0) return cls(out) @classmethod - def from_euler(cls, angles, order='yzy'): + def from_euler(cls, angles, order="yzy"): """Generate a quaternion from a set of Euler angles. Args: @@ -144,8 +145,10 @@ def from_euler(cls, angles, order='yzy'): Quaternion: Quaternion representation of Euler rotation. """ angles = np.asarray(angles, dtype=float) - quat = (cls.from_axis_rotation(angles[0], order[0]) * - cls.from_axis_rotation(angles[1], order[1]) * - cls.from_axis_rotation(angles[2], order[2])) + quat = ( + cls.from_axis_rotation(angles[0], order[0]) + * cls.from_axis_rotation(angles[1], order[1]) + * cls.from_axis_rotation(angles[2], order[2]) + ) quat.normalize(inplace=True) return quat diff --git a/qiskit/quantum_info/synthesis/two_qubit_decompose.py b/qiskit/quantum_info/synthesis/two_qubit_decompose.py index 6da1548b4967..7ebe0b3c8de7 100644 --- a/qiskit/quantum_info/synthesis/two_qubit_decompose.py +++ b/qiskit/quantum_info/synthesis/two_qubit_decompose.py @@ -53,10 +53,10 @@ def decompose_two_qubit_product_gate(special_unitary_matrix): special_unitary_matrix = np.asarray(special_unitary_matrix, dtype=complex) # extract the right component R = special_unitary_matrix[:2, :2].copy() - detR = R[0, 0]*R[1, 1] - R[0, 1]*R[1, 0] + detR = R[0, 0] * R[1, 1] - R[0, 1] * R[1, 0] if abs(detR) < 0.1: R = special_unitary_matrix[2:, :2].copy() - detR = R[0, 0]*R[1, 1] - R[0, 1]*R[1, 0] + detR = R[0, 0] * R[1, 1] - R[0, 1] * R[1, 0] if abs(detR) < 0.1: raise QiskitError("decompose_two_qubit_product_gate: unable to decompose: detR < 0.1") R /= np.sqrt(detR) @@ -65,7 +65,7 @@ def decompose_two_qubit_product_gate(special_unitary_matrix): temp = np.kron(np.eye(2), R.T.conj()) temp = special_unitary_matrix.dot(temp) L = temp[::2, ::2] - detL = L[0, 0]*L[1, 1] - L[0, 1]*L[1, 0] + detL = L[0, 0] * L[1, 1] - L[0, 1] * L[1, 0] if abs(detL) < 0.9: raise QiskitError("decompose_two_qubit_product_gate: unable to decompose: detL < 0.9") L /= np.sqrt(detL) @@ -73,29 +73,26 @@ def decompose_two_qubit_product_gate(special_unitary_matrix): temp = np.kron(L, R) deviation = abs(abs(temp.conj().T.dot(special_unitary_matrix).trace()) - 4) - if deviation > 1.E-13: - raise QiskitError("decompose_two_qubit_product_gate: decomposition failed: " - "deviation too large: {}".format(deviation)) + if deviation > 1.0e-13: + raise QiskitError( + "decompose_two_qubit_product_gate: decomposition failed: " + "deviation too large: {}".format(deviation) + ) return L, R, phase -_B = (1.0/math.sqrt(2)) * np.array([[1, 1j, 0, 0], - [0, 0, 1j, 1], - [0, 0, 1j, -1], - [1, -1j, 0, 0]], dtype=complex) +_B = (1.0 / math.sqrt(2)) * np.array( + [[1, 1j, 0, 0], [0, 0, 1j, 1], [0, 0, 1j, -1], [1, -1j, 0, 0]], dtype=complex +) _Bd = _B.T.conj() -_ipx = np.array([[0, 1j], - [1j, 0]], dtype=complex) -_ipy = np.array([[0, 1], - [-1, 0]], dtype=complex) -_ipz = np.array([[1j, 0], - [0, -1j]], dtype=complex) -_id = np.array([[1, 0], - [0, 1]], dtype=complex) +_ipx = np.array([[0, 1j], [1j, 0]], dtype=complex) +_ipy = np.array([[0, 1], [-1, 0]], dtype=complex) +_ipz = np.array([[1j, 0], [0, -1j]], dtype=complex) +_id = np.array([[1, 0], [0, 1]], dtype=complex) -class TwoQubitWeylDecomposition(): +class TwoQubitWeylDecomposition: """Decompose two-qubit unitary U = (K1l⊗K1r).Exp(i a xx + i b yy + i c zz).(K2l⊗K2r) , where U ∈ U(4), (K1l|K1r|K2l|K2r) ∈ SU(2), and we stay in the "Weyl Chamber" 𝜋/4 ≥ a ≥ b ≥ |c| @@ -125,18 +122,19 @@ class TwoQubitWeylDecomposition(): _original_decomposition: "TwoQubitWeylDecomposition" _is_flipped_from_original: bool # The approx is closest to a Weyl reflection of the original? - _default_1q_basis: ClassVar[str] = 'ZYZ' # Default one qubit basis (explicit parameterization) + _default_1q_basis: ClassVar[str] = "ZYZ" # Default one qubit basis (explicit parameterization) def __init_subclass__(cls, **kwargs): """Subclasses should be concrete, not factories. Make explicitly-instantiated subclass __new__ call base __new__ with fidelity=None""" super().__init_subclass__(**kwargs) - cls.__new__ = (lambda cls, *a, fidelity=None, **k: - TwoQubitWeylDecomposition.__new__(cls, *a, fidelity=None, **k)) + cls.__new__ = lambda cls, *a, fidelity=None, **k: TwoQubitWeylDecomposition.__new__( + cls, *a, fidelity=None, **k + ) @staticmethod - def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): + def __new__(cls, unitary_matrix, *, fidelity=(1.0 - 1.0e-9)): """Perform the Weyl chamber decomposition, and optionally choose a specialized subclass. The flip into the Weyl Chamber is described in B. Kraus and J. I. Cirac, Phys. Rev. A 63, @@ -149,13 +147,13 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): The overall decomposition scheme is taken from Drury and Love, arXiv:0806.4015 [quant-ph]. """ pi = np.pi - pi2 = np.pi/2 - pi4 = np.pi/4 + pi2 = np.pi / 2 + pi4 = np.pi / 4 # Make U be in SU(4) U = np.array(unitary_matrix, dtype=complex, copy=True) detU = la.det(U) - U *= detU**(-0.25) + U *= detU ** (-0.25) global_phase = cmath.phase(detU) / 4 Up = _Bd.dot(U).dot(_B) @@ -166,21 +164,21 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): # D, P = la.eig(M2) # this can fail for certain kinds of degeneracy state = np.random.default_rng(2020) for _ in range(100): # FIXME: this randomized algorithm is horrendous - M2real = state.normal()*M2.real + state.normal()*M2.imag + M2real = state.normal() * M2.real + state.normal() * M2.imag _, P = np.linalg.eigh(M2real) D = P.T.dot(M2).dot(P).diagonal() - if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=0, atol=1.0E-13): + if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=0, atol=1.0e-13): break else: raise QiskitError("TwoQubitWeylDecomposition: failed to diagonalize M2") - d = -np.angle(D)/2 - d[3] = -d[0]-d[1]-d[2] - cs = np.mod((d[:3]+d[3])/2, 2*np.pi) + d = -np.angle(D) / 2 + d[3] = -d[0] - d[1] - d[2] + cs = np.mod((d[:3] + d[3]) / 2, 2 * np.pi) # Reorder the eigenvalues to get in the Weyl chamber cstemp = np.mod(cs, pi2) - np.minimum(cstemp, pi2-cstemp, cstemp) + np.minimum(cstemp, pi2 - cstemp, cstemp) order = np.argsort(cstemp)[[1, 2, 0]] cs = cs[order] d[:3] = d[order] @@ -191,7 +189,7 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): P[:, -1] = -P[:, -1] # Find K1, K2 so that U = K1.A.K2, with K being product of single-qubit unitaries - K1 = _B.dot(Up).dot(P).dot(np.diag(np.exp(1j*d))).dot(_Bd) + K1 = _B.dot(Up).dot(P).dot(np.diag(np.exp(1j * d))).dot(_Bd) K2 = _B.dot(P.T).dot(_Bd) K1l, K1r, phase_l = decompose_two_qubit_product_gate(K1) @@ -202,24 +200,24 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): # Flip into Weyl chamber if cs[0] > pi2: - cs[0] -= 3*pi2 + cs[0] -= 3 * pi2 K1l = K1l.dot(_ipy) K1r = K1r.dot(_ipy) global_phase += pi2 if cs[1] > pi2: - cs[1] -= 3*pi2 + cs[1] -= 3 * pi2 K1l = K1l.dot(_ipx) K1r = K1r.dot(_ipx) global_phase += pi2 conjs = 0 if cs[0] > pi4: - cs[0] = pi2-cs[0] + cs[0] = pi2 - cs[0] K1l = K1l.dot(_ipy) K2r = _ipy.dot(K2r) conjs += 1 global_phase -= pi2 if cs[1] > pi4: - cs[1] = pi2-cs[1] + cs[1] = pi2 - cs[1] K1l = K1l.dot(_ipx) K2r = _ipx.dot(K2r) conjs += 1 @@ -227,14 +225,14 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): if conjs == 1: global_phase -= pi if cs[2] > pi2: - cs[2] -= 3*pi2 + cs[2] -= 3 * pi2 K1l = K1l.dot(_ipz) K1r = K1r.dot(_ipz) global_phase += pi2 if conjs == 1: global_phase -= pi if conjs == 1: - cs[2] = pi2-cs[2] + cs[2] = pi2 - cs[2] K1l = K1l.dot(_ipz) K2r = _ipz.dot(K2r) global_phase += pi2 @@ -264,15 +262,18 @@ def __new__(cls, unitary_matrix, *, fidelity=(1.-1.E-9)): od._is_flipped_from_original = False def is_close(ap, bp, cp): - da, db, dc = a-ap, b-bp, c-cp - tr = 4*complex(math.cos(da)*math.cos(db)*math.cos(dc), - math.sin(da)*math.sin(db)*math.sin(dc)) + da, db, dc = a - ap, b - bp, c - cp + tr = 4 * complex( + math.cos(da) * math.cos(db) * math.cos(dc), + math.sin(da) * math.sin(db) * math.sin(dc), + ) fid = trace_to_fid(tr) return fid >= fidelity if fidelity is None: # Don't specialize if None - instance = super().__new__(TwoQubitWeylGeneral - if cls is TwoQubitWeylDecomposition else cls) + instance = super().__new__( + TwoQubitWeylGeneral if cls is TwoQubitWeylDecomposition else cls + ) elif is_close(0, 0, 0): instance = super().__new__(TwoQubitWeylIdEquiv) elif is_close(pi4, pi4, pi4) or is_close(pi4, pi4, -pi4): @@ -285,11 +286,11 @@ def is_close(ap, bp, cp): instance = super().__new__(TwoQubitWeylControlledEquiv) elif is_close(pi4, pi4, c): instance = super().__new__(TwoQubitWeylMirrorControlledEquiv) - elif is_close((a+b)/2, (a+b)/2, c): + elif is_close((a + b) / 2, (a + b) / 2, c): instance = super().__new__(TwoQubitWeylfSimaabEquiv) - elif is_close(a, (b+c)/2, (b+c)/2): + elif is_close(a, (b + c) / 2, (b + c) / 2): instance = super().__new__(TwoQubitWeylfSimabbEquiv) - elif is_close(a, (b-c)/2, (c-b)/2): + elif is_close(a, (b - c) / 2, (c - b) / 2): instance = super().__new__(TwoQubitWeylfSimabmbEquiv) else: instance = super().__new__(TwoQubitWeylGeneral) @@ -311,26 +312,38 @@ def __init__(self, unitary_matrix, fidelity=None): # Update the phase after specialization: if self._is_flipped_from_original: - da, db, dc = (np.pi/2-od.a)-self.a, od.b-self.b, -od.c-self.c - tr = 4 * complex(math.cos(da)*math.cos(db)*math.cos(dc), - math.sin(da)*math.sin(db)*math.sin(dc)) + da, db, dc = (np.pi / 2 - od.a) - self.a, od.b - self.b, -od.c - self.c + tr = 4 * complex( + math.cos(da) * math.cos(db) * math.cos(dc), + math.sin(da) * math.sin(db) * math.sin(dc), + ) else: - da, db, dc = od.a-self.a, od.b-self.b, od.c-self.c - tr = 4 * complex(math.cos(da)*math.cos(db)*math.cos(dc), - math.sin(da)*math.sin(db)*math.sin(dc)) + da, db, dc = od.a - self.a, od.b - self.b, od.c - self.c + tr = 4 * complex( + math.cos(da) * math.cos(db) * math.cos(dc), + math.sin(da) * math.sin(db) * math.sin(dc), + ) self.global_phase += cmath.phase(tr) self.calculated_fidelity = trace_to_fid(tr) if logger.isEnabledFor(logging.DEBUG): actual_fidelity = self.actual_fidelity() - logger.debug("Requested fidelity: %s calculated fidelity: %s actual fidelity %s", - self.requested_fidelity, self.calculated_fidelity, actual_fidelity) - if abs(self.calculated_fidelity - actual_fidelity) > 1.E-12: - logger.warning("Requested fidelity different from actual by %s", - self.calculated_fidelity - actual_fidelity) - if self.requested_fidelity and self.calculated_fidelity + 1.E-13 < self.requested_fidelity: - raise QiskitError(f"{self.__class__.__name__}: " - f"calculated fidelity: {self.calculated_fidelity} " - f"is worse than requested fidelity: {self.requested_fidelity}.") + logger.debug( + "Requested fidelity: %s calculated fidelity: %s actual fidelity %s", + self.requested_fidelity, + self.calculated_fidelity, + actual_fidelity, + ) + if abs(self.calculated_fidelity - actual_fidelity) > 1.0e-12: + logger.warning( + "Requested fidelity different from actual by %s", + self.calculated_fidelity - actual_fidelity, + ) + if self.requested_fidelity and self.calculated_fidelity + 1.0e-13 < self.requested_fidelity: + raise QiskitError( + f"{self.__class__.__name__}: " + f"calculated fidelity: {self.calculated_fidelity} " + f"is worse than requested fidelity: {self.requested_fidelity}." + ) def specialize(self): """Make changes to the decomposition to comply with any specialization. @@ -340,16 +353,19 @@ def specialize(self): __init__()""" raise NotImplementedError - def circuit(self, *, euler_basis: Optional[str] = None, - simplify=False, atol=DEFAULT_ATOL) -> QuantumCircuit: + def circuit( + self, *, euler_basis: Optional[str] = None, simplify=False, atol=DEFAULT_ATOL + ) -> QuantumCircuit: """Returns Weyl decomposition in circuit form. simplify, atol arguments are passed to OneQubitEulerDecomposer""" if euler_basis is None: euler_basis = self._default_1q_basis oneq_decompose = OneQubitEulerDecomposer(euler_basis) - c1l, c1r, c2l, c2r = (oneq_decompose(k, simplify=simplify, atol=atol) - for k in (self.K1l, self.K1r, self.K2l, self.K2r)) + c1l, c1r, c2l, c2r = ( + oneq_decompose(k, simplify=simplify, atol=atol) + for k in (self.K1l, self.K1r, self.K2l, self.K2r) + ) circ = QuantumCircuit(2, global_phase=self.global_phase) circ.compose(c2r, [0], inplace=True) circ.compose(c2l, [1], inplace=True) @@ -363,11 +379,11 @@ def _weyl_gate(self, simplify, circ: QuantumCircuit, atol): Can be overriden in subclasses for special cases""" if not simplify or abs(self.a) > atol: - circ.rxx(-self.a*2, 0, 1) + circ.rxx(-self.a * 2, 0, 1) if not simplify or abs(self.b) > atol: - circ.ryy(-self.b*2, 0, 1) + circ.ryy(-self.b * 2, 0, 1) if not simplify or abs(self.c) > atol: - circ.rzz(-self.c*2, 0, 1) + circ.rzz(-self.c * 2, 0, 1) def actual_fidelity(self, **kwargs) -> float: """Calculates the actual fidelity of the decomposed circuit to the input unitary""" @@ -376,26 +392,32 @@ def actual_fidelity(self, **kwargs) -> float: return trace_to_fid(trace) def __repr__(self): - """Represent with enough precision to allow copy-paste debugging of all corner cases - """ + """Represent with enough precision to allow copy-paste debugging of all corner cases""" prefix = f"{type(self).__qualname__}.from_bytes(" with io.BytesIO() as f: np.save(f, self.unitary_matrix, allow_pickle=False) b64 = base64.encodebytes(f.getvalue()).splitlines() b64ascii = [repr(x) for x in b64] b64ascii[-1] += "," - pretty = [f'# {x.rstrip()}' for x in str(self).splitlines()] - indent = '\n' + ' '*4 - lines = ([prefix] + pretty + b64ascii + - [f"requested_fidelity={self.requested_fidelity},", - f"calculated_fidelity={self.calculated_fidelity},", - f"actual_fidelity={self.actual_fidelity()},", - f"abc={(self.a, self.b, self.c)})"]) + pretty = [f"# {x.rstrip()}" for x in str(self).splitlines()] + indent = "\n" + " " * 4 + lines = ( + [prefix] + + pretty + + b64ascii + + [ + f"requested_fidelity={self.requested_fidelity},", + f"calculated_fidelity={self.calculated_fidelity},", + f"actual_fidelity={self.actual_fidelity()},", + f"abc={(self.a, self.b, self.c)})", + ] + ) return indent.join(lines) @classmethod - def from_bytes(cls, bytes_in: bytes, *, requested_fidelity: float, **kwargs - ) -> "TwoQubitWeylDecomposition": + def from_bytes( + cls, bytes_in: bytes, *, requested_fidelity: float, **kwargs + ) -> "TwoQubitWeylDecomposition": """Decode bytes into TwoQubitWeylDecomposition. Used by __repr__""" del kwargs # Unused (just for display) b64 = base64.decodebytes(bytes_in) @@ -418,7 +440,7 @@ class TwoQubitWeylIdEquiv(TwoQubitWeylDecomposition): """ def specialize(self): - self.a = self.b = self.c = 0. + self.a = self.b = self.c = 0.0 self.K1l = self.K1l @ self.K2l self.K1r = self.K1r @ self.K2r self.K2l = _id.copy() @@ -432,6 +454,7 @@ class TwoQubitWeylSWAPEquiv(TwoQubitWeylDecomposition): K2l = Id , K2r = Id . """ + def specialize(self): if self.c > 0: self.K1l = self.K1l @ self.K2r @@ -440,15 +463,15 @@ def specialize(self): self._is_flipped_from_original = True self.K1l = self.K1l @ _ipz @ self.K2r self.K1r = self.K1r @ _ipz @ self.K2l - self.global_phase = self.global_phase + np.pi/2 - self.a = self.b = self.c = np.pi/4 + self.global_phase = self.global_phase + np.pi / 2 + self.a = self.b = self.c = np.pi / 4 self.K2l = _id.copy() self.K2r = _id.copy() def _weyl_gate(self, simplify, circ: QuantumCircuit, atol): del self, simplify, atol # unused circ.swap(0, 1) - circ.global_phase -= 3*np.pi/4 + circ.global_phase -= 3 * np.pi / 4 def _closest_partial_swap(a, b, c) -> float: @@ -456,10 +479,10 @@ def _closest_partial_swap(a, b, c) -> float: trace distance for Ud(x, x, x) from Ud(a, b, c) """ m = (a + b + c) / 3 - am, bm, cm = a-m, b-m, c-m - ab, bc, ca = a-b, b-c, c-a + am, bm, cm = a - m, b - m, c - m + ab, bc, ca = a - b, b - c, c - a - return m + am * bm * cm * (6 + ab*ab + bc*bc * ca*ca) / 18 + return m + am * bm * cm * (6 + ab * ab + bc * bc * ca * ca) / 18 class TwoQubitWeylPartialSWAPEquiv(TwoQubitWeylDecomposition): @@ -468,6 +491,7 @@ class TwoQubitWeylPartialSWAPEquiv(TwoQubitWeylDecomposition): This gate binds 3 parameters, we make it canonical by setting: K2l = Id . """ + def specialize(self): self.a = self.b = self.c = _closest_partial_swap(self.a, self.b, self.c) self.K1l = self.K1l @ self.K2l @@ -495,8 +519,8 @@ def specialize(self): self.K2l = _id.copy() -_oneq_xyx = OneQubitEulerDecomposer('XYX') -_oneq_zyz = OneQubitEulerDecomposer('ZYZ') +_oneq_xyx = OneQubitEulerDecomposer("XYX") +_oneq_zyz = OneQubitEulerDecomposer("ZYZ") class TwoQubitWeylControlledEquiv(TwoQubitWeylDecomposition): @@ -506,7 +530,8 @@ class TwoQubitWeylControlledEquiv(TwoQubitWeylDecomposition): K2l = Ry(θl).Rx(λl) , K2r = Ry(θr).Rx(λr) . """ - _default_1q_basis = 'XYX' + + _default_1q_basis = "XYX" def specialize(self): self.b = self.c = 0 @@ -526,8 +551,9 @@ class TwoQubitWeylMirrorControlledEquiv(TwoQubitWeylDecomposition): K2l = Ry(θl).Rz(λl) , K2r = Ry(θr).Rz(λr) . """ + def specialize(self): - self.a = self.b = np.pi/4 + self.a = self.b = np.pi / 4 k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l) k2rtheta, k2rphi, k2rlambda, k2rphase = _oneq_zyz.angles_and_phase(self.K2r) self.global_phase += k2lphase + k2rphase @@ -538,8 +564,8 @@ def specialize(self): def _weyl_gate(self, simplify, circ: QuantumCircuit, atol): circ.swap(0, 1) - circ.rzz((np.pi/4 - self.c) * 2, 0, 1) - circ.global_phase += np.pi/4 + circ.rzz((np.pi / 4 - self.c) * 2, 0, 1) + circ.global_phase += np.pi / 4 # These next 3 gates use the definition of fSim from https://arxiv.org/pdf/2001.08343.pdf eq (1) @@ -549,8 +575,9 @@ class TwoQubitWeylfSimaabEquiv(TwoQubitWeylDecomposition): This gate binds 5 parameters, we make it canonical by setting: K2l = Ry(θl).Rz(λl) . """ + def specialize(self): - self.a = self.b = (self.a + self.b)/2 + self.a = self.b = (self.a + self.b) / 2 k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_zyz.angles_and_phase(self.K2l) self.global_phase += k2lphase self.K1r = self.K1r @ np.asarray(RZGate(k2lphi)) @@ -565,10 +592,11 @@ class TwoQubitWeylfSimabbEquiv(TwoQubitWeylDecomposition): This gate binds 5 parameters, we make it canonical by setting: K2l = Ry(θl).Rx(λl) . """ - _default_1q_basis = 'XYX' + + _default_1q_basis = "XYX" def specialize(self): - self.b = self.c = (self.b + self.c)/2 + self.b = self.c = (self.b + self.c) / 2 k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l) self.global_phase += k2lphase self.K1r = self.K1r @ np.asarray(RXGate(k2lphi)) @@ -584,10 +612,10 @@ class TwoQubitWeylfSimabmbEquiv(TwoQubitWeylDecomposition): K2l = Ry(θl).Rx(λl) . """ - _default_1q_basis = 'XYX' + _default_1q_basis = "XYX" def specialize(self): - self.b = (self.b - self.c)/2 + self.b = (self.b - self.c) / 2 self.c = -self.b k2ltheta, k2lphi, k2llambda, k2lphase = _oneq_xyx.angles_and_phase(self.K2l) self.global_phase += k2lphase @@ -603,24 +631,28 @@ class TwoQubitWeylGeneral(TwoQubitWeylDecomposition): This gate binds all 6 possible parameters, so there is no need to make the single-qubit pre-/post-gates canonical. """ + def specialize(self): pass # Nothing to do def Ud(a, b, c): - """Generates the array Exp(i(a xx + b yy + c zz)) - """ - return np.array([[cmath.exp(1j*c)*math.cos(a-b), 0, 0, 1j*cmath.exp(1j*c)*math.sin(a-b)], - [0, cmath.exp(-1j*c)*math.cos(a+b), 1j*cmath.exp(-1j*c)*math.sin(a+b), 0], - [0, 1j*cmath.exp(-1j*c)*math.sin(a+b), cmath.exp(-1j*c)*math.cos(a+b), 0], - [1j*cmath.exp(1j*c)*math.sin(a-b), 0, 0, cmath.exp(1j*c)*math.cos(a-b)]], - dtype=complex) + """Generates the array Exp(i(a xx + b yy + c zz))""" + return np.array( + [ + [cmath.exp(1j * c) * math.cos(a - b), 0, 0, 1j * cmath.exp(1j * c) * math.sin(a - b)], + [0, cmath.exp(-1j * c) * math.cos(a + b), 1j * cmath.exp(-1j * c) * math.sin(a + b), 0], + [0, 1j * cmath.exp(-1j * c) * math.sin(a + b), cmath.exp(-1j * c) * math.cos(a + b), 0], + [1j * cmath.exp(1j * c) * math.sin(a - b), 0, 0, cmath.exp(1j * c) * math.cos(a - b)], + ], + dtype=complex, + ) def trace_to_fid(trace): """Average gate fidelity is :math:`Fbar = (d + |Tr (Utarget \\cdot U^dag)|^2) / d(d+1)` M. Horodecki, P. Horodecki and R. Horodecki, PRA 60, 1888 (1999)""" - return (4 + abs(trace)**2)/20 + return (4 + abs(trace) ** 2) / 20 def rz_array(theta): @@ -628,11 +660,12 @@ def rz_array(theta): Rz(theta) = diag(exp(-i*theta/2),exp(i*theta/2)) """ - return np.array([[cmath.exp(-1j*theta/2.0), 0], - [0, cmath.exp(1j*theta/2.0)]], dtype=complex) + return np.array( + [[cmath.exp(-1j * theta / 2.0), 0], [0, cmath.exp(1j * theta / 2.0)]], dtype=complex + ) -class TwoQubitBasisDecomposer(): +class TwoQubitBasisDecomposer: """A class for decomposing 2-qubit unitaries into minimal number of uses of a 2-qubit basis gate. @@ -652,35 +685,82 @@ def __init__(self, gate, basis_fidelity=1.0, euler_basis=None): if euler_basis is not None: self._decomposer1q = OneQubitEulerDecomposer(euler_basis) else: - self._decomposer1q = OneQubitEulerDecomposer('U3') + self._decomposer1q = OneQubitEulerDecomposer("U3") # FIXME: find good tolerances - self.is_supercontrolled = math.isclose(basis.a, np.pi/4) and math.isclose(basis.c, 0.) + self.is_supercontrolled = math.isclose(basis.a, np.pi / 4) and math.isclose(basis.c, 0.0) # Create some useful matrices U1, U2, U3 are equivalent to the basis, # expand as Ui = Ki1.Ubasis.Ki2 b = basis.b - K11l = 1/(1+1j) * np.array([[-1j*cmath.exp(-1j*b), cmath.exp(-1j*b)], - [-1j*cmath.exp(1j*b), -cmath.exp(1j*b)]], dtype=complex) - K11r = 1/math.sqrt(2) * np.array([[1j*cmath.exp(-1j*b), -cmath.exp(-1j*b)], - [cmath.exp(1j*b), -1j*cmath.exp(1j*b)]], dtype=complex) - K12l = 1/(1+1j) * np.array([[1j, 1j], - [-1, 1]], dtype=complex) - K12r = 1/math.sqrt(2) * np.array([[1j, 1], - [-1, -1j]], dtype=complex) - K32lK21l = 1/math.sqrt(2) * np.array([[1+1j*np.cos(2*b), 1j*np.sin(2*b)], - [1j*np.sin(2*b), 1-1j*np.cos(2*b)]], dtype=complex) - K21r = 1/(1-1j) * np.array([[-1j*cmath.exp(-2j*b), cmath.exp(-2j*b)], - [1j*cmath.exp(2j*b), cmath.exp(2j*b)]], dtype=complex) - K22l = 1/math.sqrt(2) * np.array([[1, -1], - [1, 1]], dtype=complex) + K11l = ( + 1 + / (1 + 1j) + * np.array( + [ + [-1j * cmath.exp(-1j * b), cmath.exp(-1j * b)], + [-1j * cmath.exp(1j * b), -cmath.exp(1j * b)], + ], + dtype=complex, + ) + ) + K11r = ( + 1 + / math.sqrt(2) + * np.array( + [ + [1j * cmath.exp(-1j * b), -cmath.exp(-1j * b)], + [cmath.exp(1j * b), -1j * cmath.exp(1j * b)], + ], + dtype=complex, + ) + ) + K12l = 1 / (1 + 1j) * np.array([[1j, 1j], [-1, 1]], dtype=complex) + K12r = 1 / math.sqrt(2) * np.array([[1j, 1], [-1, -1j]], dtype=complex) + K32lK21l = ( + 1 + / math.sqrt(2) + * np.array( + [ + [1 + 1j * np.cos(2 * b), 1j * np.sin(2 * b)], + [1j * np.sin(2 * b), 1 - 1j * np.cos(2 * b)], + ], + dtype=complex, + ) + ) + K21r = ( + 1 + / (1 - 1j) + * np.array( + [ + [-1j * cmath.exp(-2j * b), cmath.exp(-2j * b)], + [1j * cmath.exp(2j * b), cmath.exp(2j * b)], + ], + dtype=complex, + ) + ) + K22l = 1 / math.sqrt(2) * np.array([[1, -1], [1, 1]], dtype=complex) K22r = np.array([[0, 1], [-1, 0]], dtype=complex) - K31l = 1/math.sqrt(2) * np.array([[cmath.exp(-1j*b), cmath.exp(-1j*b)], - [-cmath.exp(1j*b), cmath.exp(1j*b)]], dtype=complex) - K31r = 1j * np.array([[cmath.exp(1j*b), 0], - [0, -cmath.exp(-1j*b)]], dtype=complex) - K32r = 1/(1-1j) * np.array([[cmath.exp(1j*b), -cmath.exp(-1j*b)], - [-1j*cmath.exp(1j*b), -1j*cmath.exp(-1j*b)]], dtype=complex) + K31l = ( + 1 + / math.sqrt(2) + * np.array( + [[cmath.exp(-1j * b), cmath.exp(-1j * b)], [-cmath.exp(1j * b), cmath.exp(1j * b)]], + dtype=complex, + ) + ) + K31r = 1j * np.array([[cmath.exp(1j * b), 0], [0, -cmath.exp(-1j * b)]], dtype=complex) + K32r = ( + 1 + / (1 - 1j) + * np.array( + [ + [cmath.exp(1j * b), -cmath.exp(-1j * b)], + [-1j * cmath.exp(1j * b), -1j * cmath.exp(-1j * b)], + ], + dtype=complex, + ) + ) k1ld = basis.K1l.T.conj() k1rd = basis.K1r.T.conj() k2ld = basis.K2l.T.conj() @@ -712,13 +792,17 @@ def __init__(self, gate, basis_fidelity=1.0, euler_basis=None): # Decomposition into different number of gates # In the future could use different decomposition functions for different basis classes, etc if not self.is_supercontrolled: - warnings.warn("Only know how to decompose properly for supercontrolled basis gate. " - "This gate is ~Ud({}, {}, {})".format(basis.a, basis.b, basis.c), - stacklevel=2) - self.decomposition_fns = [self.decomp0, - self.decomp1, - self.decomp2_supercontrolled, - self.decomp3_supercontrolled] + warnings.warn( + "Only know how to decompose properly for supercontrolled basis gate. " + "This gate is ~Ud({}, {}, {})".format(basis.a, basis.b, basis.c), + stacklevel=2, + ) + self.decomposition_fns = [ + self.decomp0, + self.decomp1, + self.decomp2_supercontrolled, + self.decomp3_supercontrolled, + ] def traces(self, target): """Give the expected traces :math:`|Tr(U \\cdot Utarget^dag)|` for different number of @@ -728,12 +812,20 @@ def traces(self, target): # This doesn't come up if either c1==0 or c2==0 but otherwise be careful. ta, tb, tc = target.a, target.b, target.c bb = self.basis.b - return [4*complex(math.cos(ta)*math.cos(tb)*math.cos(tc), - math.sin(ta)*math.sin(tb)*math.sin(tc)), - 4*complex(math.cos(math.pi/4-ta)*math.cos(bb-tb)*math.cos(tc), - math.sin(math.pi/4-ta)*math.sin(bb-tb)*math.sin(tc)), - 4*math.cos(tc), - 4] + return [ + 4 + * complex( + math.cos(ta) * math.cos(tb) * math.cos(tc), + math.sin(ta) * math.sin(tb) * math.sin(tc), + ), + 4 + * complex( + math.cos(math.pi / 4 - ta) * math.cos(bb - tb) * math.cos(tc), + math.sin(math.pi / 4 - ta) * math.sin(bb - tb) * math.sin(tc), + ), + 4 * math.cos(tc), + 4, + ] @staticmethod def decomp0(target): @@ -780,8 +872,8 @@ def decomp2_supercontrolled(self, target): U0l = target.K1l.dot(self.q0l) U0r = target.K1r.dot(self.q0r) - U1l = self.q1la.dot(rz_array(-2*target.a)).dot(self.q1lb) - U1r = self.q1ra.dot(rz_array(2*target.b)).dot(self.q1rb) + U1l = self.q1la.dot(rz_array(-2 * target.a)).dot(self.q1lb) + U1r = self.q1ra.dot(rz_array(2 * target.b)).dot(self.q1rb) U2l = self.q2l.dot(target.K2l) U2r = self.q2r.dot(target.K2r) @@ -795,9 +887,9 @@ def decomp3_supercontrolled(self, target): U0l = target.K1l.dot(self.u0l) U0r = target.K1r.dot(self.u0r) U1l = self.u1l - U1r = self.u1ra.dot(rz_array(-2*target.c)).dot(self.u1rb) - U2l = self.u2la.dot(rz_array(-2*target.a)).dot(self.u2lb) - U2r = self.u2ra.dot(rz_array(2*target.b)).dot(self.u2rb) + U1r = self.u1ra.dot(rz_array(-2 * target.c)).dot(self.u1rb) + U2l = self.u2la.dot(rz_array(-2 * target.a)).dot(self.u2lb) + U2r = self.u2ra.dot(rz_array(2 * target.b)).dot(self.u2rb) U3l = self.u3l.dot(target.K2l) U3r = self.u3r.dot(target.K2r) @@ -814,7 +906,7 @@ def __call__(self, target, basis_fidelity=None, *, _num_basis_uses=None) -> Quan target_decomposed = TwoQubitWeylDecomposition(target) traces = self.traces(target_decomposed) - expected_fidelities = [trace_to_fid(traces[i]) * basis_fidelity**i for i in range(4)] + expected_fidelities = [trace_to_fid(traces[i]) * basis_fidelity ** i for i in range(4)] best_nbasis = int(np.argmax(expected_fidelities)) if _num_basis_uses is not None: @@ -829,26 +921,35 @@ def __call__(self, target, basis_fidelity=None, *, _num_basis_uses=None) -> Quan if best_nbasis == 2: return_circuit.global_phase += np.pi for i in range(best_nbasis): - return_circuit.compose(decomposition_euler[2*i], [q[0]], inplace=True) - return_circuit.compose(decomposition_euler[2*i+1], [q[1]], inplace=True) + return_circuit.compose(decomposition_euler[2 * i], [q[0]], inplace=True) + return_circuit.compose(decomposition_euler[2 * i + 1], [q[1]], inplace=True) return_circuit.append(self.gate, [q[0], q[1]]) - return_circuit.compose(decomposition_euler[2*best_nbasis], [q[0]], inplace=True) - return_circuit.compose(decomposition_euler[2*best_nbasis+1], [q[1]], inplace=True) + return_circuit.compose(decomposition_euler[2 * best_nbasis], [q[0]], inplace=True) + return_circuit.compose(decomposition_euler[2 * best_nbasis + 1], [q[1]], inplace=True) return return_circuit def num_basis_gates(self, unitary): - """ Computes the number of basis gates needed in + """Computes the number of basis gates needed in a decomposition of input unitary """ unitary = np.asarray(unitary, dtype=complex) a, b, c = weyl_coordinates(unitary)[:] - traces = [4*(math.cos(a)*math.cos(b)*math.cos(c)+1j*math.sin(a)*math.sin(b)*math.sin(c)), - 4*(math.cos(np.pi/4-a)*math.cos(self.basis.b-b)*math.cos(c) + - 1j*math.sin(np.pi/4-a)*math.sin(self.basis.b-b)*math.sin(c)), - 4*math.cos(c), - 4] - return np.argmax([trace_to_fid(traces[i]) * self.basis_fidelity**i for i in range(4)]) + traces = [ + 4 + * ( + math.cos(a) * math.cos(b) * math.cos(c) + + 1j * math.sin(a) * math.sin(b) * math.sin(c) + ), + 4 + * ( + math.cos(np.pi / 4 - a) * math.cos(self.basis.b - b) * math.cos(c) + + 1j * math.sin(np.pi / 4 - a) * math.sin(self.basis.b - b) * math.sin(c) + ), + 4 * math.cos(c), + 4, + ] + return np.argmax([trace_to_fid(traces[i]) * self.basis_fidelity ** i for i in range(4)]) two_qubit_cnot_decompose = TwoQubitBasisDecomposer(CXGate()) diff --git a/qiskit/quantum_info/synthesis/weyl.py b/qiskit/quantum_info/synthesis/weyl.py index 1c27e79dbc06..52922601371d 100644 --- a/qiskit/quantum_info/synthesis/weyl.py +++ b/qiskit/quantum_info/synthesis/weyl.py @@ -18,10 +18,9 @@ import scipy.linalg as la from qiskit.exceptions import QiskitError -_B = (1.0/np.sqrt(2)) * np.array([[1, 1j, 0, 0], - [0, 0, 1j, 1], - [0, 0, 1j, -1], - [1, -1j, 0, 0]], dtype=complex) +_B = (1.0 / np.sqrt(2)) * np.array( + [[1, 1j, 0, 0], [0, 0, 1j, 1], [0, 0, 1j, -1], [1, -1j, 0, 0]], dtype=complex +) _Bd = _B.T.conj() @@ -38,10 +37,10 @@ def weyl_coordinates(U): Raises: QiskitError: Computed coordinates not in Weyl chamber. """ - pi2 = np.pi/2 - pi4 = np.pi/4 + pi2 = np.pi / 2 + pi4 = np.pi / 4 - U = U / la.det(U)**(0.25) + U = U / la.det(U) ** (0.25) Up = _Bd.dot(U).dot(_B) M2 = Up.T.dot(Up) @@ -49,44 +48,46 @@ def weyl_coordinates(U): # P ∈ SO(4), D is diagonal with unit-magnitude elements. # D, P = la.eig(M2) # this can fail for certain kinds of degeneracy for _ in range(3): # FIXME: this randomized algorithm is horrendous - M2real = np.random.normal()*M2.real + np.random.normal()*M2.imag + M2real = np.random.normal() * M2.real + np.random.normal() * M2.imag _, P = la.eigh(M2real) D = P.T.dot(M2).dot(P).diagonal() if np.allclose(P.dot(np.diag(D)).dot(P.T), M2, rtol=1.0e-10, atol=1.0e-10): break else: - raise QiskitError("TwoQubitWeylDecomposition: failed to diagonalize M2. " - "Please submit this output to " - "https://github.com/Qiskit/qiskit-terra/issues/4159 " - "Input %s" % U.tolist()) + raise QiskitError( + "TwoQubitWeylDecomposition: failed to diagonalize M2. " + "Please submit this output to " + "https://github.com/Qiskit/qiskit-terra/issues/4159 " + "Input %s" % U.tolist() + ) - d = -np.angle(D)/2 - d[3] = -d[0]-d[1]-d[2] - cs = np.mod((d[:3]+d[3])/2, 2*np.pi) + d = -np.angle(D) / 2 + d[3] = -d[0] - d[1] - d[2] + cs = np.mod((d[:3] + d[3]) / 2, 2 * np.pi) # Reorder the eigenvalues to get in the Weyl chamber cstemp = np.mod(cs, pi2) - np.minimum(cstemp, pi2-cstemp, cstemp) + np.minimum(cstemp, pi2 - cstemp, cstemp) order = np.argsort(cstemp)[[1, 2, 0]] cs = cs[order] d[:3] = d[order] # Flip into Weyl chamber if cs[0] > pi2: - cs[0] -= 3*pi2 + cs[0] -= 3 * pi2 if cs[1] > pi2: - cs[1] -= 3*pi2 + cs[1] -= 3 * pi2 conjs = 0 if cs[0] > pi4: - cs[0] = pi2-cs[0] + cs[0] = pi2 - cs[0] conjs += 1 if cs[1] > pi4: - cs[1] = pi2-cs[1] + cs[1] = pi2 - cs[1] conjs += 1 if cs[2] > pi2: - cs[2] -= 3*pi2 + cs[2] -= 3 * pi2 if conjs == 1: - cs[2] = pi2-cs[2] + cs[2] = pi2 - cs[2] if cs[2] > pi4: cs[2] -= pi2 diff --git a/qiskit/result/counts.py b/qiskit/result/counts.py index 3224252e27ce..1b2bc79ad3f7 100644 --- a/qiskit/result/counts.py +++ b/qiskit/result/counts.py @@ -27,10 +27,9 @@ class Counts(dict): """A class to store a counts result from a circuit execution.""" - bitstring_regex = re.compile(r'^[01\s]+$') + bitstring_regex = re.compile(r"^[01\s]+$") - def __init__(self, data, time_taken=None, creg_sizes=None, - memory_slots=None): + def __init__(self, data, time_taken=None, creg_sizes=None, memory_slots=None): """Build a counts object Args: @@ -71,18 +70,14 @@ def __init__(self, data, time_taken=None, creg_sizes=None, first_key = next(iter(data.keys())) if isinstance(first_key, int): self.int_raw = data - self.hex_raw = { - hex(key): value for key, value in self.int_raw.items()} + self.hex_raw = {hex(key): value for key, value in self.int_raw.items()} elif isinstance(first_key, str): - if first_key.startswith('0x'): + if first_key.startswith("0x"): self.hex_raw = data - self.int_raw = { - int(key, 0): value for key, value in self.hex_raw.items()} - elif first_key.startswith('0b'): - self.int_raw = { - int(key, 0): value for key, value in data.items()} - self.hex_raw = { - hex(key): value for key, value in self.int_raw.items()} + self.int_raw = {int(key, 0): value for key, value in self.hex_raw.items()} + elif first_key.startswith("0b"): + self.int_raw = {int(key, 0): value for key, value in data.items()} + self.hex_raw = {hex(key): value for key, value in self.int_raw.items()} else: if not creg_sizes and not memory_slots: self.hex_raw = None @@ -94,24 +89,27 @@ def __init__(self, data, time_taken=None, creg_sizes=None, for bitstring, value in data.items(): if not self.bitstring_regex.search(bitstring): raise exceptions.QiskitError( - 'Counts objects with dit strings do not ' - 'currently support dit string formatting parameters ' - 'creg_sizes or memory_slots') + "Counts objects with dit strings do not " + "currently support dit string formatting parameters " + "creg_sizes or memory_slots" + ) int_key = self._remove_space_underscore(bitstring) int_dict[int_key] = value hex_dict[hex(int_key)] = value self.hex_raw = hex_dict self.int_raw = int_dict else: - raise TypeError("Invalid input key type %s, must be either an int " - "key or string key with hexademical value or bit string") + raise TypeError( + "Invalid input key type %s, must be either an int " + "key or string key with hexademical value or bit string" + ) header = {} self.creg_sizes = creg_sizes if self.creg_sizes: - header['creg_sizes'] = self.creg_sizes + header["creg_sizes"] = self.creg_sizes self.memory_slots = memory_slots if self.memory_slots: - header['memory_slots'] = self.memory_slots + header["memory_slots"] = self.memory_slots if not bin_data: bin_data = postprocess.format_counts(self.hex_raw, header=header) super().__init__(bin_data) @@ -127,14 +125,13 @@ def most_frequent(self): an empty object. """ if not self: - raise exceptions.QiskitError( - "Can not return a most frequent count on an empty object") + raise exceptions.QiskitError("Can not return a most frequent count on an empty object") max_value = max(self.values()) max_values_counts = [x[0] for x in self.items() if x[1] == max_value] if len(max_values_counts) != 1: raise exceptions.QiskitError( - "Multiple values have the same maximum counts: %s" % - ','.join(max_values_counts)) + "Multiple values have the same maximum counts: %s" % ",".join(max_values_counts) + ) return max_values_counts[0] def hex_outcomes(self): @@ -153,8 +150,9 @@ def hex_outcomes(self): for bitstring, value in self.items(): if not self.bitstring_regex.search(bitstring): raise exceptions.QiskitError( - 'Counts objects with dit strings do not ' - 'currently support conversion to hexadecimal') + "Counts objects with dit strings do not " + "currently support conversion to hexadecimal" + ) int_key = self._remove_space_underscore(bitstring) out_dict[hex(int_key)] = value return out_dict @@ -174,8 +172,9 @@ def int_outcomes(self): for bitstring, value in self.items(): if not self.bitstring_regex.search(bitstring): raise exceptions.QiskitError( - 'Counts objects with dit strings do not ' - 'currently support conversion to integer') + "Counts objects with dit strings do not " + "currently support conversion to integer" + ) int_key = self._remove_space_underscore(bitstring) out_dict[int_key] = value return out_dict diff --git a/qiskit/result/exceptions.py b/qiskit/result/exceptions.py index b0dadc078820..3c33daffa05a 100644 --- a/qiskit/result/exceptions.py +++ b/qiskit/result/exceptions.py @@ -30,10 +30,11 @@ class ResultError(QiskitError): 'message': 'Your credits are not enough.', 'code': 'MAX_CREDITS_EXCEEDED'} """ + def __init__(self, error): - super().__init__(error['message']) - self.status = error['status'] - self.code = error['code'] + super().__init__(error["message"]) + self.status = error["status"] + self.code = error["code"] def __str__(self): - return '{}: {}'.format(self.code, self.message) + return "{}: {}".format(self.code, self.message) diff --git a/qiskit/result/models.py b/qiskit/result/models.py index ba8cfd9eafce..d46d9cc63266 100644 --- a/qiskit/result/models.py +++ b/qiskit/result/models.py @@ -22,8 +22,9 @@ class ExperimentResultData: """Class representing experiment result data""" - def __init__(self, counts=None, snapshots=None, memory=None, - statevector=None, unitary=None, **kwargs): + def __init__( + self, counts=None, snapshots=None, memory=None, statevector=None, unitary=None, **kwargs + ): """Initialize an ExperimentalResult Data class Args: @@ -43,19 +44,19 @@ def __init__(self, counts=None, snapshots=None, memory=None, """ self._data_attributes = [] if counts is not None: - self._data_attributes.append('counts') + self._data_attributes.append("counts") self.counts = counts if snapshots is not None: - self._data_attributes.append('snapshots') + self._data_attributes.append("snapshots") self.snapshots = snapshots if memory is not None: - self._data_attributes.append('memory') + self._data_attributes.append("memory") self.memory = memory if statevector is not None: - self._data_attributes.append('statevector') + self._data_attributes.append("statevector") self.statevector = statevector if unitary is not None: - self._data_attributes.append('unitary') + self._data_attributes.append("unitary") self.unitary = unitary for key, value in kwargs.items(): setattr(self, key, value) @@ -65,7 +66,7 @@ def __repr__(self): string_list = [] for field in self._data_attributes: string_list.append(f"{field}={getattr(self, field)}") - out = "ExperimentResultData(%s)" % ', '.join(string_list) + out = "ExperimentResultData(%s)" % ", ".join(string_list) return out def to_dict(self): @@ -107,9 +108,18 @@ class ExperimentResult: _metadata = {} - def __init__(self, shots, success, data, meas_level=MeasLevel.CLASSIFIED, - status=None, seed=None, meas_return=None, header=None, - **kwargs): + def __init__( + self, + shots, + success, + data, + meas_level=MeasLevel.CLASSIFIED, + status=None, + seed=None, + meas_return=None, + header=None, + **kwargs, + ): """Initialize an ExperimentResult object. Args: @@ -142,20 +152,24 @@ def __init__(self, shots, success, data, meas_level=MeasLevel.CLASSIFIED, self.seed = seed if meas_return is not None: if meas_return not in list(MeasReturnType): - raise QiskitError('%s not a valid meas_return value') + raise QiskitError("%s not a valid meas_return value") self.meas_return = meas_return self._metadata.update(kwargs) def __repr__(self): out = "ExperimentResult(shots=%s, success=%s, meas_level=%s, data=%s" % ( - self.shots, self.success, self.meas_level, self.data) - if hasattr(self, 'header'): + self.shots, + self.success, + self.meas_level, + self.data, + ) + if hasattr(self, "header"): out += ", header=%s" % self.header - if hasattr(self, 'status'): + if hasattr(self, "status"): out += ", status=%s" % self.status - if hasattr(self, 'seed'): + if hasattr(self, "seed"): out += ", seed=%s" % self.seed - if hasattr(self, 'meas_return'): + if hasattr(self, "meas_return"): out += ", meas_return=%s" % self.meas_return for key in self._metadata: if isinstance(self._metadata[key], str): @@ -163,14 +177,14 @@ def __repr__(self): else: value_str = repr(self._metadata[key]) out += ", %s=%s" % (key, value_str) - out += ')' + out += ")" return out def __getattr__(self, name): try: return self._metadata[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex def to_dict(self): """Return a dictionary format representation of the ExperimentResult @@ -179,19 +193,19 @@ def to_dict(self): dict: The dictionary form of the ExperimentResult """ out_dict = { - 'shots': self.shots, - 'success': self.success, - 'data': self.data.to_dict(), - 'meas_level': self.meas_level, + "shots": self.shots, + "success": self.success, + "data": self.data.to_dict(), + "meas_level": self.meas_level, } - if hasattr(self, 'header'): - out_dict['header'] = self.header.to_dict() - if hasattr(self, 'status'): - out_dict['status'] = self.status - if hasattr(self, 'seed'): - out_dict['seed'] = self.seed - if hasattr(self, 'meas_return'): - out_dict['meas_return'] = self.meas_return + if hasattr(self, "header"): + out_dict["header"] = self.header.to_dict() + if hasattr(self, "status"): + out_dict["status"] = self.status + if hasattr(self, "seed"): + out_dict["seed"] = self.seed + if hasattr(self, "meas_return"): + out_dict["meas_return"] = self.meas_return out_dict.update(self._metadata) return out_dict @@ -210,11 +224,10 @@ def from_dict(cls, data): """ in_data = copy.copy(data) - data_obj = ExperimentResultData.from_dict(in_data.pop('data')) - if 'header' in in_data: - in_data['header'] = QobjExperimentHeader.from_dict( - in_data.pop('header')) - shots = in_data.pop('shots') - success = in_data.pop('success') + data_obj = ExperimentResultData.from_dict(in_data.pop("data")) + if "header" in in_data: + in_data["header"] = QobjExperimentHeader.from_dict(in_data.pop("header")) + shots = in_data.pop("shots") + success = in_data.pop("success") return cls(shots, success, data_obj, **in_data) diff --git a/qiskit/result/postprocess.py b/qiskit/result/postprocess.py index 1dd7e47441c5..d8495adbf101 100644 --- a/qiskit/result/postprocess.py +++ b/qiskit/result/postprocess.py @@ -30,7 +30,7 @@ def _bin_to_hex(bitstring): def _pad_zeros(bitstring, memory_slots): """If the bitstring is truncated, pad extra zeros to make its length equal to memory_slots""" - return format(int(bitstring, 2), '0{}b'.format(memory_slots)) + return format(int(bitstring, 2), "0{}b".format(memory_slots)) def _separate_bitstring(bitstring, creg_sizes): @@ -38,9 +38,9 @@ def _separate_bitstring(bitstring, creg_sizes): substrings = [] running_index = 0 for _, size in reversed(creg_sizes): - substrings.append(bitstring[running_index: running_index + size]) + substrings.append(bitstring[running_index : running_index + size]) running_index += size - return ' '.join(substrings) + return " ".join(substrings) def format_counts_memory(shot_memory, header=None): @@ -62,11 +62,11 @@ def format_counts_memory(shot_memory, header=None): Returns: dict: a formatted memory """ - if shot_memory.startswith('0x'): + if shot_memory.startswith("0x"): shot_memory = _hex_to_bin(shot_memory) if header: - creg_sizes = header.get('creg_sizes', None) - memory_slots = header.get('memory_slots', None) + creg_sizes = header.get("creg_sizes", None) + memory_slots = header.get("memory_slots", None) if memory_slots: shot_memory = _pad_zeros(shot_memory, memory_slots) if creg_sizes and memory_slots: @@ -88,13 +88,13 @@ def _list_to_complex_array(complex_list): """ arr = np.asarray(complex_list, dtype=np.complex_) if not arr.shape[-1] == 2: - raise QiskitError('Inner most nested list is not of length 2.') + raise QiskitError("Inner most nested list is not of length 2.") - return arr[..., 0] + 1j*arr[..., 1] + return arr[..., 0] + 1j * arr[..., 1] def format_level_0_memory(memory): - """ Format an experiment result memory object for measurement level 0. + """Format an experiment result memory object for measurement level 0. Args: memory (list): Memory from experiment with `meas_level==1`. `avg` or @@ -110,12 +110,12 @@ def format_level_0_memory(memory): formatted_memory = _list_to_complex_array(memory) # infer meas_return from shape of returned data. if not 2 <= len(formatted_memory.shape) <= 3: - raise QiskitError('Level zero memory is not of correct shape.') + raise QiskitError("Level zero memory is not of correct shape.") return formatted_memory def format_level_1_memory(memory): - """ Format an experiment result memory object for measurement level 1. + """Format an experiment result memory object for measurement level 1. Args: memory (list): Memory from experiment with `meas_level==1`. `avg` or @@ -131,12 +131,12 @@ def format_level_1_memory(memory): formatted_memory = _list_to_complex_array(memory) # infer meas_return from shape of returned data. if not 1 <= len(formatted_memory.shape) <= 2: - raise QiskitError('Level one memory is not of correct shape.') + raise QiskitError("Level one memory is not of correct shape.") return formatted_memory def format_level_2_memory(memory, header=None): - """ Format an experiment result memory object for measurement level 2. + """Format an experiment result memory object for measurement level 2. Args: memory (list): Memory from experiment with `meas_level==2` and `memory==True`. diff --git a/qiskit/result/result.py b/qiskit/result/result.py index b886708d70e2..33b1a942f436 100644 --- a/qiskit/result/result.py +++ b/qiskit/result/result.py @@ -42,8 +42,19 @@ class Result: _metadata = {} - def __init__(self, backend_name, backend_version, qobj_id, job_id, success, - results, date=None, status=None, header=None, **kwargs): + def __init__( + self, + backend_name, + backend_version, + qobj_id, + job_id, + success, + results, + date=None, + status=None, + header=None, + **kwargs, + ): self._metadata = {} self.backend_name = backend_name self.backend_version = backend_version @@ -60,16 +71,23 @@ def __init__(self, backend_name, backend_version, qobj_id, job_id, success, self._metadata.update(kwargs) def __repr__(self): - out = ("Result(backend_name='%s', backend_version='%s', qobj_id='%s', " - "job_id='%s', success=%s, results=%s" % ( - self.backend_name, - self.backend_version, self.qobj_id, self.job_id, self.success, - self.results)) - if hasattr(self, 'date'): + out = ( + "Result(backend_name='%s', backend_version='%s', qobj_id='%s', " + "job_id='%s', success=%s, results=%s" + % ( + self.backend_name, + self.backend_version, + self.qobj_id, + self.job_id, + self.success, + self.results, + ) + ) + if hasattr(self, "date"): out += ", date=%s" % self.date - if hasattr(self, 'status'): + if hasattr(self, "status"): out += ", status=%s" % self.status - if hasattr(self, 'header'): + if hasattr(self, "header"): out += ", status=%s" % self.header for key in self._metadata: if isinstance(self._metadata[key], str): @@ -77,7 +95,7 @@ def __repr__(self): else: value_str = repr(self._metadata[key]) out += ", %s=%s" % (key, value_str) - out += ')' + out += ")" return out def to_dict(self): @@ -87,19 +105,19 @@ def to_dict(self): dict: The dictionary form of the Result """ out_dict = { - 'backend_name': self.backend_name, - 'backend_version': self.backend_version, - 'qobj_id': self.qobj_id, - 'job_id': self.job_id, - 'success': self.success, - 'results': [x.to_dict() for x in self.results] + "backend_name": self.backend_name, + "backend_version": self.backend_version, + "qobj_id": self.qobj_id, + "job_id": self.job_id, + "success": self.success, + "results": [x.to_dict() for x in self.results], } - if hasattr(self, 'date'): - out_dict['date'] = self.date - if hasattr(self, 'status'): - out_dict['status'] = self.status - if hasattr(self, 'header'): - out_dict['header'] = self.header.to_dict() + if hasattr(self, "date"): + out_dict["date"] = self.date + if hasattr(self, "status"): + out_dict["status"] = self.status + if hasattr(self, "header"): + out_dict["header"] = self.header.to_dict() out_dict.update(self._metadata) return out_dict @@ -107,7 +125,7 @@ def __getattr__(self, name): try: return self._metadata[name] except KeyError as ex: - raise AttributeError(f'Attribute {name} is not defined') from ex + raise AttributeError(f"Attribute {name} is not defined") from ex @classmethod def from_dict(cls, data): @@ -123,10 +141,9 @@ def from_dict(cls, data): """ in_data = copy.copy(data) - in_data['results'] = [ - ExperimentResult.from_dict(x) for x in in_data.pop('results')] - if 'header' in in_data: - in_data['header'] = QobjHeader.from_dict(in_data.pop('header')) + in_data["results"] = [ExperimentResult.from_dict(x) for x in in_data.pop("results")] + if "header" in in_data: + in_data["header"] = QobjHeader.from_dict(in_data.pop("header")) return cls(**in_data) def data(self, experiment=None): @@ -217,7 +234,7 @@ def get_memory(self, experiment=None): meas_level = exp_result.meas_level - memory = self.data(experiment)['memory'] + memory = self.data(experiment)["memory"] if meas_level == MeasLevel.CLASSIFIED: return postprocess.format_level_2_memory(memory, header) @@ -226,14 +243,14 @@ def get_memory(self, experiment=None): elif meas_level == MeasLevel.RAW: return postprocess.format_level_0_memory(memory) else: - raise QiskitError('Measurement level {} is not supported'.format(meas_level)) + raise QiskitError("Measurement level {} is not supported".format(meas_level)) except KeyError as ex: raise QiskitError( 'No memory for experiment "{}". ' - 'Please verify that you either ran a measurement level 2 job ' + "Please verify that you either ran a measurement level 2 job " 'with the memory flag set, eg., "memory=True", ' - 'or a measurement level 0/1 job.'.format(repr(experiment)) + "or a measurement level 0/1 job.".format(repr(experiment)) ) from ex def get_counts(self, experiment=None): @@ -266,16 +283,18 @@ def get_counts(self, experiment=None): except (AttributeError, QiskitError): # header is not available header = None - if 'counts' in self.data(key).keys(): + if "counts" in self.data(key).keys(): if header: counts_header = { - k: v for k, v in header.items() if k in { - 'time_taken', 'creg_sizes', 'memory_slots'}} + k: v + for k, v in header.items() + if k in {"time_taken", "creg_sizes", "memory_slots"} + } else: counts_header = {} - dict_list.append(Counts(self.data(key)['counts'], **counts_header)) - elif 'statevector' in self.data(key).keys(): - vec = postprocess.format_statevector(self.data(key)['statevector']) + dict_list.append(Counts(self.data(key)["counts"], **counts_header)) + elif "statevector" in self.data(key).keys(): + vec = postprocess.format_statevector(self.data(key)["statevector"]) dict_list.append(statevector.Statevector(vec).probabilities_dict(decimals=15)) else: raise QiskitError('No counts for experiment "{}"'.format(repr(key))) @@ -302,8 +321,9 @@ def get_statevector(self, experiment=None, decimals=None): QiskitError: if there is no statevector for the experiment. """ try: - return postprocess.format_statevector(self.data(experiment)['statevector'], - decimals=decimals) + return postprocess.format_statevector( + self.data(experiment)["statevector"], decimals=decimals + ) except KeyError as ex: raise QiskitError(f'No statevector for experiment "{repr(experiment)}"') from ex @@ -324,8 +344,7 @@ def get_unitary(self, experiment=None, decimals=None): QiskitError: if there is no unitary for the experiment. """ try: - return postprocess.format_unitary(self.data(experiment)['unitary'], - decimals=decimals) + return postprocess.format_unitary(self.data(experiment)["unitary"], decimals=decimals) except KeyError as ex: raise QiskitError(f'No unitary for experiment "{repr(experiment)}"') from ex @@ -347,8 +366,9 @@ def _get_experiment(self, key=None): if key is None: if len(self.results) != 1: raise QiskitError( - 'You have to select a circuit or schedule when there is more than ' - 'one available') + "You have to select a circuit or schedule when there is more than " + "one available" + ) key = 0 # Key is a QuantumCircuit/Schedule or str: retrieve result by name. @@ -362,26 +382,28 @@ def _get_experiment(self, key=None): raise QiskitError(f'Result for experiment "{key}" could not be found.') from ex else: # Look into `result[x].header.name` for the names. - exp = [result for result in self.results - if getattr(getattr(result, 'header', None), - 'name', '') == key] + exp = [ + result + for result in self.results + if getattr(getattr(result, "header", None), "name", "") == key + ] if len(exp) == 0: - raise QiskitError('Data for experiment "%s" could not be found.' % - key) + raise QiskitError('Data for experiment "%s" could not be found.' % key) if len(exp) == 1: exp = exp[0] else: warnings.warn( 'Result object contained multiple results matching name "%s", ' - 'only first match will be returned. Use an integer index to ' - 'retrieve results for all entries.' % key) + "only first match will be returned. Use an integer index to " + "retrieve results for all entries." % key + ) exp = exp[0] # Check that the retrieved experiment was successful - if getattr(exp, 'success', False): + if getattr(exp, "success", False): return exp # If unsuccessful check experiment and result status and raise exception - result_status = getattr(self, 'status', 'Result was not successful') - exp_status = getattr(exp, 'status', 'Experiment was not successful') + result_status = getattr(self, "status", "Result was not successful") + exp_status = getattr(exp, "status", "Experiment was not successful") raise QiskitError(result_status, ", ", exp_status) diff --git a/qiskit/result/utils.py b/qiskit/result/utils.py index 1e58c467bcdf..ade48d8e1c93 100644 --- a/qiskit/result/utils.py +++ b/qiskit/result/utils.py @@ -74,7 +74,7 @@ def _adjust_creg_sizes(creg_sizes, indices): # Get creg num values and then convert to the cumulative last index per creg. # e.g. [2, 1, 3] => [1, 2, 5] creg_nums = [x for _, x in creg_sizes] - creg_limits = [sum(creg_nums[0:x:1])-1 for x in range(0, len(creg_nums)+1)][1:] + creg_limits = [sum(creg_nums[0:x:1]) - 1 for x in range(0, len(creg_nums) + 1)][1:] # Now iterate over indices and find which creg that index is in. # When found increment the creg size @@ -92,7 +92,7 @@ def _adjust_creg_sizes(creg_sizes, indices): def _marginalize(counts, indices=None): """Get the marginal counts for the given set of indices""" - num_clbits = len(next(iter(counts)).replace(' ', '')) + num_clbits = len(next(iter(counts)).replace(" ", "")) # Check if we do not need to marginalize and if so, trim # whitespace and '_' and return @@ -104,7 +104,7 @@ def _marginalize(counts, indices=None): return ret if not indices or not set(indices).issubset(set(range(num_clbits))): - raise QiskitError('indices must be in range [0, {}].'.format(num_clbits-1)) + raise QiskitError("indices must be in range [0, {}].".format(num_clbits - 1)) # Sort the indices to keep in descending order # Since bitstrings have qubit-0 as least significant bit @@ -113,7 +113,7 @@ def _marginalize(counts, indices=None): # Build the return list new_counts = Counter() for key, val in counts.items(): - new_key = ''.join([_remove_space_underscore(key)[-idx-1] for idx in indices]) + new_key = "".join([_remove_space_underscore(key)[-idx - 1] for idx in indices]) new_counts[new_key] += val return dict(new_counts) @@ -123,16 +123,17 @@ def _format_marginal(counts, marg_counts, indices): multiple cregs and non-indices.""" format_counts = {} counts_template = next(iter(counts)) - counts_len = len(counts_template.replace(' ', '')) + counts_len = len(counts_template.replace(" ", "")) indices_rev = sorted(indices, reverse=True) for count in marg_counts: index_dict = dict(zip(indices_rev, count)) - count_bits = ''.join([index_dict[index] if index in index_dict else '_' - for index in range(counts_len)])[::-1] + count_bits = "".join( + [index_dict[index] if index in index_dict else "_" for index in range(counts_len)] + )[::-1] for index, bit in enumerate(counts_template): - if bit == ' ': - count_bits = count_bits[:index] + ' ' + count_bits[index:] + if bit == " ": + count_bits = count_bits[:index] + " " + count_bits[index:] format_counts[count_bits] = marg_counts[count] return format_counts diff --git a/qiskit/scheduler/config.py b/qiskit/scheduler/config.py index 4598ec439783..b8e2d5ac2425 100644 --- a/qiskit/scheduler/config.py +++ b/qiskit/scheduler/config.py @@ -18,13 +18,10 @@ from qiskit.pulse.utils import format_meas_map -class ScheduleConfig(): +class ScheduleConfig: """Configuration for pulse scheduling.""" - def __init__(self, - inst_map: InstructionScheduleMap, - meas_map: List[List[int]], - dt: float): + def __init__(self, inst_map: InstructionScheduleMap, meas_map: List[List[int]], dt: float): """ Container for information needed to schedule a QuantumCircuit into a pulse Schedule. diff --git a/qiskit/scheduler/lowering.py b/qiskit/scheduler/lowering.py index 977811194ea1..dc1d2bf1a8c5 100644 --- a/qiskit/scheduler/lowering.py +++ b/qiskit/scheduler/lowering.py @@ -29,9 +29,10 @@ from qiskit.pulse.macros import measure from qiskit.scheduler.config import ScheduleConfig -CircuitPulseDef = namedtuple('CircuitPulseDef', [ - 'schedule', # The schedule which implements the quantum circuit command - 'qubits']) # The labels of the qubits involved in the command according to the circuit +CircuitPulseDef = namedtuple( + "CircuitPulseDef", + ["schedule", "qubits"], # The schedule which implements the quantum circuit command +) # The labels of the qubits involved in the command according to the circuit def lower_gates(circuit: QuantumCircuit, schedule_config: ScheduleConfig) -> List[CircuitPulseDef]: @@ -77,11 +78,14 @@ def get_measure_schedule(qubit_mem_slots: Dict[int, int]) -> CircuitPulseDef: meas_q = circuit.calibrations[Measure().name][((qubit,), params)] meas_q = target_qobj_transform(meas_q) acquire_q = meas_q.filter(channels=[AcquireChannel(qubit)]) - mem_slot_index = [chan.index for chan in acquire_q.channels - if isinstance(chan, MemorySlot)][0] + mem_slot_index = [ + chan.index for chan in acquire_q.channels if isinstance(chan, MemorySlot) + ][0] if mem_slot_index != qubit_mem_slots[qubit]: - raise KeyError("The measurement calibration is not defined on " - "the requested classical bits") + raise KeyError( + "The measurement calibration is not defined on " + "the requested classical bits" + ) sched |= meas_q del qubit_mem_slots[qubit] acquire_excludes[qubit] = mem_slot_index @@ -91,18 +95,22 @@ def get_measure_schedule(qubit_mem_slots: Dict[int, int]) -> CircuitPulseDef: if qubit_mem_slots: qubits = list(qubit_mem_slots.keys()) qubit_mem_slots.update(acquire_excludes) - meas_sched = measure(qubits=qubits, - inst_map=inst_map, - meas_map=schedule_config.meas_map, - qubit_mem_slots=qubit_mem_slots) + meas_sched = measure( + qubits=qubits, + inst_map=inst_map, + meas_map=schedule_config.meas_map, + qubit_mem_slots=qubit_mem_slots, + ) meas_sched = target_qobj_transform(meas_sched) - meas_sched = meas_sched.exclude(channels=[AcquireChannel(qubit) for qubit - in acquire_excludes]) + meas_sched = meas_sched.exclude( + channels=[AcquireChannel(qubit) for qubit in acquire_excludes] + ) sched |= meas_sched qubit_mem_slots.clear() - return CircuitPulseDef(schedule=sched, - qubits=[chan.index for chan in sched.channels - if isinstance(chan, AcquireChannel)]) + return CircuitPulseDef( + schedule=sched, + qubits=[chan.index for chan in sched.channels if isinstance(chan, AcquireChannel)], + ) qubit_indices = {bit: idx for idx, bit in enumerate(circuit.qubits)} clbit_indices = {bit: idx for idx, bit in enumerate(circuit.clbits)} @@ -123,18 +131,24 @@ def get_measure_schedule(qubit_mem_slots: Dict[int, int]) -> CircuitPulseDef: sched += pulse_inst.Delay(duration=inst.duration, channel=channel(qubit)) circ_pulse_defs.append(CircuitPulseDef(schedule=sched, qubits=inst_qubits)) elif isinstance(inst, Measure): - if (len(inst_qubits) != 1 and len(clbits) != 1): - raise QiskitError("Qubit '{}' or classical bit '{}' errored because the " - "circuit Measure instruction only takes one of " - "each.".format(inst_qubits, clbits)) + if len(inst_qubits) != 1 and len(clbits) != 1: + raise QiskitError( + "Qubit '{}' or classical bit '{}' errored because the " + "circuit Measure instruction only takes one of " + "each.".format(inst_qubits, clbits) + ) qubit_mem_slots[inst_qubits[0]] = clbit_indices[clbits[0]] else: try: gate_cals = circuit.calibrations[inst.name] - schedule = gate_cals[( - tuple(inst_qubits), - tuple(p if getattr(p, 'parameters', None) else float(p) for p in inst.params), - )] + schedule = gate_cals[ + ( + tuple(inst_qubits), + tuple( + p if getattr(p, "parameters", None) else float(p) for p in inst.params + ), + ) + ] schedule = target_qobj_transform(schedule) circ_pulse_defs.append(CircuitPulseDef(schedule=schedule, qubits=inst_qubits)) continue @@ -144,8 +158,7 @@ def get_measure_schedule(qubit_mem_slots: Dict[int, int]) -> CircuitPulseDef: try: schedule = inst_map.get(inst, inst_qubits, *inst.params) schedule = target_qobj_transform(schedule) - circ_pulse_defs.append( - CircuitPulseDef(schedule=schedule, qubits=inst_qubits)) + circ_pulse_defs.append(CircuitPulseDef(schedule=schedule, qubits=inst_qubits)) except PulseError as ex: raise QiskitError( f"Operation '{inst.name}' on qubit(s) {inst_qubits} not supported by the " diff --git a/qiskit/scheduler/methods/basic.py b/qiskit/scheduler/methods/basic.py index 322786794859..a9268a9560d8 100644 --- a/qiskit/scheduler/methods/basic.py +++ b/qiskit/scheduler/methods/basic.py @@ -24,8 +24,7 @@ from qiskit.scheduler.lowering import lower_gates -def as_soon_as_possible(circuit: QuantumCircuit, - schedule_config: ScheduleConfig) -> Schedule: +def as_soon_as_possible(circuit: QuantumCircuit, schedule_config: ScheduleConfig) -> Schedule: """ Return the pulse Schedule which implements the input circuit using an "as soon as possible" (asap) scheduling policy. @@ -61,13 +60,15 @@ def update_times(inst_qubits: List[int], time: int = 0) -> None: start_times.append(start_time) update_times(circ_pulse_def.qubits, stop_time) - timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) - if not isinstance(cpd.schedule, Barrier)] + timed_schedules = [ + (time, cpd.schedule) + for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier) + ] return Schedule(*timed_schedules, name=circuit.name, metadata=circuit.metadata) -def as_late_as_possible(circuit: QuantumCircuit, - schedule_config: ScheduleConfig) -> Schedule: +def as_late_as_possible(circuit: QuantumCircuit, schedule_config: ScheduleConfig) -> Schedule: """ Return the pulse Schedule which implements the input circuit using an "as late as possible" (alap) scheduling policy. @@ -108,6 +109,9 @@ def update_times(inst_qubits: List[int], time: int = 0) -> None: last_stop = max(t for t in qubit_time_available.values()) if qubit_time_available else 0 start_times = [last_stop - t for t in reversed(rev_stop_times)] - timed_schedules = [(time, cpd.schedule) for time, cpd in zip(start_times, circ_pulse_defs) - if not isinstance(cpd.schedule, Barrier)] + timed_schedules = [ + (time, cpd.schedule) + for time, cpd in zip(start_times, circ_pulse_defs) + if not isinstance(cpd.schedule, Barrier) + ] return Schedule(*timed_schedules, name=circuit.name, metadata=circuit.metadata) diff --git a/qiskit/scheduler/schedule_circuit.py b/qiskit/scheduler/schedule_circuit.py index 4b22345c30eb..2830cd53937c 100644 --- a/qiskit/scheduler/schedule_circuit.py +++ b/qiskit/scheduler/schedule_circuit.py @@ -21,9 +21,9 @@ from qiskit.scheduler.methods import as_soon_as_possible, as_late_as_possible -def schedule_circuit(circuit: QuantumCircuit, - schedule_config: ScheduleConfig, - method: Optional[str] = None) -> Schedule: +def schedule_circuit( + circuit: QuantumCircuit, schedule_config: ScheduleConfig, method: Optional[str] = None +) -> Schedule: """ Basic scheduling pass from a circuit to a pulse Schedule, using the backend. If no method is specified, then a basic, as late as possible scheduling pass is performed, i.e. pulses are @@ -48,13 +48,13 @@ def schedule_circuit(circuit: QuantumCircuit, QiskitError: If method isn't recognized. """ methods = { - 'as_soon_as_possible': as_soon_as_possible, - 'asap': as_soon_as_possible, - 'as_late_as_possible': as_late_as_possible, - 'alap': as_late_as_possible + "as_soon_as_possible": as_soon_as_possible, + "asap": as_soon_as_possible, + "as_late_as_possible": as_late_as_possible, + "alap": as_late_as_possible, } if method is None: - method = 'as_late_as_possible' + method = "as_late_as_possible" try: return methods[method](circuit, schedule_config) except KeyError as ex: diff --git a/qiskit/scheduler/sequence.py b/qiskit/scheduler/sequence.py index f9dd48921f2b..a04c045a1952 100644 --- a/qiskit/scheduler/sequence.py +++ b/qiskit/scheduler/sequence.py @@ -77,9 +77,11 @@ def _meas_start_time(): delay_overlaps_meas = False for q in circ_pulse_def.qubits: qubit_time_available[q] = stop_time - if meas_time is not None \ - and circ_pulse_def.schedule.name == "delay" \ - and stop_time > meas_time: + if ( + meas_time is not None + and circ_pulse_def.schedule.name == "delay" + and stop_time > meas_time + ): qubit_time_available[q] = meas_time delay_overlaps_meas = True # skip to delays overlapping measures and barriers diff --git a/qiskit/scheduler/utils.py b/qiskit/scheduler/utils.py index 368581b50eac..6a782120f7d3 100644 --- a/qiskit/scheduler/utils.py +++ b/qiskit/scheduler/utils.py @@ -16,11 +16,12 @@ from qiskit.utils.deprecation import deprecate_function from qiskit.pulse import macros, utils -format_meas_map = deprecate_function( - '"format_meas_map" has been moved to "qiskit.pulse.utils"')(utils.format_meas_map) +format_meas_map = deprecate_function('"format_meas_map" has been moved to "qiskit.pulse.utils"')( + utils.format_meas_map +) -measure = deprecate_function( - '"measure" has been moved to "qiskit.pulse.macros"')(macros.measure) +measure = deprecate_function('"measure" has been moved to "qiskit.pulse.macros"')(macros.measure) -measure_all = deprecate_function( - '"measure_all" has been moved to "qiskit.pulse.macros"')(macros.measure_all) +measure_all = deprecate_function('"measure_all" has been moved to "qiskit.pulse.macros"')( + macros.measure_all +) diff --git a/qiskit/test/base.py b/qiskit/test/base.py index 5afc2d007663..b089d301a344 100644 --- a/qiskit/test/base.py +++ b/qiskit/test/base.py @@ -29,10 +29,12 @@ import sys import unittest from unittest.util import safe_repr + try: import fixtures from testtools.compat import advance_iterator from testtools import content + HAS_FIXTURES = True except ImportError: HAS_FIXTURES = False @@ -79,13 +81,14 @@ def gather_details(source_dict, target_dict): new_name = name disambiguator = itertools.count(1) while new_name in target_dict: - new_name = '%s-%d' % (name, advance_iterator(disambiguator)) + new_name = "%s-%d" % (name, advance_iterator(disambiguator)) name = new_name target_dict[name] = _copy_content(content_object) class BaseQiskitTestCase(unittest.TestCase): """Common extra functionality on top of unittest.""" + @staticmethod def _get_resource_path(filename, path=Path.TEST): """Get the absolute path to a resource. @@ -99,8 +102,9 @@ def _get_resource_path(filename, path=Path.TEST): """ return os.path.normpath(os.path.join(path.value, filename)) - def assertDictAlmostEqual(self, dict1, dict2, delta=None, msg=None, - places=None, default_value=0): + def assertDictAlmostEqual( + self, dict1, dict2, delta=None, msg=None, places=None, default_value=0 + ): """Assert two dictionaries with numeric values are almost equal. Fail if the two dictionaries are unequal as determined by @@ -142,9 +146,9 @@ def setUpClass(cls): # Set logging to file and stdout if the LOG_LEVEL envar is set. cls.log = logging.getLogger(cls.__name__) - if os.getenv('LOG_LEVEL'): - filename = '%s.log' % os.path.splitext(inspect.getfile(cls))[0] - setup_test_logging(cls.log, os.getenv('LOG_LEVEL'), filename) + if os.getenv("LOG_LEVEL"): + filename = "%s.log" % os.path.splitext(inspect.getfile(cls))[0] + setup_test_logging(cls.log, os.getenv("LOG_LEVEL"), filename) def tearDown(self): # Reset the default providers, as in practice they acts as a singleton @@ -164,7 +168,8 @@ def __init__(self, *args, **kwargs): if not HAS_FIXTURES: raise ImportError( "Test runner requirements testtools and fixtures are missing. " - "Install them with 'pip install testtools fixtures'") + "Install them with 'pip install testtools fixtures'" + ) super().__init__(*args, **kwargs) self.__RunTest = self.run_tests_with self._reset() @@ -173,7 +178,8 @@ def __init__(self, *args, **kwargs): (unittest.SkipTest, self._report_skip), (self.failureException, self._report_failure), (unittest.case._UnexpectedSuccess, self._report_unexpected_success), - (Exception, self._report_error)] + (Exception, self._report_error), + ] def _reset(self): """Reset the test case as if it had never been run.""" @@ -186,13 +192,12 @@ def _reset(self): self.__teardown_called = False self.__details = None - def onException(self, exc_info, tb_label='traceback'): + def onException(self, exc_info, tb_label="traceback"): """Called when an exception propagates from test code. :seealso addOnException: """ - if exc_info[0] not in [ - unittest.SkipTest, unittest.case._UnexpectedSuccess]: + if exc_info[0] not in [unittest.SkipTest, unittest.case._UnexpectedSuccess]: self._report_traceback(exc_info, tb_label=tb_label) for handler in self.__exception_handlers: handler(exc_info) @@ -206,11 +211,11 @@ def _run_teardown(self, result): "TestCase.tearDown was not called. Have you upcalled all the " "way up the hierarchy from your tearDown? e.g. Call " "super(%s, self).tearDown() from your tearDown()." - % (sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__)) + % (sys.modules[self.__class__.__module__].__file__, self.__class__.__name__) + ) def _get_test_method(self): - method_name = getattr(self, '_testMethodName') + method_name = getattr(self, "_testMethodName") return getattr(self, method_name) def _run_test_method(self, result): @@ -236,8 +241,7 @@ def useFixture(self, fixture): try: fixture.setUp() except MultipleExceptions as e: - if (fixtures is not None and - e.args[-1][0] is fixtures.fixture.SetupError): + if fixtures is not None and e.args[-1][0] is fixtures.fixture.SetupError: gather_details(e.args[-1][1].args[0], self.getDetails()) raise except Exception: @@ -248,8 +252,7 @@ def useFixture(self, fixture): # the fixture. Ideally this whole try/except is not # really needed any more, however, we keep this code to # remain compatible with the older setUp(). - if (hasattr(fixture, '_details') and - fixture._details is not None): + if hasattr(fixture, "_details") and fixture._details is not None: gather_details(fixture.getDetails(), self.getDetails()) except Exception: # Report the setUp exception, then raise the error during @@ -266,8 +269,7 @@ def reraise(exc_class, exc_obj, exc_tb, _marker=object()): reraise(*exc_info) else: self.addCleanup(fixture.cleanUp) - self.addCleanup( - gather_details, fixture.getDetails(), self.getDetails()) + self.addCleanup(gather_details, fixture.getDetails(), self.getDetails()) return fixture def _run_setup(self, result): @@ -279,11 +281,11 @@ def _run_setup(self, result): "TestCase.setUp was not called. Have you upcalled all the " "way up the hierarchy from your setUp? e.g. Call " "super(%s, self).setUp() from your setUp()." - % (sys.modules[self.__class__.__module__].__file__, - self.__class__.__name__)) + % (sys.modules[self.__class__.__module__].__file__, self.__class__.__name__) + ) def _add_reason(self, reason): - self.addDetail('reason', content.text_content(reason)) + self.addDetail("reason", content.text_content(reason)) @staticmethod def _report_error(self, result, err): @@ -306,18 +308,20 @@ def _report_skip(self, result, err): self._add_reason(reason) result.addSkip(self, details=self.getDetails()) - def _report_traceback(self, exc_info, tb_label='traceback'): - id_gen = self._traceback_id_gens.setdefault( - tb_label, itertools.count(0)) + def _report_traceback(self, exc_info, tb_label="traceback"): + id_gen = self._traceback_id_gens.setdefault(tb_label, itertools.count(0)) while True: tb_id = advance_iterator(id_gen) if tb_id: - tb_label = '%s-%d' % (tb_label, tb_id) + tb_label = "%s-%d" % (tb_label, tb_id) if tb_label not in self.getDetails(): break - self.addDetail(tb_label, content.TracebackContent( - exc_info, self, capture_locals=getattr( - self, '__testtools_tb_locals__', False))) + self.addDetail( + tb_label, + content.TracebackContent( + exc_info, self, capture_locals=getattr(self, "__testtools_tb_locals__", False) + ), + ) @staticmethod def _report_unexpected_success(self, result, err): @@ -326,8 +330,7 @@ def _report_unexpected_success(self, result, err): def run(self, result=None): self._reset() try: - run_test = self.__RunTest( - self, self.exception_handlers, last_resort=self._report_error) + run_test = self.__RunTest(self, self.exception_handlers, last_resort=self._report_error) except TypeError: # Backwards compat: if we can't call the constructor # with last_resort, try without that. @@ -341,16 +344,15 @@ def setUp(self): "In File: %s\n" "TestCase.setUp was already called. Do not explicitly call " "setUp from your tests. In your own setUp, use super to call " - "the base setUp." - % (sys.modules[self.__class__.__module__].__file__,)) + "the base setUp." % (sys.modules[self.__class__.__module__].__file__,) + ) self.__setup_called = True - if os.environ.get('QISKIT_TEST_CAPTURE_STREAMS'): - stdout = self.useFixture(fixtures.StringStream('stdout')).stream - self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) - stderr = self.useFixture(fixtures.StringStream('stderr')).stream - self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) - self.useFixture(fixtures.LoggerFixture(nuke_handlers=False, - level=None)) + if os.environ.get("QISKIT_TEST_CAPTURE_STREAMS"): + stdout = self.useFixture(fixtures.StringStream("stdout")).stream + self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout)) + stderr = self.useFixture(fixtures.StringStream("stderr")).stream + self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) + self.useFixture(fixtures.LoggerFixture(nuke_handlers=False, level=None)) def tearDown(self): super().tearDown() @@ -359,8 +361,8 @@ def tearDown(self): "In File: %s\n" "TestCase.tearDown was already called. Do not explicitly call " "tearDown from your tests. In your own tearDown, use super to " - "call the base tearDown." - % (sys.modules[self.__class__.__module__].__file__,)) + "call the base tearDown." % (sys.modules[self.__class__.__module__].__file__,) + ) self.__teardown_called = True # Reset the default providers, as in practice they acts as a singleton # due to importing the instances from the top-level qiskit namespace. @@ -447,32 +449,30 @@ def valid_comparison(value): # Check arguments. if dict1 == dict2: - return '' + return "" if places is not None: if delta is not None: raise TypeError("specify delta or places not both") - msg_suffix = ' within %s places' % places + msg_suffix = " within %s places" % places else: delta = delta or 1e-8 - msg_suffix = ' within %s delta' % delta + msg_suffix = " within %s delta" % delta # Compare all keys in both dicts, populating error_msg. - error_msg = '' + error_msg = "" for key in set(dict1.keys()) | set(dict2.keys()): val1 = dict1.get(key, default_value) val2 = dict2.get(key, default_value) if not valid_comparison(abs(val1 - val2)): - error_msg += '(%s: %s != %s), ' % (safe_repr(key), - safe_repr(val1), - safe_repr(val2)) + error_msg += "(%s: %s != %s), " % (safe_repr(key), safe_repr(val1), safe_repr(val2)) if error_msg: return error_msg[:-2] + msg_suffix else: - return '' + return "" -if not HAS_FIXTURES and not os.environ.get('QISKIT_TEST_CAPTURE_STREAMS'): +if not HAS_FIXTURES and not os.environ.get("QISKIT_TEST_CAPTURE_STREAMS"): QiskitTestCase = BasicQiskitTestCase else: QiskitTestCase = FullQiskitTestCase diff --git a/qiskit/test/decorators.py b/qiskit/test/decorators.py index ede9dcfc2326..40f22c648215 100644 --- a/qiskit/test/decorators.py +++ b/qiskit/test/decorators.py @@ -54,7 +54,7 @@ def is_aer_provider_available(): bool: True if simulator executable is available """ # TODO: HACK FROM THE DEPTHS OF DESPAIR AS AER DOES NOT WORK ON MAC - if sys.platform == 'darwin': + if sys.platform == "darwin": return False try: import qiskit.providers.aer # pylint: disable=unused-import @@ -72,7 +72,7 @@ def requires_aer_provider(test_item): Returns: callable: the decorated function. """ - reason = 'Aer provider not found, skipping test' + reason = "Aer provider not found, skipping test" return unittest.skipIf(not is_aer_provider_available(), reason)(test_item) @@ -88,9 +88,9 @@ def slow_test(func): @functools.wraps(func) def _wrapper(*args, **kwargs): - skip_slow = not TEST_OPTIONS['run_slow'] + skip_slow = not TEST_OPTIONS["run_slow"] if skip_slow: - raise unittest.SkipTest('Skipping slow tests') + raise unittest.SkipTest("Skipping slow tests") return func(*args, **kwargs) @@ -107,17 +107,18 @@ def _get_credentials(): SkipTest: when credentials can't be found """ try: - from qiskit.providers.ibmq.credentials import (Credentials, - discover_credentials) + from qiskit.providers.ibmq.credentials import Credentials, discover_credentials except ImportError as ex: - raise unittest.SkipTest('qiskit-ibmq-provider could not be found, ' - 'and is required for executing online tests. ' - 'To install, run "pip install qiskit-ibmq-provider" ' - 'or check your installation.') from ex - - if os.getenv('IBMQ_TOKEN') and os.getenv('IBMQ_URL'): - return Credentials(os.getenv('IBMQ_TOKEN'), os.getenv('IBMQ_URL')) - elif os.getenv('QISKIT_TESTS_USE_CREDENTIALS_FILE'): + raise unittest.SkipTest( + "qiskit-ibmq-provider could not be found, " + "and is required for executing online tests. " + 'To install, run "pip install qiskit-ibmq-provider" ' + "or check your installation." + ) from ex + + if os.getenv("IBMQ_TOKEN") and os.getenv("IBMQ_URL"): + return Credentials(os.getenv("IBMQ_TOKEN"), os.getenv("IBMQ_URL")) + elif os.getenv("QISKIT_TESTS_USE_CREDENTIALS_FILE"): # Attempt to read the standard credentials. discovered_credentials = discover_credentials() @@ -127,29 +128,31 @@ def _get_credentials(): raise unittest.SkipTest( "More than 1 credential set found, use: " "IBMQ_TOKEN and IBMQ_URL env variables to " - "set credentials explicitly") + "set credentials explicitly" + ) # Use the first available credentials. return list(discovered_credentials.values())[0] raise unittest.SkipTest( - 'No IBMQ credentials found for running the test. This is required for ' - 'running online tests.') + "No IBMQ credentials found for running the test. This is required for " + "running online tests." + ) def requires_qe_access(func): """Deprecated in favor of `online_test`""" - warn("`requires_qe_access` is going to be replaced in favor of `online_test`", - DeprecationWarning) + warn( + "`requires_qe_access` is going to be replaced in favor of `online_test`", DeprecationWarning + ) @functools.wraps(func) def _wrapper(self, *args, **kwargs): - if TEST_OPTIONS['skip_online']: - raise unittest.SkipTest('Skipping online tests') + if TEST_OPTIONS["skip_online"]: + raise unittest.SkipTest("Skipping online tests") credentials = _get_credentials() self.using_ibmq_credentials = credentials.is_ibmq() - kwargs.update({'qe_token': credentials.token, - 'qe_url': credentials.url}) + kwargs.update({"qe_token": credentials.token, "qe_url": credentials.url}) return func(self, *args, **kwargs) @@ -182,19 +185,18 @@ def _wrapper(self, *args, **kwargs): # To avoid checking the connection in each test global HAS_NET_CONNECTION # pylint: disable=global-statement - if TEST_OPTIONS['skip_online']: - raise unittest.SkipTest('Skipping online tests') + if TEST_OPTIONS["skip_online"]: + raise unittest.SkipTest("Skipping online tests") if HAS_NET_CONNECTION is None: - HAS_NET_CONNECTION = _has_connection('qiskit.org', 443) + HAS_NET_CONNECTION = _has_connection("qiskit.org", 443) if not HAS_NET_CONNECTION: raise unittest.SkipTest("Test requires internet connection.") credentials = _get_credentials() self.using_ibmq_credentials = credentials.is_ibmq() - kwargs.update({'qe_token': credentials.token, - 'qe_url': credentials.url}) + kwargs.update({"qe_token": credentials.token, "qe_url": credentials.url}) return func(self, *args, **kwargs) diff --git a/qiskit/test/ibmq_mock.py b/qiskit/test/ibmq_mock.py index 649082cce5a6..a854f926b766 100644 --- a/qiskit/test/ibmq_mock.py +++ b/qiskit/test/ibmq_mock.py @@ -36,9 +36,7 @@ def mock_get_backend(backend): mock_ibmq = MagicMock() mock_provider = MagicMock() if not hasattr(backend_mocks, backend): - raise NameError( - 'The specified backend name is not a valid mock from ' - 'qiskit.test.mock') + raise NameError("The specified backend name is not a valid mock from " "qiskit.test.mock") fake_backend = getattr(backend_mocks, backend)() mock_provider.get_backend.return_value = fake_backend mock_ibmq.get_provider.return_value = mock_provider diff --git a/qiskit/test/mock/backends/__init__.py b/qiskit/test/mock/backends/__init__.py index a93fa6f13ae3..037a91e3cf97 100644 --- a/qiskit/test/mock/backends/__init__.py +++ b/qiskit/test/mock/backends/__init__.py @@ -51,6 +51,7 @@ from .valencia import FakeValencia from .vigo import FakeVigo from .yorktown import FakeYorktown + # Legacy Backends from .almaden import FakeLegacyAlmaden from .armonk import FakeLegacyArmonk diff --git a/qiskit/test/mock/backends/almaden/fake_almaden.py b/qiskit/test/mock/backends/almaden/fake_almaden.py index e05bd733ad95..5c5f8987cdb2 100644 --- a/qiskit/test/mock/backends/almaden/fake_almaden.py +++ b/qiskit/test/mock/backends/almaden/fake_almaden.py @@ -21,13 +21,13 @@ class FakeAlmaden(fake_pulse_backend.FakePulseBackend): """A fake Almaden backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) @@ -40,13 +40,13 @@ class FakeAlmaden(fake_pulse_backend.FakePulseBackend): class FakeLegacyAlmaden(fake_pulse_backend.FakePulseLegacyBackend): """A fake Almaden backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/armonk/fake_armonk.py b/qiskit/test/mock/backends/armonk/fake_armonk.py index 5bc609054e46..afaf2727fd9b 100644 --- a/qiskit/test/mock/backends/armonk/fake_armonk.py +++ b/qiskit/test/mock/backends/armonk/fake_armonk.py @@ -21,7 +21,7 @@ class FakeArmonk(fake_pulse_backend.FakePulseBackend): """A fake 1 qubit backend. - 0 + 0 """ dirname = os.path.dirname(__file__) @@ -34,7 +34,7 @@ class FakeArmonk(fake_pulse_backend.FakePulseBackend): class FakeLegacyArmonk(fake_pulse_backend.FakePulseLegacyBackend): """A fake 1 qubit backend. - 0 + 0 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/boeblingen/fake_boeblingen.py b/qiskit/test/mock/backends/boeblingen/fake_boeblingen.py index 04efb5240236..0397da2ed433 100644 --- a/qiskit/test/mock/backends/boeblingen/fake_boeblingen.py +++ b/qiskit/test/mock/backends/boeblingen/fake_boeblingen.py @@ -21,13 +21,13 @@ class FakeBoeblingen(fake_pulse_backend.FakePulseBackend): """A fake Boeblingen backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) @@ -40,13 +40,13 @@ class FakeBoeblingen(fake_pulse_backend.FakePulseBackend): class FakeLegacyBoeblingen(fake_pulse_backend.FakePulseLegacyBackend): """A fake Boeblingen backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/burlington/fake_burlington.py b/qiskit/test/mock/backends/burlington/fake_burlington.py index 865654cd1aff..69d94be68095 100644 --- a/qiskit/test/mock/backends/burlington/fake_burlington.py +++ b/qiskit/test/mock/backends/burlington/fake_burlington.py @@ -21,9 +21,9 @@ class FakeBurlington(fake_qasm_backend.FakeQasmBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) @@ -35,9 +35,9 @@ class FakeBurlington(fake_qasm_backend.FakeQasmBackend): class FakeLegacyBurlington(fake_qasm_backend.FakeQasmLegacyBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/cambridge/fake_cambridge.py b/qiskit/test/mock/backends/cambridge/fake_cambridge.py index 2e4c0276492e..17f24edaf24d 100644 --- a/qiskit/test/mock/backends/cambridge/fake_cambridge.py +++ b/qiskit/test/mock/backends/cambridge/fake_cambridge.py @@ -21,15 +21,15 @@ class FakeCambridge(fake_qasm_backend.FakeQasmBackend): """A fake Cambridge backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 06 - ↕ ↕ - 07 ↔ 08 ↔ 09 ↔ 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 ↔ 15 - ↕ ↕ ↕ - 16 17 18 - ↕ ↕ ↕ - 19 ↔ 20 ↔ 21 ↔ 22 ↔ 23 ↔ 24 ↔ 25 ↔ 26 ↔ 27 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 06 + ↕ ↕ + 07 ↔ 08 ↔ 09 ↔ 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 ↔ 15 + ↕ ↕ ↕ + 16 17 18 + ↕ ↕ ↕ + 19 ↔ 20 ↔ 21 ↔ 22 ↔ 23 ↔ 24 ↔ 25 ↔ 26 ↔ 27 """ dirname = os.path.dirname(__file__) @@ -45,21 +45,21 @@ class FakeCambridgeAlternativeBasis(FakeCambridge): def __init__(self): super().__init__() - self._configuration.basis_gates = ['u', 'sx', 'p', 'cx', 'id'] + self._configuration.basis_gates = ["u", "sx", "p", "cx", "id"] class FakeLegacyCambridge(fake_qasm_backend.FakeQasmLegacyBackend): """A fake Cambridge backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 06 - ↕ ↕ - 07 ↔ 08 ↔ 09 ↔ 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 ↔ 15 - ↕ ↕ ↕ - 16 17 18 - ↕ ↕ ↕ - 19 ↔ 20 ↔ 21 ↔ 22 ↔ 23 ↔ 24 ↔ 25 ↔ 26 ↔ 27 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 06 + ↕ ↕ + 07 ↔ 08 ↔ 09 ↔ 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 ↔ 15 + ↕ ↕ ↕ + 16 17 18 + ↕ ↕ ↕ + 19 ↔ 20 ↔ 21 ↔ 22 ↔ 23 ↔ 24 ↔ 25 ↔ 26 ↔ 27 """ dirname = os.path.dirname(__file__) @@ -75,4 +75,4 @@ class FakeLegacyCambridgeAlternativeBasis(FakeLegacyCambridge): def __init__(self): super().__init__() - self._configuration.basis_gates = ['u', 'sx', 'p', 'cx', 'id'] + self._configuration.basis_gates = ["u", "sx", "p", "cx", "id"] diff --git a/qiskit/test/mock/backends/essex/fake_essex.py b/qiskit/test/mock/backends/essex/fake_essex.py index 98a16e5d9109..bfb4f6cba01f 100644 --- a/qiskit/test/mock/backends/essex/fake_essex.py +++ b/qiskit/test/mock/backends/essex/fake_essex.py @@ -21,11 +21,11 @@ class FakeEssex(fake_qasm_backend.FakeQasmBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 2 - ↕ - 3 - ↕ - 4 + 0 ↔ 1 ↔ 2 + ↕ + 3 + ↕ + 4 """ dirname = os.path.dirname(__file__) @@ -37,11 +37,11 @@ class FakeEssex(fake_qasm_backend.FakeQasmBackend): class FakeLegacyEssex(fake_qasm_backend.FakeQasmLegacyBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 2 - ↕ - 3 - ↕ - 4 + 0 ↔ 1 ↔ 2 + ↕ + 3 + ↕ + 4 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/johannesburg/fake_johannesburg.py b/qiskit/test/mock/backends/johannesburg/fake_johannesburg.py index 20716105f5a4..ee3567afcce0 100644 --- a/qiskit/test/mock/backends/johannesburg/fake_johannesburg.py +++ b/qiskit/test/mock/backends/johannesburg/fake_johannesburg.py @@ -21,13 +21,13 @@ class FakeJohannesburg(fake_pulse_backend.FakePulseBackend): """A fake Johannesburg backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) @@ -40,13 +40,13 @@ class FakeJohannesburg(fake_pulse_backend.FakePulseBackend): class FakeLegacyJohannesburg(fake_pulse_backend.FakePulseLegacyBackend): """A fake Johannesburg backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/london/fake_london.py b/qiskit/test/mock/backends/london/fake_london.py index 5fc245cacab1..5f644906ac45 100644 --- a/qiskit/test/mock/backends/london/fake_london.py +++ b/qiskit/test/mock/backends/london/fake_london.py @@ -21,11 +21,11 @@ class FakeLondon(fake_qasm_backend.FakeQasmBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 2 - ↕ - 3 - ↕ - 4 + 0 ↔ 1 ↔ 2 + ↕ + 3 + ↕ + 4 """ dirname = os.path.dirname(__file__) @@ -37,11 +37,11 @@ class FakeLondon(fake_qasm_backend.FakeQasmBackend): class FakeLegacyLondon(fake_qasm_backend.FakeQasmLegacyBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 2 - ↕ - 3 - ↕ - 4 + 0 ↔ 1 ↔ 2 + ↕ + 3 + ↕ + 4 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/melbourne/fake_melbourne.py b/qiskit/test/mock/backends/melbourne/fake_melbourne.py index 0183c4010476..576629a76083 100644 --- a/qiskit/test/mock/backends/melbourne/fake_melbourne.py +++ b/qiskit/test/mock/backends/melbourne/fake_melbourne.py @@ -17,8 +17,7 @@ import os import json -from qiskit.providers.models import (GateConfig, QasmBackendConfiguration, - BackendProperties) +from qiskit.providers.models import GateConfig, QasmBackendConfiguration, BackendProperties from qiskit.test.mock.fake_backend import FakeBackend, FakeLegacyBackend @@ -27,19 +26,36 @@ class FakeMelbourne(FakeBackend): def __init__(self): """ - 0 ← 1 → 2 → 3 ← 4 ← 5 → 6 - ↑ ↑ ↑ ↓ ↓ ↓ - 13 → 12 ← 11 → 10 ← 9 → 8 ← 7 + 0 ← 1 → 2 → 3 ← 4 ← 5 → 6 + ↑ ↑ ↑ ↓ ↓ ↓ + 13 → 12 ← 11 → 10 ← 9 → 8 ← 7 """ - cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], - [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], - [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] + cmap = [ + [1, 0], + [1, 2], + [2, 3], + [4, 3], + [4, 10], + [5, 4], + [5, 6], + [5, 9], + [6, 8], + [7, 8], + [9, 8], + [9, 10], + [11, 3], + [11, 10], + [11, 12], + [12, 2], + [13, 1], + [13, 12], + ] configuration = QasmBackendConfiguration( - backend_name='fake_melbourne', - backend_version='0.0.0', + backend_name="fake_melbourne", + backend_version="0.0.0", n_qubits=14, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -47,7 +63,7 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) @@ -67,19 +83,36 @@ class FakeLegacyMelbourne(FakeLegacyBackend): def __init__(self): """ - 0 ← 1 → 2 → 3 ← 4 ← 5 → 6 - ↑ ↑ ↑ ↓ ↓ ↓ - 13 → 12 ← 11 → 10 ← 9 → 8 ← 7 + 0 ← 1 → 2 → 3 ← 4 ← 5 → 6 + ↑ ↑ ↑ ↓ ↓ ↓ + 13 → 12 ← 11 → 10 ← 9 → 8 ← 7 """ - cmap = [[1, 0], [1, 2], [2, 3], [4, 3], [4, 10], [5, 4], - [5, 6], [5, 9], [6, 8], [7, 8], [9, 8], [9, 10], - [11, 3], [11, 10], [11, 12], [12, 2], [13, 1], [13, 12]] + cmap = [ + [1, 0], + [1, 2], + [2, 3], + [4, 3], + [4, 10], + [5, 4], + [5, 6], + [5, 9], + [6, 8], + [7, 8], + [9, 8], + [9, 10], + [11, 3], + [11, 10], + [11, 12], + [12, 2], + [13, 1], + [13, 12], + ] configuration = QasmBackendConfiguration( - backend_name='fake_melbourne', - backend_version='0.0.0', + backend_name="fake_melbourne", + backend_version="0.0.0", n_qubits=14, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -87,7 +120,7 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) diff --git a/qiskit/test/mock/backends/ourense/fake_ourense.py b/qiskit/test/mock/backends/ourense/fake_ourense.py index a4c302b578ed..08d10706ddca 100644 --- a/qiskit/test/mock/backends/ourense/fake_ourense.py +++ b/qiskit/test/mock/backends/ourense/fake_ourense.py @@ -21,9 +21,9 @@ class FakeOurense(fake_qasm_backend.FakeQasmBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) @@ -35,9 +35,9 @@ class FakeOurense(fake_qasm_backend.FakeQasmBackend): class FakeLegacyOurense(fake_qasm_backend.FakeQasmLegacyBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/poughkeepsie/fake_poughkeepsie.py b/qiskit/test/mock/backends/poughkeepsie/fake_poughkeepsie.py index d6e2571d21be..7cd11f655dc6 100644 --- a/qiskit/test/mock/backends/poughkeepsie/fake_poughkeepsie.py +++ b/qiskit/test/mock/backends/poughkeepsie/fake_poughkeepsie.py @@ -17,8 +17,7 @@ import os import json -from qiskit.providers.models import (GateConfig, QasmBackendConfiguration, - BackendProperties) +from qiskit.providers.models import GateConfig, QasmBackendConfiguration, BackendProperties from qiskit.test.mock.fake_backend import FakeBackend, FakeLegacyBackend @@ -27,26 +26,68 @@ class FakePoughkeepsie(FakeBackend): def __init__(self): """ - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ - cmap = [[0, 1], [0, 5], [1, 0], [1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3], [4, 9], - [5, 0], [5, 6], [5, 10], [6, 5], [6, 7], [7, 6], [7, 8], [7, 12], [8, 7], [8, 9], - [9, 4], [9, 8], [9, 14], [10, 5], [10, 11], [10, 15], [11, 10], [11, 12], [12, 7], - [12, 11], [12, 13], [13, 12], [13, 14], [14, 9], [14, 13], [14, 19], [15, 10], - [15, 16], [16, 15], [16, 17], [17, 16], [17, 18], [18, 17], [18, 19], [19, 14], - [19, 18]] + cmap = [ + [0, 1], + [0, 5], + [1, 0], + [1, 2], + [2, 1], + [2, 3], + [3, 2], + [3, 4], + [4, 3], + [4, 9], + [5, 0], + [5, 6], + [5, 10], + [6, 5], + [6, 7], + [7, 6], + [7, 8], + [7, 12], + [8, 7], + [8, 9], + [9, 4], + [9, 8], + [9, 14], + [10, 5], + [10, 11], + [10, 15], + [11, 10], + [11, 12], + [12, 7], + [12, 11], + [12, 13], + [13, 12], + [13, 14], + [14, 9], + [14, 13], + [14, 19], + [15, 10], + [15, 16], + [16, 15], + [16, 17], + [17, 16], + [17, 18], + [18, 17], + [18, 19], + [19, 14], + [19, 18], + ] configuration = QasmBackendConfiguration( - backend_name='fake_poughkeepsie', - backend_version='0.0.0', + backend_name="fake_poughkeepsie", + backend_version="0.0.0", n_qubits=20, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -54,7 +95,7 @@ def __init__(self): memory=True, max_shots=8192, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) @@ -74,26 +115,68 @@ class FakeLegacyPoughkeepsie(FakeLegacyBackend): def __init__(self): """ - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ - cmap = [[0, 1], [0, 5], [1, 0], [1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3], [4, 9], - [5, 0], [5, 6], [5, 10], [6, 5], [6, 7], [7, 6], [7, 8], [7, 12], [8, 7], [8, 9], - [9, 4], [9, 8], [9, 14], [10, 5], [10, 11], [10, 15], [11, 10], [11, 12], [12, 7], - [12, 11], [12, 13], [13, 12], [13, 14], [14, 9], [14, 13], [14, 19], [15, 10], - [15, 16], [16, 15], [16, 17], [17, 16], [17, 18], [18, 17], [18, 19], [19, 14], - [19, 18]] + cmap = [ + [0, 1], + [0, 5], + [1, 0], + [1, 2], + [2, 1], + [2, 3], + [3, 2], + [3, 4], + [4, 3], + [4, 9], + [5, 0], + [5, 6], + [5, 10], + [6, 5], + [6, 7], + [7, 6], + [7, 8], + [7, 12], + [8, 7], + [8, 9], + [9, 4], + [9, 8], + [9, 14], + [10, 5], + [10, 11], + [10, 15], + [11, 10], + [11, 12], + [12, 7], + [12, 11], + [12, 13], + [13, 12], + [13, 14], + [14, 9], + [14, 13], + [14, 19], + [15, 10], + [15, 16], + [16, 15], + [16, 17], + [17, 16], + [17, 18], + [18, 17], + [18, 19], + [19, 14], + [19, 18], + ] configuration = QasmBackendConfiguration( - backend_name='fake_poughkeepsie', - backend_version='0.0.0', + backend_name="fake_poughkeepsie", + backend_version="0.0.0", n_qubits=20, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -101,7 +184,7 @@ def __init__(self): memory=True, max_shots=8192, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) diff --git a/qiskit/test/mock/backends/rueschlikon/fake_rueschlikon.py b/qiskit/test/mock/backends/rueschlikon/fake_rueschlikon.py index b1a4f29668a8..306de8311ad9 100644 --- a/qiskit/test/mock/backends/rueschlikon/fake_rueschlikon.py +++ b/qiskit/test/mock/backends/rueschlikon/fake_rueschlikon.py @@ -23,20 +23,40 @@ class FakeRueschlikon(FakeBackend): def __init__(self): """ - 1 → 2 → 3 → 4 ← 5 ← 6 → 7 ← 8 - ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ - 0 ← 15 → 14 ← 13 ← 12 → 11 → 10 ← 9 + 1 → 2 → 3 → 4 ← 5 ← 6 → 7 ← 8 + ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ + 0 ← 15 → 14 ← 13 ← 12 → 11 → 10 ← 9 """ - cmap = [[1, 0], [1, 2], [2, 3], [3, 4], [3, 14], [5, 4], [6, 5], - [6, 7], [6, 11], [7, 10], [8, 7], [9, 8], [9, 10], - [11, 10], [12, 5], [12, 11], [12, 13], [13, 4], - [13, 14], [15, 0], [15, 2], [15, 14]] + cmap = [ + [1, 0], + [1, 2], + [2, 3], + [3, 4], + [3, 14], + [5, 4], + [6, 5], + [6, 7], + [6, 11], + [7, 10], + [8, 7], + [9, 8], + [9, 10], + [11, 10], + [12, 5], + [12, 11], + [12, 13], + [13, 4], + [13, 14], + [15, 0], + [15, 2], + [15, 14], + ] configuration = QasmBackendConfiguration( - backend_name='fake_rueschlikon', - backend_version='0.0.0', + backend_name="fake_rueschlikon", + backend_version="0.0.0", n_qubits=16, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -44,7 +64,7 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) @@ -56,20 +76,40 @@ class FakeLegacyRueschlikon(FakeLegacyBackend): def __init__(self): """ - 1 → 2 → 3 → 4 ← 5 ← 6 → 7 ← 8 - ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ - 0 ← 15 → 14 ← 13 ← 12 → 11 → 10 ← 9 + 1 → 2 → 3 → 4 ← 5 ← 6 → 7 ← 8 + ↓ ↑ ↓ ↓ ↑ ↓ ↓ ↑ + 0 ← 15 → 14 ← 13 ← 12 → 11 → 10 ← 9 """ - cmap = [[1, 0], [1, 2], [2, 3], [3, 4], [3, 14], [5, 4], [6, 5], - [6, 7], [6, 11], [7, 10], [8, 7], [9, 8], [9, 10], - [11, 10], [12, 5], [12, 11], [12, 13], [13, 4], - [13, 14], [15, 0], [15, 2], [15, 14]] + cmap = [ + [1, 0], + [1, 2], + [2, 3], + [3, 4], + [3, 14], + [5, 4], + [6, 5], + [6, 7], + [6, 11], + [7, 10], + [8, 7], + [9, 8], + [9, 10], + [11, 10], + [12, 5], + [12, 11], + [12, 13], + [13, 4], + [13, 14], + [15, 0], + [15, 2], + [15, 14], + ] configuration = QasmBackendConfiguration( - backend_name='fake_rueschlikon', - backend_version='0.0.0', + backend_name="fake_rueschlikon", + backend_version="0.0.0", n_qubits=16, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -77,7 +117,7 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) diff --git a/qiskit/test/mock/backends/singapore/fake_singapore.py b/qiskit/test/mock/backends/singapore/fake_singapore.py index 130b4b0d87fd..ce2e5e6817d5 100644 --- a/qiskit/test/mock/backends/singapore/fake_singapore.py +++ b/qiskit/test/mock/backends/singapore/fake_singapore.py @@ -21,13 +21,13 @@ class FakeSingapore(fake_pulse_backend.FakePulseBackend): """A fake Singapore backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) @@ -40,13 +40,13 @@ class FakeSingapore(fake_pulse_backend.FakePulseBackend): class FakeLegacySingapore(fake_pulse_backend.FakePulseLegacyBackend): """A fake Singapore backend. - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ↕ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ - 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ↕ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ + 15 ↔ 16 ↔ 17 ↔ 18 ↔ 19 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/backends/tenerife/fake_tenerife.py b/qiskit/test/mock/backends/tenerife/fake_tenerife.py index 4d327dae5fe6..7a29a4868a48 100644 --- a/qiskit/test/mock/backends/tenerife/fake_tenerife.py +++ b/qiskit/test/mock/backends/tenerife/fake_tenerife.py @@ -17,8 +17,7 @@ import os import json -from qiskit.providers.models import (GateConfig, QasmBackendConfiguration, - BackendProperties) +from qiskit.providers.models import GateConfig, QasmBackendConfiguration, BackendProperties from qiskit.test.mock.fake_backend import FakeBackend, FakeLegacyBackend @@ -27,19 +26,19 @@ class FakeTenerife(FakeBackend): def __init__(self): """ - 1 - ↙ ↑ - 0 ← 2 ← 3 - ↑ ↙ - 4 + 1 + ↙ ↑ + 0 ← 2 ← 3 + ↑ ↙ + 4 """ cmap = [[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]] configuration = QasmBackendConfiguration( - backend_name='fake_tenerife', - backend_version='0.0.0', + backend_name="fake_tenerife", + backend_version="0.0.0", n_qubits=5, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -47,15 +46,14 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) super().__init__(configuration) def properties(self): - """Returns a snapshot of device properties as recorded on 8/30/19. - """ + """Returns a snapshot of device properties as recorded on 8/30/19.""" dirname = os.path.dirname(__file__) filename = "props_tenerife.json" with open(os.path.join(dirname, filename)) as f_prop: @@ -68,19 +66,19 @@ class FakeLegacyTenerife(FakeLegacyBackend): def __init__(self): """ - 1 - ↙ ↑ - 0 ← 2 ← 3 - ↑ ↙ - 4 + 1 + ↙ ↑ + 0 ← 2 ← 3 + ↑ ↙ + 4 """ cmap = [[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]] configuration = QasmBackendConfiguration( - backend_name='fake_tenerife', - backend_version='0.0.0', + backend_name="fake_tenerife", + backend_version="0.0.0", n_qubits=5, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -88,15 +86,14 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) super().__init__(configuration) def properties(self): - """Returns a snapshot of device properties as recorded on 8/30/19. - """ + """Returns a snapshot of device properties as recorded on 8/30/19.""" dirname = os.path.dirname(__file__) filename = "props_tenerife.json" with open(os.path.join(dirname, filename)) as f_prop: diff --git a/qiskit/test/mock/backends/tokyo/fake_tokyo.py b/qiskit/test/mock/backends/tokyo/fake_tokyo.py index 14104a2a4cc2..e2c3780739f2 100644 --- a/qiskit/test/mock/backends/tokyo/fake_tokyo.py +++ b/qiskit/test/mock/backends/tokyo/fake_tokyo.py @@ -17,8 +17,7 @@ import os import json -from qiskit.providers.models import (GateConfig, QasmBackendConfiguration, - BackendProperties) +from qiskit.providers.models import GateConfig, QasmBackendConfiguration, BackendProperties from qiskit.test.mock.fake_backend import FakeBackend, FakeLegacyBackend @@ -27,30 +26,92 @@ class FakeTokyo(FakeBackend): def __init__(self): """ - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ ↕ ↕ ⤫ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ⤫ ↕ ↕ ⤫ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ ⤫ ↕ ⤫ ↕ - 15 ↔ 16 ↔ 17 18 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ ↕ ↕ ⤫ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ⤫ ↕ ↕ ⤫ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ ⤫ ↕ ⤫ ↕ + 15 ↔ 16 ↔ 17 18 19 """ - cmap = [[0, 1], [0, 5], [1, 0], [1, 2], [1, 6], [1, 7], [2, 1], [2, 6], - [3, 8], [4, 8], [4, 9], [5, 0], [5, 6], [5, 10], [5, 11], [6, 1], - [6, 2], [6, 5], [6, 7], [6, 10], [6, 11], [7, 1], [7, 6], [7, 8], - [7, 12], [8, 3], [8, 4], [8, 7], [8, 9], [8, 12], [8, 13], [9, 4], - [9, 8], [10, 5], [10, 6], [10, 11], [10, 15], [11, 5], [11, 6], - [11, 10], [11, 12], [11, 16], [11, 17], [12, 7], [12, 8], [12, 11], - [12, 13], [12, 16], [13, 8], [13, 12], [13, 14], [13, 18], [13, 19], - [14, 13], [14, 18], [14, 19], [15, 10], [15, 16], [16, 11], [16, 12], - [16, 15], [16, 17], [17, 11], [17, 16], [17, 18], [18, 13], [18, 14], - [18, 17], [19, 13], [19, 14]] + cmap = [ + [0, 1], + [0, 5], + [1, 0], + [1, 2], + [1, 6], + [1, 7], + [2, 1], + [2, 6], + [3, 8], + [4, 8], + [4, 9], + [5, 0], + [5, 6], + [5, 10], + [5, 11], + [6, 1], + [6, 2], + [6, 5], + [6, 7], + [6, 10], + [6, 11], + [7, 1], + [7, 6], + [7, 8], + [7, 12], + [8, 3], + [8, 4], + [8, 7], + [8, 9], + [8, 12], + [8, 13], + [9, 4], + [9, 8], + [10, 5], + [10, 6], + [10, 11], + [10, 15], + [11, 5], + [11, 6], + [11, 10], + [11, 12], + [11, 16], + [11, 17], + [12, 7], + [12, 8], + [12, 11], + [12, 13], + [12, 16], + [13, 8], + [13, 12], + [13, 14], + [13, 18], + [13, 19], + [14, 13], + [14, 18], + [14, 19], + [15, 10], + [15, 16], + [16, 11], + [16, 12], + [16, 15], + [16, 17], + [17, 11], + [17, 16], + [17, 18], + [18, 13], + [18, 14], + [18, 17], + [19, 13], + [19, 14], + ] configuration = QasmBackendConfiguration( - backend_name='fake_tokyo', - backend_version='0.0.0', + backend_name="fake_tokyo", + backend_version="0.0.0", n_qubits=20, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -58,15 +119,14 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) super().__init__(configuration) def properties(self): - """Returns a snapshot of device properties as recorded on 8/30/19. - """ + """Returns a snapshot of device properties as recorded on 8/30/19.""" dirname = os.path.dirname(__file__) filename = "props_tokyo.json" with open(os.path.join(dirname, filename)) as f_prop: @@ -79,30 +139,92 @@ class FakeLegacyTokyo(FakeLegacyBackend): def __init__(self): """ - 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 - ↕ ↕ ↕ ↕ ⤫ ↕ - 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 - ↕ ⤫ ↕ ↕ ⤫ ↕ - 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 - ↕ ↕ ⤫ ↕ ⤫ ↕ - 15 ↔ 16 ↔ 17 18 19 + 00 ↔ 01 ↔ 02 ↔ 03 ↔ 04 + ↕ ↕ ↕ ↕ ⤫ ↕ + 05 ↔ 06 ↔ 07 ↔ 08 ↔ 09 + ↕ ⤫ ↕ ↕ ⤫ ↕ + 10 ↔ 11 ↔ 12 ↔ 13 ↔ 14 + ↕ ↕ ⤫ ↕ ⤫ ↕ + 15 ↔ 16 ↔ 17 18 19 """ - cmap = [[0, 1], [0, 5], [1, 0], [1, 2], [1, 6], [1, 7], [2, 1], [2, 6], - [3, 8], [4, 8], [4, 9], [5, 0], [5, 6], [5, 10], [5, 11], [6, 1], - [6, 2], [6, 5], [6, 7], [6, 10], [6, 11], [7, 1], [7, 6], [7, 8], - [7, 12], [8, 3], [8, 4], [8, 7], [8, 9], [8, 12], [8, 13], [9, 4], - [9, 8], [10, 5], [10, 6], [10, 11], [10, 15], [11, 5], [11, 6], - [11, 10], [11, 12], [11, 16], [11, 17], [12, 7], [12, 8], [12, 11], - [12, 13], [12, 16], [13, 8], [13, 12], [13, 14], [13, 18], [13, 19], - [14, 13], [14, 18], [14, 19], [15, 10], [15, 16], [16, 11], [16, 12], - [16, 15], [16, 17], [17, 11], [17, 16], [17, 18], [18, 13], [18, 14], - [18, 17], [19, 13], [19, 14]] + cmap = [ + [0, 1], + [0, 5], + [1, 0], + [1, 2], + [1, 6], + [1, 7], + [2, 1], + [2, 6], + [3, 8], + [4, 8], + [4, 9], + [5, 0], + [5, 6], + [5, 10], + [5, 11], + [6, 1], + [6, 2], + [6, 5], + [6, 7], + [6, 10], + [6, 11], + [7, 1], + [7, 6], + [7, 8], + [7, 12], + [8, 3], + [8, 4], + [8, 7], + [8, 9], + [8, 12], + [8, 13], + [9, 4], + [9, 8], + [10, 5], + [10, 6], + [10, 11], + [10, 15], + [11, 5], + [11, 6], + [11, 10], + [11, 12], + [11, 16], + [11, 17], + [12, 7], + [12, 8], + [12, 11], + [12, 13], + [12, 16], + [13, 8], + [13, 12], + [13, 14], + [13, 18], + [13, 19], + [14, 13], + [14, 18], + [14, 19], + [15, 10], + [15, 16], + [16, 11], + [16, 12], + [16, 15], + [16, 17], + [17, 11], + [17, 16], + [17, 18], + [18, 13], + [18, 14], + [18, 17], + [19, 13], + [19, 14], + ] configuration = QasmBackendConfiguration( - backend_name='fake_tokyo', - backend_version='0.0.0', + backend_name="fake_tokyo", + backend_version="0.0.0", n_qubits=20, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=False, @@ -110,15 +232,14 @@ def __init__(self): memory=False, max_shots=65536, max_experiments=900, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=cmap, ) super().__init__(configuration) def properties(self): - """Returns a snapshot of device properties as recorded on 8/30/19. - """ + """Returns a snapshot of device properties as recorded on 8/30/19.""" dirname = os.path.dirname(__file__) filename = "props_tokyo.json" with open(os.path.join(dirname, filename)) as f_prop: diff --git a/qiskit/test/mock/backends/vigo/fake_vigo.py b/qiskit/test/mock/backends/vigo/fake_vigo.py index 299226fb0710..e366fde49396 100644 --- a/qiskit/test/mock/backends/vigo/fake_vigo.py +++ b/qiskit/test/mock/backends/vigo/fake_vigo.py @@ -21,9 +21,9 @@ class FakeVigo(fake_qasm_backend.FakeQasmBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) @@ -35,9 +35,9 @@ class FakeVigo(fake_qasm_backend.FakeQasmBackend): class FakeLegacyVigo(fake_qasm_backend.FakeQasmLegacyBackend): """A fake 5 qubit backend. - 0 ↔ 1 ↔ 3 ↔ 4 - ↕ - 2 + 0 ↔ 1 ↔ 3 ↔ 4 + ↕ + 2 """ dirname = os.path.dirname(__file__) diff --git a/qiskit/test/mock/fake_1q.py b/qiskit/test/mock/fake_1q.py index 3275b4832106..3d57eb520147 100644 --- a/qiskit/test/mock/fake_1q.py +++ b/qiskit/test/mock/fake_1q.py @@ -24,53 +24,75 @@ class Fake1Q(FakeBackend): def __init__(self): """ - 0 + 0 """ mock_time = datetime.datetime.now() dt = 1.3333 configuration = BackendProperties( - backend_name='fake_1q', - backend_version='0.0.0', + backend_name="fake_1q", + backend_version="0.0.0", n_qubits=1, - basis_gates=['u1', 'u2', 'u3', 'cx'], + basis_gates=["u1", "u2", "u3", "cx"], simulator=False, local=True, conditional=False, memory=False, max_shots=1024, qubits=[ - [Nduv(date=mock_time, name='T1', unit='µs', value=71.9500421005539), - Nduv(date=mock_time, name='frequency', unit='MHz', value=4919.96800692)] + [ + Nduv(date=mock_time, name="T1", unit="µs", value=71.9500421005539), + Nduv(date=mock_time, name="frequency", unit="MHz", value=4919.96800692), + ] ], gates=[ - Gate(gate='u1', name='u1_0', qubits=[0], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=1.0), - Nduv(date=mock_time, name='gate_length', unit='ns', value=0.)]), - Gate(gate='u3', name='u3_0', qubits=[0], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=1.0), - Nduv(date=mock_time, name='gate_length', unit='ns', value=2 * dt)]), - Gate(gate='u3', name='u3_1', qubits=[1], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=1.0), - Nduv(date=mock_time, name='gate_length', unit='ns', value=4 * dt)]), - Gate(gate='cx', name='cx0_1', qubits=[0, 1], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=1.0), - Nduv(date=mock_time, name='gate_length', unit='ns', value=22 * dt)]), + Gate( + gate="u1", + name="u1_0", + qubits=[0], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=1.0), + Nduv(date=mock_time, name="gate_length", unit="ns", value=0.0), + ], + ), + Gate( + gate="u3", + name="u3_0", + qubits=[0], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=1.0), + Nduv(date=mock_time, name="gate_length", unit="ns", value=2 * dt), + ], + ), + Gate( + gate="u3", + name="u3_1", + qubits=[1], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=1.0), + Nduv(date=mock_time, name="gate_length", unit="ns", value=4 * dt), + ], + ), + Gate( + gate="cx", + name="cx0_1", + qubits=[0, 1], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=1.0), + Nduv(date=mock_time, name="gate_length", unit="ns", value=22 * dt), + ], + ), ], coupling_map=None, n_registers=1, last_update_date=mock_time, - general=[] + general=[], ) super().__init__(configuration) def defaults(self): - """ defaults == configuration """ + """defaults == configuration""" return self._configuration def properties(self): - """ properties == configuration """ + """properties == configuration""" return self._configuration diff --git a/qiskit/test/mock/fake_backend.py b/qiskit/test/mock/fake_backend.py index c323cf839695..5990ac45096f 100644 --- a/qiskit/test/mock/fake_backend.py +++ b/qiskit/test/mock/fake_backend.py @@ -28,19 +28,20 @@ try: from qiskit.providers import aer + HAS_AER = True except ImportError: HAS_AER = False from qiskit.providers import basicaer -class _Credentials(): - def __init__(self, token='123456', url='https://'): +class _Credentials: + def __init__(self, token="123456", url="https://"): self.token = token self.url = url - self.hub = 'hub' - self.group = 'group' - self.project = 'project' + self.hub = "hub" + self.group = "group" + self.project = "project" class FakeBackend(BackendV1): @@ -63,60 +64,46 @@ def properties(self): unique_qubits = list(set().union(*coupling_map)) properties = { - 'backend_name': self.name(), - 'backend_version': self.configuration().backend_version, - 'last_update_date': '2000-01-01 00:00:00Z', - 'qubits': [ + "backend_name": self.name(), + "backend_version": self.configuration().backend_version, + "last_update_date": "2000-01-01 00:00:00Z", + "qubits": [ [ - { - "date": "2000-01-01 00:00:00Z", - "name": "T1", - "unit": "\u00b5s", - "value": 0.0 - }, - { - "date": "2000-01-01 00:00:00Z", - "name": "T2", - "unit": "\u00b5s", - "value": 0.0 - }, + {"date": "2000-01-01 00:00:00Z", "name": "T1", "unit": "\u00b5s", "value": 0.0}, + {"date": "2000-01-01 00:00:00Z", "name": "T2", "unit": "\u00b5s", "value": 0.0}, { "date": "2000-01-01 00:00:00Z", "name": "frequency", "unit": "GHz", - "value": 0.0 + "value": 0.0, }, { "date": "2000-01-01 00:00:00Z", "name": "readout_error", "unit": "", - "value": 0.0 + "value": 0.0, }, - { - "date": "2000-01-01 00:00:00Z", - "name": "operational", - "unit": "", - "value": 1 - } - ] for _ in range(len(unique_qubits)) - ], - 'gates': [{ - "gate": "cx", - "name": "CX" + str(pair[0]) + "_" + str(pair[1]), - "parameters": [ - { - "date": "2000-01-01 00:00:00Z", - "name": "gate_error", - "unit": "", - "value": 0.0 - } - ], - "qubits": [ - pair[0], - pair[1] + {"date": "2000-01-01 00:00:00Z", "name": "operational", "unit": "", "value": 1}, ] - } for pair in coupling_map], - 'general': [] + for _ in range(len(unique_qubits)) + ], + "gates": [ + { + "gate": "cx", + "name": "CX" + str(pair[0]) + "_" + str(pair[1]), + "parameters": [ + { + "date": "2000-01-01 00:00:00Z", + "name": "gate_error", + "unit": "", + "value": 0.0, + } + ], + "qubits": [pair[0], pair[1]], + } + for pair in coupling_map + ], + "general": [], } return BackendProperties.from_dict(properties) @@ -143,30 +130,32 @@ def run(self, run_input, **kwargs): elif all(isinstance(x, circuit.QuantumCircuit) for x in circuits): pulse_job = False if pulse_job is None: - raise QiskitError("Invalid input object %s, must be either a " - "QuantumCircuit, Schedule, or a list of " - "either" % circuits) + raise QiskitError( + "Invalid input object %s, must be either a " + "QuantumCircuit, Schedule, or a list of " + "either" % circuits + ) if HAS_AER: if pulse_job: from qiskit.providers.aer.pulse import PulseSystemModel + system_model = PulseSystemModel.from_backend(self) - sim = aer.Aer.get_backend('pulse_simulator') + sim = aer.Aer.get_backend("pulse_simulator") job = sim.run(circuits, system_model, **kwargs) else: - sim = aer.Aer.get_backend('qasm_simulator') + sim = aer.Aer.get_backend("qasm_simulator") if self.properties(): from qiskit.providers.aer.noise import NoiseModel + noise_model = NoiseModel.from_backend(self, warnings=False) job = sim.run(circuits, noise_model=noise_model) else: job = sim.run(circuits, **kwargs) else: if pulse_job: - raise QiskitError("Unable to run pulse schedules without " - "qiskit-aer installed") - warnings.warn("Aer not found using BasicAer and no noise", - RuntimeWarning) - sim = basicaer.BasicAer.get_backend('qasm_simulator') + raise QiskitError("Unable to run pulse schedules without " "qiskit-aer installed") + warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) + sim = basicaer.BasicAer.get_backend("qasm_simulator") job = sim.run(circuits, **kwargs) return job @@ -190,60 +179,46 @@ def properties(self): unique_qubits = list(set().union(*coupling_map)) properties = { - 'backend_name': self.name(), - 'backend_version': self.configuration().backend_version, - 'last_update_date': '2000-01-01 00:00:00Z', - 'qubits': [ + "backend_name": self.name(), + "backend_version": self.configuration().backend_version, + "last_update_date": "2000-01-01 00:00:00Z", + "qubits": [ [ - { - "date": "2000-01-01 00:00:00Z", - "name": "T1", - "unit": "\u00b5s", - "value": 0.0 - }, - { - "date": "2000-01-01 00:00:00Z", - "name": "T2", - "unit": "\u00b5s", - "value": 0.0 - }, + {"date": "2000-01-01 00:00:00Z", "name": "T1", "unit": "\u00b5s", "value": 0.0}, + {"date": "2000-01-01 00:00:00Z", "name": "T2", "unit": "\u00b5s", "value": 0.0}, { "date": "2000-01-01 00:00:00Z", "name": "frequency", "unit": "GHz", - "value": 0.0 + "value": 0.0, }, { "date": "2000-01-01 00:00:00Z", "name": "readout_error", "unit": "", - "value": 0.0 + "value": 0.0, }, - { - "date": "2000-01-01 00:00:00Z", - "name": "operational", - "unit": "", - "value": 1 - } - ] for _ in range(len(unique_qubits)) - ], - 'gates': [{ - "gate": "cx", - "name": "CX" + str(pair[0]) + "_" + str(pair[1]), - "parameters": [ - { - "date": "2000-01-01 00:00:00Z", - "name": "gate_error", - "unit": "", - "value": 0.0 - } - ], - "qubits": [ - pair[0], - pair[1] + {"date": "2000-01-01 00:00:00Z", "name": "operational", "unit": "", "value": 1}, ] - } for pair in coupling_map], - 'general': [] + for _ in range(len(unique_qubits)) + ], + "gates": [ + { + "gate": "cx", + "name": "CX" + str(pair[0]) + "_" + str(pair[1]), + "parameters": [ + { + "date": "2000-01-01 00:00:00Z", + "name": "gate_error", + "unit": "", + "value": 0.0, + } + ], + "qubits": [pair[0], pair[1]], + } + for pair in coupling_map + ], + "general": [], } return BackendProperties.from_dict(properties) @@ -251,15 +226,17 @@ def properties(self): def run(self, qobj): """Main job in simulator""" if HAS_AER: - if qobj.type == 'PULSE': + if qobj.type == "PULSE": from qiskit.providers.aer.pulse import PulseSystemModel + system_model = PulseSystemModel.from_backend(self) - sim = aer.Aer.get_backend('pulse_simulator') + sim = aer.Aer.get_backend("pulse_simulator") job = sim.run(qobj, system_model) else: - sim = aer.Aer.get_backend('qasm_simulator') + sim = aer.Aer.get_backend("qasm_simulator") if self.properties(): from qiskit.providers.aer.noise import NoiseModel + noise_model = NoiseModel.from_backend(self, warnings=False) job = sim.run(qobj, noise_model=noise_model) else: @@ -268,14 +245,12 @@ def run(self, qobj): out_job = fake_job.FakeLegacyJob(self, job.job_id, None) out_job._future = job._future else: - if qobj.type == 'PULSE': - raise QiskitError("Unable to run pulse schedules without " - "qiskit-aer installed") - warnings.warn("Aer not found using BasicAer and no noise", - RuntimeWarning) + if qobj.type == "PULSE": + raise QiskitError("Unable to run pulse schedules without " "qiskit-aer installed") + warnings.warn("Aer not found using BasicAer and no noise", RuntimeWarning) def run_job(): - sim = basicaer.BasicAer.get_backend('qasm_simulator') + sim = basicaer.BasicAer.get_backend("qasm_simulator") return sim.run(qobj).result() job_id = uuid.uuid4() diff --git a/qiskit/test/mock/fake_job.py b/qiskit/test/mock/fake_job.py index 5b5fb50434ff..da4fc84ab3c9 100644 --- a/qiskit/test/mock/fake_job.py +++ b/qiskit/test/mock/fake_job.py @@ -23,6 +23,7 @@ class FakeJob(JobV1): """Fake simulator job""" + _executor = futures.ThreadPoolExecutor() def __init__(self, backend, job_id, fn): @@ -54,11 +55,9 @@ def status(self): elif self._error: _status = JobStatus.ERROR else: - raise Exception('Unexpected state of {}'.format( - self.__class__.__name__)) + raise Exception("Unexpected state of {}".format(self.__class__.__name__)) _status_msg = None - return {'status': _status, - 'status_msg': _status_msg} + return {"status": _status, "status_msg": _status_msg} def job_id(self): return self._job_id @@ -85,6 +84,7 @@ def _error(self): class FakeLegacyJob(BaseJob): """Fake simulator job""" + _executor = futures.ThreadPoolExecutor() def __init__(self, backend, job_id, fn): @@ -116,11 +116,9 @@ def status(self): elif self._error: _status = JobStatus.ERROR else: - raise Exception('Unexpected state of {}'.format( - self.__class__.__name__)) + raise Exception("Unexpected state of {}".format(self.__class__.__name__)) _status_msg = None - return {'status': _status, - 'status_msg': _status_msg} + return {"status": _status, "status_msg": _status_msg} def job_id(self): return self._job_id diff --git a/qiskit/test/mock/fake_openpulse_2q.py b/qiskit/test/mock/fake_openpulse_2q.py index 9b7f068242ae..db08df4105e0 100644 --- a/qiskit/test/mock/fake_openpulse_2q.py +++ b/qiskit/test/mock/fake_openpulse_2q.py @@ -15,8 +15,13 @@ """ import datetime -from qiskit.providers.models import (GateConfig, PulseBackendConfiguration, - PulseDefaults, Command, UchannelLO) +from qiskit.providers.models import ( + GateConfig, + PulseBackendConfiguration, + PulseDefaults, + Command, + UchannelLO, +) from qiskit.providers.models.backendproperties import Nduv, Gate, BackendProperties from qiskit.qobj import PulseQobjInstruction from .fake_backend import FakeBackend @@ -27,24 +32,24 @@ class FakeOpenPulse2Q(FakeBackend): def __init__(self): configuration = PulseBackendConfiguration( - backend_name='fake_openpulse_2q', - backend_version='0.0.0', + backend_name="fake_openpulse_2q", + backend_version="0.0.0", n_qubits=2, meas_levels=[0, 1, 2], - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=True, open_pulse=True, memory=False, max_shots=65536, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=[[0, 1]], n_registers=2, n_uchannels=2, u_channel_lo=[ - [UchannelLO(q=0, scale=1. + 0.j)], - [UchannelLO(q=0, scale=-1. + 0.j), UchannelLO(q=1, scale=1. + 0.j)] + [UchannelLO(q=0, scale=1.0 + 0.0j)], + [UchannelLO(q=0, scale=-1.0 + 0.0j), UchannelLO(q=1, scale=1.0 + 0.0j)], ], qubit_lo_range=[[4.5, 5.5], [4.5, 5.5]], meas_lo_range=[[6.0, 7.0], [6.0, 7.0]], @@ -53,295 +58,270 @@ def __init__(self): rep_times=[100, 250, 500, 1000], meas_map=[[0, 1]], channel_bandwidth=[ - [-0.2, 0.4], [-0.3, 0.3], [-0.3, 0.3], - [-0.02, 0.02], [-0.02, 0.02], [-0.02, 0.02] + [-0.2, 0.4], + [-0.3, 0.3], + [-0.3, 0.3], + [-0.02, 0.02], + [-0.02, 0.02], + [-0.02, 0.02], ], - meas_kernels=['kernel1'], - discriminators=['max_1Q_fidelity'], + meas_kernels=["kernel1"], + discriminators=["max_1Q_fidelity"], acquisition_latency=[[100, 100], [100, 100]], conditional_latency=[ - [100, 1000], [1000, 100], [100, 1000], - [1000, 100], [100, 1000], [1000, 100] + [100, 1000], + [1000, 100], + [100, 1000], + [1000, 100], + [100, 1000], + [1000, 100], ], hamiltonian={ - 'h_str': ["np.pi*(2*v0-alpha0)*O0", "np.pi*alpha0*O0*O0", "2*np.pi*r*X0||D0", - "2*np.pi*r*X0||U1", "2*np.pi*r*X1||U0", "np.pi*(2*v1-alpha1)*O1", - "np.pi*alpha1*O1*O1", "2*np.pi*r*X1||D1", "2*np.pi*j*(Sp0*Sm1+Sm0*Sp1)"], - 'description': "A hamiltonian for a mocked 2Q device, with 1Q and 2Q terms.", - 'qub': {'0': 3, '1': 3}, - 'vars': {'v0': 5.00, - 'v1': 5.1, - 'j': 0.01, - 'r': 0.02, - 'alpha0': -0.33, - 'alpha1': -0.33} + "h_str": [ + "np.pi*(2*v0-alpha0)*O0", + "np.pi*alpha0*O0*O0", + "2*np.pi*r*X0||D0", + "2*np.pi*r*X0||U1", + "2*np.pi*r*X1||U0", + "np.pi*(2*v1-alpha1)*O1", + "np.pi*alpha1*O1*O1", + "2*np.pi*r*X1||D1", + "2*np.pi*j*(Sp0*Sm1+Sm0*Sp1)", + ], + "description": "A hamiltonian for a mocked 2Q device, with 1Q and 2Q terms.", + "qub": {"0": 3, "1": 3}, + "vars": { + "v0": 5.00, + "v1": 5.1, + "j": 0.01, + "r": 0.02, + "alpha0": -0.33, + "alpha1": -0.33, + }, }, channels={ - 'acquire0': { - 'operates': {'qubits': [0]}, - 'purpose': 'acquire', - 'type': 'acquire' - }, - 'acquire1': { - 'operates': {'qubits': [1]}, - 'purpose': 'acquire', - 'type': 'acquire' - }, - 'd0': { - 'operates': {'qubits': [0]}, - 'purpose': 'drive', - 'type': 'drive' - }, - 'd1': { - 'operates': {'qubits': [1]}, - 'purpose': 'drive', - 'type': 'drive' - }, - 'm0': { - 'type': 'measure', - 'purpose': 'measure', - 'operates': {'qubits': [0]} - }, - 'm1': { - 'type': 'measure', - 'purpose': 'measure', - 'operates': {'qubits': [1]} + "acquire0": {"operates": {"qubits": [0]}, "purpose": "acquire", "type": "acquire"}, + "acquire1": {"operates": {"qubits": [1]}, "purpose": "acquire", "type": "acquire"}, + "d0": {"operates": {"qubits": [0]}, "purpose": "drive", "type": "drive"}, + "d1": {"operates": {"qubits": [1]}, "purpose": "drive", "type": "drive"}, + "m0": {"type": "measure", "purpose": "measure", "operates": {"qubits": [0]}}, + "m1": {"type": "measure", "purpose": "measure", "operates": {"qubits": [1]}}, + "u0": { + "operates": {"qubits": [0, 1]}, + "purpose": "cross-resonance", + "type": "control", }, - 'u0': { - 'operates': {'qubits': [0, 1]}, - 'purpose': 'cross-resonance', - 'type': 'control' + "u1": { + "operates": {"qubits": [1, 0]}, + "purpose": "cross-resonance", + "type": "control", }, - 'u1': { - 'operates': {'qubits': [1, 0]}, - 'purpose': 'cross-resonance', - 'type': 'control' - } }, processor_type={ "family": "Canary", "revision": "1.0", "segment": "A", - } + }, ) - self._defaults = PulseDefaults.from_dict({ - 'qubit_freq_est': [4.9, 5.0], - 'meas_freq_est': [6.5, 6.6], - 'buffer': 10, - 'pulse_library': [ - { - 'name': 'x90p_d0', - 'samples': 2 * [0.1+0j] - }, - { - 'name': 'x90p_d1', - 'samples': 2 * [0.1+0j] - }, - { - 'name': 'x90m_d0', - 'samples': 2 * [-0.1+0j] - }, - { - 'name': 'x90m_d1', - 'samples': 2 * [-0.1+0j] - }, - { - 'name': 'y90p_d0', - 'samples': 2 * [0.1j] - }, - { - 'name': 'y90p_d1', - 'samples': 2 * [0.1j] - }, - { - 'name': 'xp_d0', - 'samples': 2 * [0.2 + 0j] - }, - { - 'name': 'ym_d0', - 'samples': 2 * [-0.2j] - }, - { - 'name': 'cr90p_u0', - 'samples': 9 * [0.1 + 0j] - }, - { - 'name': 'cr90m_u0', - 'samples': 9 * [-0.1 + 0j] - }, - { - 'name': 'measure_m0', - 'samples': 10 * [0.1 + 0j] - }, - { - 'name': 'measure_m1', - 'samples': 10 * [0.1 + 0j] - } - ], - 'cmd_def': [ - Command.from_dict({ - 'name': 'u1', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u1', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u2', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P1').to_dict(), - PulseQobjInstruction(name='y90p_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=2, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u2', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P1').to_dict(), - PulseQobjInstruction(name='y90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=2, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u3', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P2').to_dict(), - PulseQobjInstruction(name='x90p_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=2, - phase='-P0').to_dict(), - PulseQobjInstruction(name='x90m_d0', - ch='d0', - t0=2).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=4, - phase='-P1').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u3', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P2').to_dict(), - PulseQobjInstruction(name='x90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=2, - phase='-P0').to_dict(), - PulseQobjInstruction(name='x90m_d1', - ch='d1', - t0=2).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=4, - phase='-P1').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'cx', - 'qubits': [0, 1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase=1.57).to_dict(), - PulseQobjInstruction(name='ym_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='xp_d0', - ch='d0', - t0=11).to_dict(), - PulseQobjInstruction(name='x90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='cr90p_u0', - ch='u0', - t0=2).to_dict(), - PulseQobjInstruction(name='cr90m_u0', - ch='u0', - t0=13).to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'measure', - 'qubits': [0, 1], - 'sequence': [ - PulseQobjInstruction(name='measure_m0', - ch='m0', - t0=0).to_dict(), - PulseQobjInstruction(name='measure_m1', - ch='m1', - t0=0).to_dict(), - PulseQobjInstruction(name='acquire', - duration=10, - t0=0, - qubits=[0, 1], - memory_slot=[0, 1]).to_dict()]}).to_dict() - ] - }) + self._defaults = PulseDefaults.from_dict( + { + "qubit_freq_est": [4.9, 5.0], + "meas_freq_est": [6.5, 6.6], + "buffer": 10, + "pulse_library": [ + {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, + {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, + {"name": "y90p_d0", "samples": 2 * [0.1j]}, + {"name": "y90p_d1", "samples": 2 * [0.1j]}, + {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d0", "samples": 2 * [-0.2j]}, + {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, + {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, + ], + "cmd_def": [ + Command.from_dict( + { + "name": "u1", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "measure", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction(name="measure_m0", ch="m0", t0=0).to_dict(), + PulseQobjInstruction(name="measure_m1", ch="m1", t0=0).to_dict(), + PulseQobjInstruction( + name="acquire", + duration=10, + t0=0, + qubits=[0, 1], + memory_slot=[0, 1], + ).to_dict(), + ], + } + ).to_dict(), + ], + } + ) mock_time = datetime.datetime.now() dt = 1.3333 self._properties = BackendProperties( - backend_name='fake_openpulse_2q', - backend_version='0.0.0', + backend_name="fake_openpulse_2q", + backend_version="0.0.0", last_update_date=mock_time, qubits=[ - [Nduv(date=mock_time, name='T1', unit='µs', value=71.9500421005539), - Nduv(date=mock_time, name='frequency', unit='MHz', value=4919.96800692)], - [Nduv(date=mock_time, name='T1', unit='µs', value=81.9500421005539), - Nduv(date=mock_time, name='frequency', unit='GHz', value=5.01996800692)] + [ + Nduv(date=mock_time, name="T1", unit="µs", value=71.9500421005539), + Nduv(date=mock_time, name="frequency", unit="MHz", value=4919.96800692), + ], + [ + Nduv(date=mock_time, name="T1", unit="µs", value=81.9500421005539), + Nduv(date=mock_time, name="frequency", unit="GHz", value=5.01996800692), + ], ], gates=[ - Gate(gate='u1', qubits=[0], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=0.06), - Nduv(date=mock_time, name='gate_length', unit='ns', value=0.)]), - Gate(gate='u3', qubits=[0], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=0.06), - Nduv(date=mock_time, name='gate_length', unit='ns', value=2 * dt)]), - Gate(gate='u3', qubits=[1], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=0.06), - Nduv(date=mock_time, name='gate_length', unit='ns', value=4 * dt)]), - Gate(gate='cx', qubits=[0, 1], - parameters=[ - Nduv(date=mock_time, name='gate_error', unit='', value=1.0), - Nduv(date=mock_time, name='gate_length', unit='ns', value=22 * dt)]), + Gate( + gate="u1", + qubits=[0], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=0.06), + Nduv(date=mock_time, name="gate_length", unit="ns", value=0.0), + ], + ), + Gate( + gate="u3", + qubits=[0], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=0.06), + Nduv(date=mock_time, name="gate_length", unit="ns", value=2 * dt), + ], + ), + Gate( + gate="u3", + qubits=[1], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=0.06), + Nduv(date=mock_time, name="gate_length", unit="ns", value=4 * dt), + ], + ), + Gate( + gate="cx", + qubits=[0, 1], + parameters=[ + Nduv(date=mock_time, name="gate_error", unit="", value=1.0), + Nduv(date=mock_time, name="gate_length", unit="ns", value=22 * dt), + ], + ), ], - general=[] + general=[], ) super().__init__(configuration) diff --git a/qiskit/test/mock/fake_openpulse_3q.py b/qiskit/test/mock/fake_openpulse_3q.py index 8239069915f6..e50010fda528 100644 --- a/qiskit/test/mock/fake_openpulse_3q.py +++ b/qiskit/test/mock/fake_openpulse_3q.py @@ -14,8 +14,13 @@ Fake backend supporting OpenPulse. """ -from qiskit.providers.models import (GateConfig, PulseBackendConfiguration, - PulseDefaults, Command, UchannelLO) +from qiskit.providers.models import ( + GateConfig, + PulseBackendConfiguration, + PulseDefaults, + Command, + UchannelLO, +) from qiskit.qobj import PulseQobjInstruction from .fake_backend import FakeBackend @@ -25,25 +30,25 @@ class FakeOpenPulse3Q(FakeBackend): def __init__(self): configuration = PulseBackendConfiguration( - backend_name='fake_openpulse_3q', - backend_version='0.0.0', + backend_name="fake_openpulse_3q", + backend_version="0.0.0", n_qubits=3, meas_levels=[0, 1, 2], - basis_gates=['u1', 'u2', 'u3', 'cx', 'id'], + basis_gates=["u1", "u2", "u3", "cx", "id"], simulator=False, local=True, conditional=True, open_pulse=True, memory=False, max_shots=65536, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], coupling_map=[[0, 1], [1, 2]], n_registers=3, n_uchannels=3, u_channel_lo=[ - [UchannelLO(q=0, scale=1. + 0.j)], - [UchannelLO(q=0, scale=-1. + 0.j), UchannelLO(q=1, scale=1. + 0.j)], - [UchannelLO(q=0, scale=1. + 0.j)] + [UchannelLO(q=0, scale=1.0 + 0.0j)], + [UchannelLO(q=0, scale=-1.0 + 0.0j), UchannelLO(q=1, scale=1.0 + 0.0j)], + [UchannelLO(q=0, scale=1.0 + 0.0j)], ], qubit_lo_range=[[4.5, 5.5], [4.5, 5.5], [4.5, 5.5]], meas_lo_range=[[6.0, 7.0], [6.0, 7.0], [6.0, 7.0]], @@ -52,372 +57,274 @@ def __init__(self): rep_times=[100, 250, 500, 1000], meas_map=[[0, 1, 2]], channel_bandwidth=[ - [-0.2, 0.4], [-0.3, 0.3], [-0.3, 0.3], - [-0.02, 0.02], [-0.02, 0.02], [-0.02, 0.02], - [-0.2, 0.4], [-0.3, 0.3], [-0.3, 0.3] + [-0.2, 0.4], + [-0.3, 0.3], + [-0.3, 0.3], + [-0.02, 0.02], + [-0.02, 0.02], + [-0.02, 0.02], + [-0.2, 0.4], + [-0.3, 0.3], + [-0.3, 0.3], ], - meas_kernels=['kernel1'], - discriminators=['max_1Q_fidelity'], + meas_kernels=["kernel1"], + discriminators=["max_1Q_fidelity"], acquisition_latency=[[100, 100], [100, 100], [100, 100]], conditional_latency=[ - [100, 1000], [1000, 100], [100, 1000], - [100, 1000], [1000, 100], [100, 1000], - [1000, 100], [100, 1000], [1000, 100] + [100, 1000], + [1000, 100], + [100, 1000], + [100, 1000], + [1000, 100], + [100, 1000], + [1000, 100], + [100, 1000], + [1000, 100], ], channels={ - 'acquire0': { - 'type': 'acquire', - 'purpose': 'acquire', - 'operates': {'qubits': [0]} + "acquire0": {"type": "acquire", "purpose": "acquire", "operates": {"qubits": [0]}}, + "acquire1": {"type": "acquire", "purpose": "acquire", "operates": {"qubits": [1]}}, + "acquire2": {"type": "acquire", "purpose": "acquire", "operates": {"qubits": [2]}}, + "d0": {"type": "drive", "purpose": "drive", "operates": {"qubits": [0]}}, + "d1": {"type": "drive", "purpose": "drive", "operates": {"qubits": [1]}}, + "d2": {"type": "drive", "purpose": "drive", "operates": {"qubits": [2]}}, + "m0": {"type": "measure", "purpose": "measure", "operates": {"qubits": [0]}}, + "m1": {"type": "measure", "purpose": "measure", "operates": {"qubits": [1]}}, + "m2": {"type": "measure", "purpose": "measure", "operates": {"qubits": [2]}}, + "u0": { + "type": "control", + "purpose": "cross-resonance", + "operates": {"qubits": [0, 1]}, }, - 'acquire1': { - 'type': 'acquire', - 'purpose': 'acquire', - 'operates': {'qubits': [1]} + "u1": { + "type": "control", + "purpose": "cross-resonance", + "operates": {"qubits": [1, 0]}, }, - 'acquire2': { - 'type': 'acquire', - 'purpose': 'acquire', - 'operates': {'qubits': [2]} + "u2": { + "type": "control", + "purpose": "cross-resonance", + "operates": {"qubits": [2, 1]}, }, - 'd0': { - 'type': 'drive', - 'purpose': 'drive', - 'operates': {'qubits': [0]} - }, - 'd1': { - 'type': 'drive', - 'purpose': 'drive', - 'operates': {'qubits': [1]} - }, - 'd2': { - 'type': 'drive', - 'purpose': 'drive', - 'operates': {'qubits': [2]} - }, - 'm0': { - 'type': 'measure', - 'purpose': 'measure', - 'operates': {'qubits': [0]} - }, - 'm1': { - 'type': 'measure', - 'purpose': 'measure', - 'operates': {'qubits': [1]} - }, - 'm2': { - 'type': 'measure', - 'purpose': 'measure', - 'operates': {'qubits': [2]} - }, - 'u0': { - 'type': 'control', - 'purpose': 'cross-resonance', - 'operates': {'qubits': [0, 1]} - }, - 'u1': { - 'type': 'control', - 'purpose': 'cross-resonance', - 'operates': {'qubits': [1, 0]} - }, - 'u2': { - 'type': 'control', - 'purpose': 'cross-resonance', - 'operates': {'qubits': [2, 1]} - } - } + }, ) - self._defaults = PulseDefaults.from_dict({ - 'qubit_freq_est': [4.9, 5.0, 4.8], - 'meas_freq_est': [6.5, 6.6, 6.4], - 'buffer': 10, - 'pulse_library': [ - { - 'name': 'x90p_d0', - 'samples': 2 * [0.1 + 0j] - }, - { - 'name': 'x90p_d1', - 'samples': 2 * [0.1 + 0j] - }, - { - 'name': 'x90p_d2', - 'samples': 2 * [0.1 + 0j] - }, - { - 'name': 'x90m_d0', - 'samples': 2 * [-0.1 + 0j] - }, - { - 'name': 'x90m_d1', - 'samples': 2 * [-0.1 + 0j] - }, - { - 'name': 'x90m_d2', - 'samples': 2 * [-0.1 + 0j] - }, - { - 'name': 'y90p_d0', - 'samples': 2 * [0.1j] - }, - { - 'name': 'y90p_d1', - 'samples': 2 * [0.1j] - }, - { - 'name': 'y90p_d2', - 'samples': 2 * [0.1j] - }, - { - 'name': 'xp_d0', - 'samples': 2 * [0.2 + 0j] - }, - { - 'name': 'ym_d0', - 'samples': 2 * [-0.2j] - }, - { - 'name': 'xp_d1', - 'samples': 2 * [0.2 + 0j] - }, - { - 'name': 'ym_d1', - 'samples': 2 * [-0.2j] - }, - { - 'name': 'cr90p_u0', - 'samples': 9 * [0.1 + 0j] - }, - { - 'name': 'cr90m_u0', - 'samples': 9 * [-0.1 + 0j] - }, - { - 'name': 'cr90p_u1', - 'samples': 9 * [0.1 + 0j] - }, - { - 'name': 'cr90m_u1', - 'samples': 9 * [-0.1 + 0j] - }, - { - 'name': 'measure_m0', - 'samples': 10 * [0.1 + 0j] - }, - { - 'name': 'measure_m1', - 'samples': 10 * [0.1 + 0j] - }, - { - 'name': 'measure_m2', - 'samples': 10 * [0.1 + 0j] - } - ], - - 'cmd_def': [ - Command.from_dict({ - 'name': 'u1', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u1', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u1', - 'qubits': [2], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d2', - t0=0, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u2', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P1').to_dict(), - PulseQobjInstruction(name='y90p_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=2, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u2', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P1').to_dict(), - PulseQobjInstruction(name='y90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=2, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u2', - 'qubits': [2], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d2', - t0=0, - phase='-P1').to_dict(), - PulseQobjInstruction(name='y90p_d2', - ch='d2', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d2', - t0=2, - phase='-P0').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u3', - 'qubits': [0], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase='-P2').to_dict(), - PulseQobjInstruction(name='x90p_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=2, - phase='-P0').to_dict(), - PulseQobjInstruction(name='x90m_d0', - ch='d0', - t0=2).to_dict(), - PulseQobjInstruction(name='fc', - ch='d0', - t0=4, - phase='-P1').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u3', - 'qubits': [1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase='-P2').to_dict(), - PulseQobjInstruction(name='x90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=2, - phase='-P0').to_dict(), - PulseQobjInstruction(name='x90m_d1', - ch='d1', - t0=2).to_dict(), - PulseQobjInstruction(name='fc', - ch='d1', - t0=4, - phase='-P1').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'u3', - 'qubits': [2], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d2', - t0=0, - phase='-P2').to_dict(), - PulseQobjInstruction(name='x90p_d2', - ch='d2', - t0=0).to_dict(), - PulseQobjInstruction(name='fc', - ch='d2', - t0=2, - phase='-P0').to_dict(), - PulseQobjInstruction(name='x90m_d2', - ch='d2', - t0=2).to_dict(), - PulseQobjInstruction(name='fc', - ch='d2', - t0=4, - phase='-P1').to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'cx', - 'qubits': [0, 1], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d0', - t0=0, - phase=1.57).to_dict(), - PulseQobjInstruction(name='ym_d0', - ch='d0', - t0=0).to_dict(), - PulseQobjInstruction(name='xp_d0', - ch='d0', - t0=11).to_dict(), - PulseQobjInstruction(name='x90p_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='cr90p_u0', - ch='u0', - t0=2).to_dict(), - PulseQobjInstruction(name='cr90m_u0', - ch='u0', - t0=13).to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'cx', - 'qubits': [1, 2], - 'sequence': [ - PulseQobjInstruction(name='fc', - ch='d1', - t0=0, - phase=1.57).to_dict(), - PulseQobjInstruction(name='ym_d1', - ch='d1', - t0=0).to_dict(), - PulseQobjInstruction(name='xp_d1', - ch='d1', - t0=11).to_dict(), - PulseQobjInstruction(name='x90p_d2', - ch='d2', - t0=0).to_dict(), - PulseQobjInstruction(name='cr90p_u1', - ch='u1', - t0=2).to_dict(), - PulseQobjInstruction(name='cr90m_u1', - ch='u1', - t0=13).to_dict()]}).to_dict(), - Command.from_dict({ - 'name': 'measure', - 'qubits': [0, 1, 2], - 'sequence': [ - PulseQobjInstruction(name='measure_m0', - ch='m0', - t0=0).to_dict(), - PulseQobjInstruction(name='measure_m1', - ch='m1', - t0=0).to_dict(), - PulseQobjInstruction(name='measure_m2', - ch='m2', - t0=0).to_dict(), - PulseQobjInstruction(name='acquire', - duration=10, - t0=0, - qubits=[0, 1, 2], - memory_slot=[0, 1, 2]).to_dict()] - }).to_dict() - ] - }) + self._defaults = PulseDefaults.from_dict( + { + "qubit_freq_est": [4.9, 5.0, 4.8], + "meas_freq_est": [6.5, 6.6, 6.4], + "buffer": 10, + "pulse_library": [ + {"name": "x90p_d0", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d1", "samples": 2 * [0.1 + 0j]}, + {"name": "x90p_d2", "samples": 2 * [0.1 + 0j]}, + {"name": "x90m_d0", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d1", "samples": 2 * [-0.1 + 0j]}, + {"name": "x90m_d2", "samples": 2 * [-0.1 + 0j]}, + {"name": "y90p_d0", "samples": 2 * [0.1j]}, + {"name": "y90p_d1", "samples": 2 * [0.1j]}, + {"name": "y90p_d2", "samples": 2 * [0.1j]}, + {"name": "xp_d0", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d0", "samples": 2 * [-0.2j]}, + {"name": "xp_d1", "samples": 2 * [0.2 + 0j]}, + {"name": "ym_d1", "samples": 2 * [-0.2j]}, + {"name": "cr90p_u0", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u0", "samples": 9 * [-0.1 + 0j]}, + {"name": "cr90p_u1", "samples": 9 * [0.1 + 0j]}, + {"name": "cr90m_u1", "samples": 9 * [-0.1 + 0j]}, + {"name": "measure_m0", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m1", "samples": 10 * [0.1 + 0j]}, + {"name": "measure_m2", "samples": 10 * [0.1 + 0j]}, + ], + "cmd_def": [ + Command.from_dict( + { + "name": "u1", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u1", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P0" + ).to_dict() + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u2", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P1" + ).to_dict(), + PulseQobjInstruction(name="y90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=2, phase="-P0" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [0], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d0", ch="d0", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d0", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d1", ch="d1", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d1", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "u3", + "qubits": [2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d2", t0=0, phase="-P2" + ).to_dict(), + PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=2, phase="-P0" + ).to_dict(), + PulseQobjInstruction(name="x90m_d2", ch="d2", t0=2).to_dict(), + PulseQobjInstruction( + name="fc", ch="d2", t0=4, phase="-P1" + ).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [0, 1], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d0", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d0", ch="d0", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d0", ch="d0", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u0", ch="u0", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u0", ch="u0", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "cx", + "qubits": [1, 2], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d1", t0=0, phase=1.57 + ).to_dict(), + PulseQobjInstruction(name="ym_d1", ch="d1", t0=0).to_dict(), + PulseQobjInstruction(name="xp_d1", ch="d1", t0=11).to_dict(), + PulseQobjInstruction(name="x90p_d2", ch="d2", t0=0).to_dict(), + PulseQobjInstruction(name="cr90p_u1", ch="u1", t0=2).to_dict(), + PulseQobjInstruction(name="cr90m_u1", ch="u1", t0=13).to_dict(), + ], + } + ).to_dict(), + Command.from_dict( + { + "name": "measure", + "qubits": [0, 1, 2], + "sequence": [ + PulseQobjInstruction(name="measure_m0", ch="m0", t0=0).to_dict(), + PulseQobjInstruction(name="measure_m1", ch="m1", t0=0).to_dict(), + PulseQobjInstruction(name="measure_m2", ch="m2", t0=0).to_dict(), + PulseQobjInstruction( + name="acquire", + duration=10, + t0=0, + qubits=[0, 1, 2], + memory_slot=[0, 1, 2], + ).to_dict(), + ], + } + ).to_dict(), + ], + } + ) super().__init__(configuration) def defaults(self): # pylint: disable=missing-function-docstring diff --git a/qiskit/test/mock/fake_provider.py b/qiskit/test/mock/fake_provider.py index c06023e80b75..c9d7112cabd1 100644 --- a/qiskit/test/mock/fake_provider.py +++ b/qiskit/test/mock/fake_provider.py @@ -35,8 +35,7 @@ class FakeProvider(ProviderV1): def get_backend(self, name=None, **kwargs): backend = self._backends[0] if name: - filtered_backends = [backend for backend in self._backends - if backend.name() == name] + filtered_backends = [backend for backend in self._backends if backend.name() == name] if not filtered_backends: raise QiskitBackendNotFoundError() @@ -48,44 +47,46 @@ def backends(self, name=None, **kwargs): return self._backends def __init__(self): - self._backends = [FakeAlmaden(), - FakeArmonk(), - FakeAthens(), - FakeBelem(), - FakeBoeblingen(), - FakeBogota(), - FakeBurlington(), - FakeCambridge(), - FakeCambridgeAlternativeBasis(), - FakeCasablanca(), - FakeEssex(), - FakeGuadalupe(), - FakeJohannesburg(), - FakeLima(), - FakeLondon(), - FakeManhattan(), - FakeMelbourne(), - FakeMontreal(), - FakeMumbai(), - FakeOpenPulse2Q(), - FakeOpenPulse3Q(), - FakeOurense(), - FakeParis(), - FakePoughkeepsie(), - FakeQasmSimulator(), - FakeQuito(), - FakeRochester(), - FakeRome(), - FakeRueschlikon(), - FakeSantiago(), - FakeSingapore(), - FakeSydney(), - FakeTenerife(), - FakeTokyo(), - FakeToronto(), - FakeValencia(), - FakeVigo(), - FakeYorktown()] + self._backends = [ + FakeAlmaden(), + FakeArmonk(), + FakeAthens(), + FakeBelem(), + FakeBoeblingen(), + FakeBogota(), + FakeBurlington(), + FakeCambridge(), + FakeCambridgeAlternativeBasis(), + FakeCasablanca(), + FakeEssex(), + FakeGuadalupe(), + FakeJohannesburg(), + FakeLima(), + FakeLondon(), + FakeManhattan(), + FakeMelbourne(), + FakeMontreal(), + FakeMumbai(), + FakeOpenPulse2Q(), + FakeOpenPulse3Q(), + FakeOurense(), + FakeParis(), + FakePoughkeepsie(), + FakeQasmSimulator(), + FakeQuito(), + FakeRochester(), + FakeRome(), + FakeRueschlikon(), + FakeSantiago(), + FakeSingapore(), + FakeSydney(), + FakeTenerife(), + FakeTokyo(), + FakeToronto(), + FakeValencia(), + FakeVigo(), + FakeYorktown(), + ] super().__init__() @@ -139,8 +140,7 @@ class FakeLegacyProvider(BaseProvider): def get_backend(self, name=None, **kwargs): backend = self._backends[0] if name: - filtered_backends = [backend for backend in self._backends - if backend.name() == name] + filtered_backends = [backend for backend in self._backends if backend.name() == name] if not filtered_backends: raise QiskitBackendNotFoundError() @@ -152,39 +152,41 @@ def backends(self, name=None, **kwargs): return self._backends def __init__(self): - self._backends = [FakeLegacyAlmaden(), - FakeLegacyArmonk(), - FakeLegacyAthens(), - FakeLegacyBelem(), - FakeLegacyBoeblingen(), - FakeLegacyBogota(), - FakeLegacyBurlington(), - FakeLegacyCambridge(), - FakeLegacyCambridgeAlternativeBasis(), - FakeLegacyCasablanca(), - FakeLegacyEssex(), - FakeLegacyJohannesburg(), - FakeLegacyLima(), - FakeLegacyLondon(), - FakeLegacyManhattan(), - FakeLegacyMelbourne(), - FakeLegacyMontreal(), - FakeLegacyMumbai(), - FakeLegacyOurense(), - FakeLegacyParis(), - FakeLegacyPoughkeepsie(), - FakeLegacyQuito(), - FakeLegacyRochester(), - FakeLegacyRome(), - FakeLegacyRueschlikon(), - FakeLegacySantiago(), - FakeLegacySingapore(), - FakeLegacySydney(), - FakeLegacyTenerife(), - FakeLegacyTokyo(), - FakeLegacyToronto(), - FakeLegacyValencia(), - FakeLegacyVigo(), - FakeLegacyYorktown()] + self._backends = [ + FakeLegacyAlmaden(), + FakeLegacyArmonk(), + FakeLegacyAthens(), + FakeLegacyBelem(), + FakeLegacyBoeblingen(), + FakeLegacyBogota(), + FakeLegacyBurlington(), + FakeLegacyCambridge(), + FakeLegacyCambridgeAlternativeBasis(), + FakeLegacyCasablanca(), + FakeLegacyEssex(), + FakeLegacyJohannesburg(), + FakeLegacyLima(), + FakeLegacyLondon(), + FakeLegacyManhattan(), + FakeLegacyMelbourne(), + FakeLegacyMontreal(), + FakeLegacyMumbai(), + FakeLegacyOurense(), + FakeLegacyParis(), + FakeLegacyPoughkeepsie(), + FakeLegacyQuito(), + FakeLegacyRochester(), + FakeLegacyRome(), + FakeLegacyRueschlikon(), + FakeLegacySantiago(), + FakeLegacySingapore(), + FakeLegacySydney(), + FakeLegacyTenerife(), + FakeLegacyTokyo(), + FakeLegacyToronto(), + FakeLegacyValencia(), + FakeLegacyVigo(), + FakeLegacyYorktown(), + ] super().__init__() diff --git a/qiskit/test/mock/fake_pulse_backend.py b/qiskit/test/mock/fake_pulse_backend.py index 5b55f80cf14e..11ff0a5811da 100644 --- a/qiskit/test/mock/fake_pulse_backend.py +++ b/qiskit/test/mock/fake_pulse_backend.py @@ -14,7 +14,7 @@ Fake backend abstract class for mock backends supporting OpenPulse. """ -from qiskit.providers.models import (PulseBackendConfiguration, PulseDefaults) +from qiskit.providers.models import PulseBackendConfiguration, PulseDefaults from qiskit.test.mock.fake_qasm_backend import FakeQasmBackend, FakeQasmLegacyBackend from qiskit.exceptions import QiskitError from qiskit.test.mock.utils.json_decoder import decode_pulse_defaults diff --git a/qiskit/test/mock/fake_qasm_backend.py b/qiskit/test/mock/fake_qasm_backend.py index 793d661a04ca..fe5dce37df8c 100644 --- a/qiskit/test/mock/fake_qasm_backend.py +++ b/qiskit/test/mock/fake_qasm_backend.py @@ -19,8 +19,10 @@ from qiskit.providers.models import BackendProperties, QasmBackendConfiguration from qiskit.test.mock.fake_backend import FakeBackend, FakeLegacyBackend -from qiskit.test.mock.utils.json_decoder import (decode_backend_configuration, - decode_backend_properties) +from qiskit.test.mock.utils.json_decoder import ( + decode_backend_configuration, + decode_backend_properties, +) from qiskit.exceptions import QiskitError diff --git a/qiskit/test/mock/fake_qasm_simulator.py b/qiskit/test/mock/fake_qasm_simulator.py index c06e4d136635..294ad75ab88e 100644 --- a/qiskit/test/mock/fake_qasm_simulator.py +++ b/qiskit/test/mock/fake_qasm_simulator.py @@ -23,10 +23,10 @@ class FakeQasmSimulator(FakeBackend): def __init__(self): configuration = QasmBackendConfiguration( - backend_name='fake_qasm_simulator', - backend_version='0.0.0', + backend_name="fake_qasm_simulator", + backend_version="0.0.0", n_qubits=5, - basis_gates=['u1', 'u2', 'u3', 'cx', 'id', 'unitary'], + basis_gates=["u1", "u2", "u3", "cx", "id", "unitary"], coupling_map=None, simulator=True, local=True, @@ -34,9 +34,9 @@ def __init__(self): open_pulse=False, memory=True, max_shots=65536, - gates=[GateConfig(name='TODO', parameters=[], qasm_def='TODO')], + gates=[GateConfig(name="TODO", parameters=[], qasm_def="TODO")], dt=1.3333, - dtm=10.5 + dtm=10.5, ) super().__init__(configuration) diff --git a/qiskit/test/mock/fake_qobj.py b/qiskit/test/mock/fake_qobj.py index cfadc0fd7e43..76d9eb6528c2 100644 --- a/qiskit/test/mock/fake_qobj.py +++ b/qiskit/test/mock/fake_qobj.py @@ -14,24 +14,30 @@ Base Fake Qobj. """ -from qiskit.qobj import (QasmQobj, QobjExperimentHeader, QobjHeader, - QasmQobjInstruction, QasmQobjExperimentConfig, - QasmQobjExperiment, QasmQobjConfig) +from qiskit.qobj import ( + QasmQobj, + QobjExperimentHeader, + QobjHeader, + QasmQobjInstruction, + QasmQobjExperimentConfig, + QasmQobjExperiment, + QasmQobjConfig, +) from .fake_qasm_simulator import FakeQasmSimulator class FakeQobj(QasmQobj): """A fake `Qobj` instance.""" + def __init__(self): - qobj_id = 'test_id' + qobj_id = "test_id" config = QasmQobjConfig(shots=1024, memory_slots=1, max_credits=100) header = QobjHeader(backend_name=FakeQasmSimulator().name()) - experiments = [QasmQobjExperiment( - instructions=[ - QasmQobjInstruction(name='barrier', qubits=[1]) - ], - header=QobjExperimentHeader(), - config=QasmQobjExperimentConfig(seed=123456) - )] - super().__init__(qobj_id=qobj_id, config=config, - experiments=experiments, header=header) + experiments = [ + QasmQobjExperiment( + instructions=[QasmQobjInstruction(name="barrier", qubits=[1])], + header=QobjExperimentHeader(), + config=QasmQobjExperimentConfig(seed=123456), + ) + ] + super().__init__(qobj_id=qobj_id, config=config, experiments=experiments, header=header) diff --git a/qiskit/test/mock/utils/configurable_backend.py b/qiskit/test/mock/utils/configurable_backend.py index 53cb59d336be..e5eef82ce09c 100644 --- a/qiskit/test/mock/utils/configurable_backend.py +++ b/qiskit/test/mock/utils/configurable_backend.py @@ -18,9 +18,13 @@ import numpy as np from qiskit.exceptions import QiskitError -from qiskit.providers.models import (PulseBackendConfiguration, - BackendProperties, PulseDefaults, - Command, UchannelLO) +from qiskit.providers.models import ( + PulseBackendConfiguration, + BackendProperties, + PulseDefaults, + Command, + UchannelLO, +) from qiskit.providers.models.backendproperties import Nduv, Gate from qiskit.qobj import PulseQobjInstruction from qiskit.test.mock.fake_backend import FakeBackend @@ -29,20 +33,22 @@ class ConfigurableFakeBackend(FakeBackend): """Configurable backend.""" - def __init__(self, - name: str, - n_qubits: int, - version: Optional[str] = None, - coupling_map: Optional[List[List[int]]] = None, - basis_gates: Optional[List[str]] = None, - qubit_t1: Optional[Union[float, List[float]]] = None, - qubit_t2: Optional[Union[float, List[float]]] = None, - qubit_frequency: Optional[Union[float, List[float]]] = None, - qubit_readout_error: Optional[Union[float, List[float]]] = None, - single_qubit_gates: Optional[List[str]] = None, - dt: Optional[float] = None, - std: Optional[float] = None, - seed: Optional[int] = None): + def __init__( + self, + name: str, + n_qubits: int, + version: Optional[str] = None, + coupling_map: Optional[List[List[int]]] = None, + basis_gates: Optional[List[str]] = None, + qubit_t1: Optional[Union[float, List[float]]] = None, + qubit_t2: Optional[Union[float, List[float]]] = None, + qubit_frequency: Optional[Union[float, List[float]]] = None, + qubit_readout_error: Optional[Union[float, List[float]]] = None, + single_qubit_gates: Optional[List[str]] = None, + dt: Optional[float] = None, + std: Optional[float] = None, + seed: Optional[int] = None, + ): """Creates backend based on provided configuration. Args: @@ -63,36 +69,32 @@ def __init__(self, np.random.seed(seed) if version is None: - version = '0.0.0' + version = "0.0.0" if basis_gates is None: - basis_gates = ['id', 'u1', 'u2', 'u3', 'cx'] + basis_gates = ["id", "u1", "u2", "u3", "cx"] if std is None: std = 0.01 if not isinstance(qubit_t1, list): - qubit_t1 = np.random.normal(loc=qubit_t1 or 113., - scale=std, - size=n_qubits).tolist() + qubit_t1 = np.random.normal(loc=qubit_t1 or 113.0, scale=std, size=n_qubits).tolist() if not isinstance(qubit_t2, list): - qubit_t2 = np.random.normal(loc=qubit_t1 or 150.2, - scale=std, - size=n_qubits).tolist() + qubit_t2 = np.random.normal(loc=qubit_t1 or 150.2, scale=std, size=n_qubits).tolist() if not isinstance(qubit_frequency, list): - qubit_frequency = np.random.normal(loc=qubit_frequency or 4.8, - scale=std, - size=n_qubits).tolist() + qubit_frequency = np.random.normal( + loc=qubit_frequency or 4.8, scale=std, size=n_qubits + ).tolist() if not isinstance(qubit_readout_error, list): - qubit_readout_error = np.random.normal(loc=qubit_readout_error or 0.04, - scale=std, - size=n_qubits).tolist() + qubit_readout_error = np.random.normal( + loc=qubit_readout_error or 0.04, scale=std, size=n_qubits + ).tolist() if single_qubit_gates is None: - single_qubit_gates = ['id', 'u1', 'u2', 'u3'] + single_qubit_gates = ["id", "u1", "u2", "u3"] if dt is None: dt = 1.33 @@ -152,78 +154,91 @@ def _build_props(self) -> BackendProperties: qubits = [] gates = [] - for (qubit_t1, qubit_t2, freq, read_err) in zip(self.qubit_t1, - self.qubit_t2, - self.qubit_frequency, - self.qubit_readout_error): - qubits.append([ - Nduv(date=self.now, name='T1', unit='µs', value=qubit_t1), - Nduv(date=self.now, name='T2', unit='µs', value=qubit_t2), - Nduv(date=self.now, name='frequency', unit='GHz', value=freq), - Nduv(date=self.now, name='readout_error', unit='', value=read_err) - ]) + for (qubit_t1, qubit_t2, freq, read_err) in zip( + self.qubit_t1, self.qubit_t2, self.qubit_frequency, self.qubit_readout_error + ): + qubits.append( + [ + Nduv(date=self.now, name="T1", unit="µs", value=qubit_t1), + Nduv(date=self.now, name="T2", unit="µs", value=qubit_t2), + Nduv(date=self.now, name="frequency", unit="GHz", value=freq), + Nduv(date=self.now, name="readout_error", unit="", value=read_err), + ] + ) for gate in self.basis_gates: - parameters = [Nduv(date=self.now, name='gate_error', unit='', value=0.01), - Nduv(date=self.now, name='gate_length', unit='ns', value=4*self.dt)] + parameters = [ + Nduv(date=self.now, name="gate_error", unit="", value=0.01), + Nduv(date=self.now, name="gate_length", unit="ns", value=4 * self.dt), + ] if gate in self.single_qubit_gates: for i in range(self.n_qubits): - gates.append(Gate(gate=gate, name="{}_{}".format(gate, i), - qubits=[i], parameters=parameters)) - elif gate == 'cx': + gates.append( + Gate( + gate=gate, + name="{}_{}".format(gate, i), + qubits=[i], + parameters=parameters, + ) + ) + elif gate == "cx": for (qubit1, qubit2) in list(itertools.combinations(range(self.n_qubits), 2)): - gates.append(Gate(gate=gate, - name="{gate}{q1}_{q2}".format(gate=gate, - q1=qubit1, - q2=qubit2), - qubits=[qubit1, qubit2], - parameters=parameters)) + gates.append( + Gate( + gate=gate, + name="{gate}{q1}_{q2}".format(gate=gate, q1=qubit1, q2=qubit2), + qubits=[qubit1, qubit2], + parameters=parameters, + ) + ) else: - raise QiskitError("{gate} is not supported by fake backend builder." - "".format(gate=gate)) + raise QiskitError( + "{gate} is not supported by fake backend builder." "".format(gate=gate) + ) - return BackendProperties(backend_name=self.name, - backend_version=self.version, - last_update_date=self.now, - qubits=qubits, - gates=gates, - general=[]) + return BackendProperties( + backend_name=self.name, + backend_version=self.version, + last_update_date=self.now, + qubits=qubits, + gates=gates, + general=[], + ) def _build_conf(self) -> PulseBackendConfiguration: """Build configuration for backend.""" h_str = [ - ",".join(["_SUM[i,0,{n_qubits}".format(n_qubits=self.n_qubits), - "wq{i}/2*(I{i}-Z{i})]"]), - ",".join(["_SUM[i,0,{n_qubits}".format(n_qubits=self.n_qubits), - "omegad{i}*X{i}||D{i}]"]) + ",".join( + ["_SUM[i,0,{n_qubits}".format(n_qubits=self.n_qubits), "wq{i}/2*(I{i}-Z{i})]"] + ), + ",".join( + ["_SUM[i,0,{n_qubits}".format(n_qubits=self.n_qubits), "omegad{i}*X{i}||D{i}]"] + ), ] variables = [] for (qubit1, qubit2) in self.coupling_map: h_str += [ "jq{q1}q{q2}*Sp{q1}*Sm{q2}".format(q1=qubit1, q2=qubit2), - "jq{q1}q{q2}*Sm{q1}*Sp{q2}".format(q1=qubit1, q2=qubit2) + "jq{q1}q{q2}*Sm{q1}*Sp{q2}".format(q1=qubit1, q2=qubit2), ] variables.append(("jq{q1}q{q2}".format(q1=qubit1, q2=qubit2), 0)) for i, (qubit1, qubit2) in enumerate(self.coupling_map): h_str.append("omegad{}*X{}||U{}".format(qubit1, qubit2, i)) for i in range(self.n_qubits): - variables += [ - ("omegad{}".format(i), 0), - ("wq{}".format(i), 0) - ] + variables += [("omegad{}".format(i), 0), ("wq{}".format(i), 0)] hamiltonian = { - 'h_str': h_str, - 'description': 'Hamiltonian description for {} qubits backend.'.format(self.n_qubits), - 'qub': {i: 2 for i in range(self.n_qubits)}, - 'vars': dict(variables) + "h_str": h_str, + "description": "Hamiltonian description for {} qubits backend.".format(self.n_qubits), + "qub": {i: 2 for i in range(self.n_qubits)}, + "vars": dict(variables), } meas_map = [list(range(self.n_qubits))] qubit_lo_range = [[freq - 0.5, freq + 0.5] for freq in self.qubit_frequency] meas_lo_range = [[6.5, 7.5] for _ in range(self.n_qubits)] - u_channel_lo = [[UchannelLO(q=i, scale=1.0+0.0j)] for i in range(len(self.coupling_map))] + u_channel_lo = [[UchannelLO(q=i, scale=1.0 + 0.0j)] for i in range(len(self.coupling_map))] return PulseBackendConfiguration( backend_name=self.name, @@ -250,11 +265,11 @@ def _build_conf(self) -> PulseBackendConfiguration: rep_times=[1000], meas_map=meas_map, channel_bandwidth=[], - meas_kernels=['kernel1'], - discriminators=['max_1Q_fidelity'], + meas_kernels=["kernel1"], + discriminators=["max_1Q_fidelity"], acquisition_latency=[], conditional_latency=[], - hamiltonian=hamiltonian + hamiltonian=hamiltonian, ) def _build_defaults(self) -> PulseDefaults: @@ -263,75 +278,88 @@ def _build_defaults(self) -> PulseDefaults: qubit_freq_est = self.qubit_frequency meas_freq_est = np.linspace(6.4, 6.6, self.n_qubits).tolist() pulse_library = [ + {"name": "test_pulse_1", "samples": [[0.0, 0.0], [0.0, 0.1]]}, + {"name": "test_pulse_2", "samples": [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0]]}, + {"name": "test_pulse_3", "samples": [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0], [0.5, 0.0]]}, { - 'name': 'test_pulse_1', - 'samples': [[0.0, 0.0], [0.0, 0.1]] - }, - { - 'name': 'test_pulse_2', - 'samples': [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0]] - }, - { - 'name': 'test_pulse_3', - 'samples': [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0], [0.5, 0.0]] + "name": "test_pulse_4", + "samples": 7 * [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0], [0.5, 0.0]], }, - { - 'name': 'test_pulse_4', - 'samples': 7 * [[0.0, 0.0], [0.0, 0.1], [0.0, 1.0], [0.5, 0.0]] - } ] - measure_command_sequence = [PulseQobjInstruction(name='acquire', duration=10, t0=0, - qubits=list(range(self.n_qubits)), - memory_slot=list(range(self.n_qubits)) - ).to_dict()] - measure_command_sequence += [PulseQobjInstruction(name='test_pulse_1', - ch='m{}'.format(i), t0=0).to_dict() - for i in range(self.n_qubits)] + measure_command_sequence = [ + PulseQobjInstruction( + name="acquire", + duration=10, + t0=0, + qubits=list(range(self.n_qubits)), + memory_slot=list(range(self.n_qubits)), + ).to_dict() + ] + measure_command_sequence += [ + PulseQobjInstruction(name="test_pulse_1", ch="m{}".format(i), t0=0).to_dict() + for i in range(self.n_qubits) + ] - measure_command = Command.from_dict({ - 'name': 'measure', - 'qubits': list(range(self.n_qubits)), - 'sequence': measure_command_sequence - }).to_dict() + measure_command = Command.from_dict( + { + "name": "measure", + "qubits": list(range(self.n_qubits)), + "sequence": measure_command_sequence, + } + ).to_dict() cmd_def = [measure_command] for gate in self.single_qubit_gates: for i in range(self.n_qubits): - cmd_def.append(Command.from_dict({ - 'name': gate, - 'qubits': [i], - 'sequence': [PulseQobjInstruction(name='fc', ch='d{}'.format(i), - t0=0, phase='-P0').to_dict(), - PulseQobjInstruction(name='test_pulse_3', - ch='d{}'.format(i), - t0=0).to_dict()] - }).to_dict()) + cmd_def.append( + Command.from_dict( + { + "name": gate, + "qubits": [i], + "sequence": [ + PulseQobjInstruction( + name="fc", ch="d{}".format(i), t0=0, phase="-P0" + ).to_dict(), + PulseQobjInstruction( + name="test_pulse_3", ch="d{}".format(i), t0=0 + ).to_dict(), + ], + } + ).to_dict() + ) for qubit1, qubit2 in self.coupling_map: cmd_def += [ - Command.from_dict({ - 'name': 'cx', - 'qubits': [qubit1, qubit2], - 'sequence': [PulseQobjInstruction(name='test_pulse_1', - ch='d{}'.format(qubit1), - t0=0).to_dict(), - PulseQobjInstruction(name='test_pulse_2', - ch='u{}'.format(qubit1), - t0=10).to_dict(), - PulseQobjInstruction(name='test_pulse_1', - ch='d{}'.format(qubit2), - t0=20).to_dict(), - PulseQobjInstruction(name='fc', ch='d{}'.format(qubit2), - t0=20, phase=2.1).to_dict()] - }).to_dict() + Command.from_dict( + { + "name": "cx", + "qubits": [qubit1, qubit2], + "sequence": [ + PulseQobjInstruction( + name="test_pulse_1", ch="d{}".format(qubit1), t0=0 + ).to_dict(), + PulseQobjInstruction( + name="test_pulse_2", ch="u{}".format(qubit1), t0=10 + ).to_dict(), + PulseQobjInstruction( + name="test_pulse_1", ch="d{}".format(qubit2), t0=20 + ).to_dict(), + PulseQobjInstruction( + name="fc", ch="d{}".format(qubit2), t0=20, phase=2.1 + ).to_dict(), + ], + } + ).to_dict() ] - return PulseDefaults.from_dict({ - 'qubit_freq_est': qubit_freq_est, - 'meas_freq_est': meas_freq_est, - 'buffer': 0, - 'pulse_library': pulse_library, - 'cmd_def': cmd_def - }) + return PulseDefaults.from_dict( + { + "qubit_freq_est": qubit_freq_est, + "meas_freq_est": meas_freq_est, + "buffer": 0, + "pulse_library": pulse_library, + "cmd_def": cmd_def, + } + ) diff --git a/qiskit/test/mock/utils/json_decoder.py b/qiskit/test/mock/utils/json_decoder.py index 7d13429306bf..3a861592bdb4 100644 --- a/qiskit/test/mock/utils/json_decoder.py +++ b/qiskit/test/mock/utils/json_decoder.py @@ -25,12 +25,12 @@ def decode_pulse_defaults(defaults: Dict) -> None: Args: defaults: A ``PulseDefaults`` in dictionary format. """ - for item in defaults['pulse_library']: + for item in defaults["pulse_library"]: _decode_pulse_library_item(item) - for cmd in defaults['cmd_def']: - if 'sequence' in cmd: - for instr in cmd['sequence']: + for cmd in defaults["cmd_def"]: + if "sequence" in cmd: + for instr in cmd["sequence"]: _decode_pulse_qobj_instr(instr) @@ -40,15 +40,15 @@ def decode_backend_properties(properties: Dict) -> None: Args: properties: A ``BackendProperties`` in dictionary format. """ - properties['last_update_date'] = dateutil.parser.isoparse(properties['last_update_date']) - for qubit in properties['qubits']: + properties["last_update_date"] = dateutil.parser.isoparse(properties["last_update_date"]) + for qubit in properties["qubits"]: for nduv in qubit: - nduv['date'] = dateutil.parser.isoparse(nduv['date']) - for gate in properties['gates']: - for param in gate['parameters']: - param['date'] = dateutil.parser.isoparse(param['date']) - for gen in properties['general']: - gen['date'] = dateutil.parser.isoparse(gen['date']) + nduv["date"] = dateutil.parser.isoparse(nduv["date"]) + for gate in properties["gates"]: + for param in gate["parameters"]: + param["date"] = dateutil.parser.isoparse(param["date"]) + for gen in properties["general"]: + gen["date"] = dateutil.parser.isoparse(gen["date"]) def decode_backend_configuration(config: Dict) -> None: @@ -58,12 +58,12 @@ def decode_backend_configuration(config: Dict) -> None: config: A ``QasmBackendConfiguration`` or ``PulseBackendConfiguration`` in dictionary format. """ - config['online_date'] = dateutil.parser.isoparse(config['online_date']) + config["online_date"] = dateutil.parser.isoparse(config["online_date"]) - if 'u_channel_lo' in config: - for u_channle_list in config['u_channel_lo']: + if "u_channel_lo" in config: + for u_channle_list in config["u_channel_lo"]: for u_channle_lo in u_channle_list: - u_channle_lo['scale'] = _to_complex(u_channle_lo['scale']) + u_channle_lo["scale"] = _to_complex(u_channle_lo["scale"]) def _to_complex(value: Union[List[float], complex]) -> complex: @@ -92,8 +92,9 @@ def _decode_pulse_library_item(pulse_library_item: Dict) -> None: Args: pulse_library_item: A ``PulseLibraryItem`` in dictionary format. """ - pulse_library_item['samples'] =\ - [_to_complex(sample) for sample in pulse_library_item['samples']] + pulse_library_item["samples"] = [ + _to_complex(sample) for sample in pulse_library_item["samples"] + ] def _decode_pulse_qobj_instr(pulse_qobj_instr: Dict) -> None: @@ -102,7 +103,7 @@ def _decode_pulse_qobj_instr(pulse_qobj_instr: Dict) -> None: Args: pulse_qobj_instr: A ``PulseQobjInstruction`` in dictionary format. """ - if 'val' in pulse_qobj_instr: - pulse_qobj_instr['val'] = _to_complex(pulse_qobj_instr['val']) - if 'parameters' in pulse_qobj_instr and 'amp' in pulse_qobj_instr['parameters']: - pulse_qobj_instr['parameters']['amp'] = _to_complex(pulse_qobj_instr['parameters']['amp']) + if "val" in pulse_qobj_instr: + pulse_qobj_instr["val"] = _to_complex(pulse_qobj_instr["val"]) + if "parameters" in pulse_qobj_instr and "amp" in pulse_qobj_instr["parameters"]: + pulse_qobj_instr["parameters"]["amp"] = _to_complex(pulse_qobj_instr["parameters"]["amp"]) diff --git a/qiskit/test/providers/backend.py b/qiskit/test/providers/backend.py index f8e7906e91d4..83832b8a29e7 100644 --- a/qiskit/test/providers/backend.py +++ b/qiskit/test/providers/backend.py @@ -32,6 +32,7 @@ class BackendTestCase(QiskitTestCase): ``_get_backend`` function. circuit (QuantumCircuit): circuit to be used for the tests. """ + backend_cls = None circuit = ReferenceCircuits.bell() @@ -42,7 +43,7 @@ def setUp(self): @classmethod def setUpClass(cls): if cls is BackendTestCase: - raise SkipTest('Skipping base class tests') + raise SkipTest("Skipping base class tests") super().setUpClass() def _get_backend(self): diff --git a/qiskit/test/providers/provider.py b/qiskit/test/providers/provider.py index 067ce2b3d962..8170ae35859b 100644 --- a/qiskit/test/providers/provider.py +++ b/qiskit/test/providers/provider.py @@ -30,8 +30,9 @@ class ProviderTestCase(QiskitTestCase): ``_get_provider`` function. backend_name (str): name of a backend provided by the provider. """ + provider_cls = None - backend_name = '' + backend_name = "" def setUp(self): super().setUp() @@ -40,7 +41,7 @@ def setUp(self): @classmethod def setUpClass(cls): if cls is ProviderTestCase: - raise SkipTest('Skipping base class tests') + raise SkipTest("Skipping base class tests") super().setUpClass() def _get_provider(self): diff --git a/qiskit/test/reference_circuits.py b/qiskit/test/reference_circuits.py index 1a36431ca488..8b94dfa89a28 100644 --- a/qiskit/test/reference_circuits.py +++ b/qiskit/test/reference_circuits.py @@ -21,9 +21,9 @@ class ReferenceCircuits: @staticmethod def bell(): """Return a Bell circuit.""" - qr = QuantumRegister(2, name='qr') - cr = ClassicalRegister(2, name='qc') - qc = QuantumCircuit(qr, cr, name='bell') + qr = QuantumRegister(2, name="qr") + cr = ClassicalRegister(2, name="qc") + qc = QuantumCircuit(qr, cr, name="bell") qc.h(qr[0]) qc.cx(qr[0], qr[1]) qc.measure(qr, cr) @@ -33,8 +33,8 @@ def bell(): @staticmethod def bell_no_measure(): """Return a Bell circuit.""" - qr = QuantumRegister(2, name='qr') - qc = QuantumCircuit(qr, name='bell_no_measure') + qr = QuantumRegister(2, name="qr") + qc = QuantumCircuit(qr, name="bell_no_measure") qc.h(qr[0]) qc.cx(qr[0], qr[1]) diff --git a/qiskit/test/runtest.py b/qiskit/test/runtest.py index bcfcdc62b973..b5057f1360fd 100644 --- a/qiskit/test/runtest.py +++ b/qiskit/test/runtest.py @@ -20,14 +20,15 @@ """Individual test case execution.""" __all__ = [ - 'MultipleExceptions', - 'RunTest', - ] + "MultipleExceptions", + "RunTest", +] import sys try: from testtools.testresult import ExtendedToOriginalDecorator + HAS_TESTTOOLS = True except ImportError: HAS_TESTTOOLS = False @@ -82,7 +83,8 @@ def __init__(self, case, handlers=None, last_resort=None): if not HAS_TESTTOOLS: raise ImportError( "Test runner requirements testtools and fixtures are missing. " - "Install them with 'pip install testtools fixtures'") + "Install them with 'pip install testtools fixtures'" + ) self.case = case self.handlers = handlers or [] self.exception_caught = object() @@ -127,8 +129,7 @@ def _run_prepared_result(self, result): self.result = result try: self._exceptions = [] - self.case.__testtools_tb_locals__ = getattr( - result, 'tb_locals', False) + self.case.__testtools_tb_locals__ = getattr(result, "tb_locals", False) self._run_core() if self._exceptions: # One or more caught exceptions, now trigger the test's @@ -148,17 +149,17 @@ def _run_prepared_result(self, result): def _run_core(self): """Run the user supplied test code.""" test_method = self.case._get_test_method() - skip_case = getattr(self.case, '__unittest_skip__', False) - if skip_case or getattr(test_method, '__unittest_skip__', False): + skip_case = getattr(self.case, "__unittest_skip__", False) + if skip_case or getattr(test_method, "__unittest_skip__", False): self.result.addSkip( self.case, - reason=getattr(self.case if skip_case else test_method, - '__unittest_skip_why__', None) + reason=getattr( + self.case if skip_case else test_method, "__unittest_skip_why__", None + ), ) return - if self.exception_caught == self._run_user(self.case._run_setup, - self.result): + if self.exception_caught == self._run_user(self.case._run_setup, self.result): # Don't run the test method if we failed getting here. self._run_cleanups(self.result) return @@ -166,26 +167,22 @@ def _run_core(self): # exception we'll have failed. failed = False try: - if self.exception_caught == self._run_user( - self.case._run_test_method, self.result): + if self.exception_caught == self._run_user(self.case._run_test_method, self.result): failed = True finally: try: - if self.exception_caught == self._run_user( - self.case._run_teardown, self.result): + if self.exception_caught == self._run_user(self.case._run_teardown, self.result): failed = True finally: try: - if self.exception_caught == self._run_user( - self._run_cleanups, self.result): + if self.exception_caught == self._run_user(self._run_cleanups, self.result): failed = True finally: - if getattr(self.case, 'force_failure', None): + if getattr(self.case, "force_failure", None): self._run_user(_raise_force_fail_error) failed = True if not failed: - self.result.addSuccess(self.case, - details=self.case.getDetails()) + self.result.addSuccess(self.case, details=self.case.getDetails()) def _run_cleanups(self, result): """Run the cleanups that have been added with addCleanup. @@ -196,8 +193,7 @@ def _run_cleanups(self, result): failing = False while self.case._cleanups: function, arguments, keywordArguments = self.case._cleanups.pop() - got_exception = self._run_user( - function, *arguments, **keywordArguments) + got_exception = self._run_user(function, *arguments, **keywordArguments) if got_exception == self.exception_caught: failing = True if failing: @@ -214,7 +210,7 @@ def _run_user(self, fn, *args, **kwargs): except Exception: return self._got_user_exception(sys.exc_info()) - def _got_user_exception(self, exc_info, tb_label='traceback'): + def _got_user_exception(self, exc_info, tb_label="traceback"): """Called when user code raises an exception. If 'exc_info' is a `MultipleExceptions`, then we recurse into it unpacking the errors that it's made up from. diff --git a/qiskit/test/testing_options.py b/qiskit/test/testing_options.py index 0b093724e47d..0ecd8beea15d 100644 --- a/qiskit/test/testing_options.py +++ b/qiskit/test/testing_options.py @@ -18,7 +18,7 @@ logger = logging.getLogger(__name__) -def get_test_options(option_var='QISKIT_TESTS'): +def get_test_options(option_var="QISKIT_TESTS"): """Read option_var from env and returns a dict in which the test options are set. Args: @@ -27,12 +27,7 @@ def get_test_options(option_var='QISKIT_TESTS'): Returns: dict: A dictionary with the format {