From d964795b0a24b1d3287ba2ba2dda45d1dfed4a5d Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Tue, 21 May 2019 18:04:08 +0200 Subject: [PATCH 01/29] Update CONTRIBUTING guide because of the new release --- CHANGELOG.rst | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index da049c1442..06b66e5ef1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,7 +22,6 @@ Added Changed ------- -- Set simulator seed from "seed_simulator" in qobj (#210) Removed ------- @@ -31,6 +30,24 @@ Fixed ----- +`0.2.1`_ - 2019-05-20 +===================== + +Added +----- + +Changed +------- +- Set simulator seed from "seed_simulator" in qobj (#210) + +Removed +------- + +Fixed +----- +- Fix memory error handling for huge circuits (#216) +- Fix equality expressions in Python code (#208) + `0.2.0`_ - 2019-05-02 ===================== @@ -112,7 +129,8 @@ Added - Standalone Simulators support -.. _UNRELEASED: https://github.com/Qiskit/qiskit-aer/compare/0.2.0...HEAD +.. _UNRELEASED: https://github.com/Qiskit/qiskit-aer/compare/0.2.1...HEAD +.. _0.2.1: https://github.com/Qiskit/qiskit-aer/compare/0.2.0...0.2.1 .. _0.2.0: https://github.com/Qiskit/qiskit-aer/compare/0.1.1...0.2.0 .. _0.1.1: https://github.com/Qiskit/qiskit-aer/compare/0.1.0...0.1.1 .. _0.1.0: https://github.com/Qiskit/qiskit-aer/compare/0.0.0...0.1.0 From e65bda3832e8d219381fc7333fd1af6f4b3d4755 Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Thu, 6 Jun 2019 16:13:55 +0200 Subject: [PATCH 02/29] Enable python linter (#219) * * Enable Python linter in Travis * Add Python linters as development dependecies * Update .pylintrc to match the one in Terra * Removing linter errors * Removing pycodestyle warnings and errors * Removed all linter and pep8 warnings from the code (not tests and benchmarks yet) --- .pylintrc | 44 ++-- .travis.yml | 12 +- qiskit/providers/aer/__init__.py | 5 +- qiskit/providers/aer/aerjob.py | 1 - qiskit/providers/aer/backends/__init__.py | 1 - qiskit/providers/aer/backends/aerbackend.py | 1 + .../providers/aer/backends/qasm_simulator.py | 27 +-- .../aer/backends/statevector_simulator.py | 14 +- .../aer/backends/unitary_simulator.py | 17 +- qiskit/providers/aer/noise/__init__.py | 2 +- qiskit/providers/aer/noise/device/models.py | 6 +- .../providers/aer/noise/device/parameters.py | 22 +- qiskit/providers/aer/noise/errors/__init__.py | 1 - .../providers/aer/noise/errors/errorutils.py | 118 +++++----- .../aer/noise/errors/quantum_error.py | 25 ++- .../aer/noise/errors/readout_error.py | 9 +- .../aer/noise/errors/standard_errors.py | 6 +- qiskit/providers/aer/noise/noise_model.py | 8 +- .../aer/noise/utils/noise_transformation.py | 212 +++++++++++++----- qiskit/providers/aer/utils/qobj_utils.py | 16 +- qiskit/providers/aer/version.py | 5 +- requirements-dev.txt | 4 +- test/benchmark/__init__.py | 3 + test/benchmark/quantum_volume_benchmarks.py | 1 + test/benchmark/tools.py | 4 + .../backends/qasm_simulator/qasm_basics.py | 1 - .../backends/qasm_simulator/qasm_fusion.py | 9 +- .../backends/test_statevector_simulator.py | 120 ++++++---- test/terra/noise/test_noise_model.py | 2 + 29 files changed, 430 insertions(+), 266 deletions(-) diff --git a/.pylintrc b/.pylintrc index 6af30e07e4..f15d30bf61 100644 --- a/.pylintrc +++ b/.pylintrc @@ -20,7 +20,8 @@ persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. -load-plugins=pylint.extensions.docparams # enable checking of docstring args +load-plugins=pylint.extensions.docparams, # enable checking of docstring args + pylint.extensions.docstyle # basic docstring stle checks # Use multiple processes to speed up Pylint. jobs=1 @@ -32,16 +33,7 @@ unsafe-load-any-extension=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code -extension-pkg-whitelist=numpy - -# Allow optimization of some AST trees. This will activate a peephole AST -# optimizer, which will apply various small optimizations. For instance, it can -# be used to obtain the result of joining multiple strings with the addition -# operator. Joining a lot of strings can lead to a maximum recursion error in -# Pylint and this flag can prevent that. It has one side effect, the resulting -# AST will be different than the one from reality. This option is deprecated -# and it will be removed in Pylint 2.0. -optimize-ast=no +extension-pkg-whitelist= [MESSAGES CONTROL] @@ -65,16 +57,20 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,import-star-module-level,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,long-suffix,old-ne-operator,old-octal-literal,suppressed-message,useless-suppression, - no-self-use, # disabled as it is too verbose +disable=no-self-use, # disabled as it is too verbose fixme, # disabled as TODOs would show up as warnings protected-access, # disabled as we don't follow the public vs private # convention strictly duplicate-code, # disabled as it is too verbose + redundant-returns-doc, # for @abstractmethod, it cannot interpret "pass" # disable the "too-many/few-..." refactoring hints too-many-lines, too-many-branches, too-many-locals, too-many-nested-blocks, too-many-statements, too-many-instance-attributes, too-many-arguments, - too-many-public-methods, too-few-public-methods, too-many-ancestors + too-many-public-methods, too-few-public-methods, too-many-ancestors, + 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 + @@ -115,9 +111,12 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme # v,w = typical vectors # x,y,z = typical axes # _ = placeholder name -# qr,cr,qc = quantum and classical registers, and quantum circuit +# q,r,qr,cr,qc = quantum and classical registers, and quantum circuit # pi = the PI constant -good-names=i,j,k,n,m,ex,v,w,x,y,z,Run,_,logger,qr,cr,qc,pi +# op = operation iterator +# b = basis iterator +good-names=i,j,k,n,m,ex,v,w,x,y,z,Run,_,logger,q,c,r,qr,cr,qc,nd,pi,op,b,ar,br, + __unittest # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,toto,tutu,tata @@ -158,7 +157,7 @@ function-rgx=[a-z_][a-z0-9_]{2,30}$ function-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct method names -method-rgx=(([a-z_][a-z0-9_]{2,49})|(assert[A-Z][a-zA-Z0-9]{2,43}))$ +method-rgx=(([a-z_][a-z0-9_]{2,49})|(assert[A-Z][a-zA-Z0-9]{2,43})|(test_[_a-zA-Z0-9]{2,}))$ # Naming hint for method names method-name-hint=[a-z_][a-z0-9_]{2,30}$ or camelCase `assert*` in tests. @@ -295,20 +294,17 @@ ignore-mixin-members=yes # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. -ignored-modules=matplotlib.cm +ignored-modules=matplotlib.cm,numpy.random # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local +ignored-classes=optparse.Values,thread._local,_thread._local,QuantumCircuit # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=self.circuit.*,qcs.h,qc1.h,qc2.cx,qc.h,self.u1,self.cx,self.ccx,trial_circuit -# self.circuit.*: false positives when self.circuit == QuantumCircuit, as it -# provides a "__getitem__" dynamic method and gate definition is dynamic. -# For qiskit.extensions.standard, self.foo is also needed. +generated-members= # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that @@ -335,7 +331,7 @@ callbacks=cb_,_cb # List of qualified module names which can have objects that can redefine # builtins. -redefining-builtins-modules=six.moves,future.builtins +redefining-builtins-modules=six.moves,future.builtins,tools.compiler [CLASSES] diff --git a/.travis.yml b/.travis.yml index 40934538ca..c2fc836653 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,7 @@ stage_osx: &stage_osx # Define the order of the stages. stages: - # - lint + - lint - test # Define the job matrix explicitly, as matrix expansion causes issues when @@ -94,9 +94,13 @@ jobs: # "lint" stage ########################################################################### # C++ and Python linters - # TODO! - # - stage: lint - + # TODO: C++ clang-tidy! + - stage: lint + name: Python Style and Linter + <<: *stage_linux + script: + - pycodestyle --ignore=E402,W504 --max-line-length=100 qiskit/providers/aer + - pylint -j 2 -rn qiskit/providers/aer # "test" stage ########################################################################### diff --git a/qiskit/providers/aer/__init__.py b/qiskit/providers/aer/__init__.py index 178e77fa2a..8ffef2ac04 100644 --- a/qiskit/providers/aer/__init__.py +++ b/qiskit/providers/aer/__init__.py @@ -10,6 +10,8 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +""" Main Terra addon public interface """ + # https://github.com/Qiskit/qiskit-aer/issues/1 # Because of this issue, we need to make sure that Numpy's OpenMP library is initialized # before loading our simulators, so we force it using this ugly trick @@ -19,8 +21,7 @@ np.dot(np.zeros(100), np.zeros(100)) # ... ¯\_(ツ)_/¯ -"""Aer Backends.""" - +# pylint: disable=wrong-import-position from .aerprovider import AerProvider from .aerjob import AerJob from .aererror import AerError diff --git a/qiskit/providers/aer/aerjob.py b/qiskit/providers/aer/aerjob.py index 0ea997bde4..8258132a74 100644 --- a/qiskit/providers/aer/aerjob.py +++ b/qiskit/providers/aer/aerjob.py @@ -16,7 +16,6 @@ from concurrent import futures import logging -import sys import functools from qiskit.providers import BaseJob, JobStatus, JobError diff --git a/qiskit/providers/aer/backends/__init__.py b/qiskit/providers/aer/backends/__init__.py index 1c483eda5e..e7db9d13e0 100644 --- a/qiskit/providers/aer/backends/__init__.py +++ b/qiskit/providers/aer/backends/__init__.py @@ -15,4 +15,3 @@ from .qasm_simulator import QasmSimulator from .statevector_simulator import StatevectorSimulator from .unitary_simulator import UnitarySimulator - diff --git a/qiskit/providers/aer/backends/aerbackend.py b/qiskit/providers/aer/backends/aerbackend.py index 62bc20e513..f945067711 100644 --- a/qiskit/providers/aer/backends/aerbackend.py +++ b/qiskit/providers/aer/backends/aerbackend.py @@ -77,6 +77,7 @@ def __init__(self, controller, configuration, provider=None): super().__init__(configuration, provider=provider) self._controller = controller + # pylint: disable=arguments-differ def run(self, qobj, backend_options=None, noise_model=None, validate=True): """Run a qobj on the backend.""" # Submit job diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index 5c18425577..bbba34c3c9 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -19,6 +19,7 @@ from qiskit.util import local_hardware_info from qiskit.providers.models import BackendConfiguration from .aerbackend import AerBackend +# pylint: disable=import-error from .qasm_controller_wrapper import qasm_controller_execute from ..aererror import AerError from ..version import __version__ @@ -243,16 +244,16 @@ def _validate(self, qobj, backend_options, noise_model): self.name(), system_memory) if method != "automatic": raise AerError(err_string + '.') - else: - if n_qubits > 63: - raise AerError('{}, and has too many qubits to fall ' - 'back to the extended_stabilizer ' - 'method.'.format(err_string)) - if not ch_supported: - raise AerError('{}, and contains instructions ' - 'not supported by the extended_etabilizer ' - 'method.'.format(err_string)) - logger.info( - 'The QasmSimulator will automatically ' - 'switch to the Extended Stabilizer backend, based on ' - 'the memory requirements.') + + if n_qubits > 63: + raise AerError('{}, and has too many qubits to fall ' + 'back to the extended_stabilizer ' + 'method.'.format(err_string)) + if not ch_supported: + raise AerError('{}, and contains instructions ' + 'not supported by the extended_etabilizer ' + 'method.'.format(err_string)) + logger.info( + 'The QasmSimulator will automatically ' + 'switch to the Extended Stabilizer backend, based on ' + 'the memory requirements.') diff --git a/qiskit/providers/aer/backends/statevector_simulator.py b/qiskit/providers/aer/backends/statevector_simulator.py index aa877af726..d1e55146bc 100644 --- a/qiskit/providers/aer/backends/statevector_simulator.py +++ b/qiskit/providers/aer/backends/statevector_simulator.py @@ -22,6 +22,7 @@ from qiskit.util import local_hardware_info from qiskit.providers.models import BackendConfiguration from .aerbackend import AerBackend +# pylint: disable=import-error from .statevector_controller_wrapper import statevector_controller_execute from ..aererror import AerError from ..version import __version__ @@ -111,20 +112,19 @@ def _validate(self, qobj, backend_options, noise_model): """ name = self.name() if noise_model is not None: - logger.error("{} cannot be run with a noise.".format(name)) raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: - raise AerError('Number of qubits ({}) '.format(n_qubits) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'for "{}" '.format(name) + - 'with {} GB system memory.'.format(int(local_hardware_info()['memory']))) + raise AerError( + 'Number of qubits ({}) is greater than max ({}) for "{}" with {} GB system memory.' + .format(n_qubits, max_qubits, name, int(local_hardware_info()['memory']))) + if qobj.config.shots != 1: - logger.info('"%s" only supports 1 shot. Setting shots=1.', - name) + logger.info('"%s" only supports 1 shot. Setting shots=1.', name) qobj.config.shots = 1 + for experiment in qobj.experiments: exp_name = experiment.header.name if getattr(experiment.config, 'shots', 1) != 1: diff --git a/qiskit/providers/aer/backends/unitary_simulator.py b/qiskit/providers/aer/backends/unitary_simulator.py index bebad03d9c..02d831edda 100644 --- a/qiskit/providers/aer/backends/unitary_simulator.py +++ b/qiskit/providers/aer/backends/unitary_simulator.py @@ -24,6 +24,7 @@ from .aerbackend import AerBackend from ..aererror import AerError +# pylint: disable=import-error from .unitary_controller_wrapper import unitary_controller_execute from ..version import __version__ @@ -84,7 +85,7 @@ class UnitarySimulator(AerBackend): 'open_pulse': False, 'memory': False, 'max_shots': 1, - 'description': 'A Python simulator for computing the unitary' + + 'description': 'A Python simulator for computing the unitary' 'matrix for experiments in qobj files', 'coupling_map': None, 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', @@ -116,16 +117,14 @@ def _validate(self, qobj, backend_options, noise_model): """ name = self.name() if noise_model is not None: - logger.error("{} cannot be run with a noise.".format(name)) raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: - raise AerError('Number of qubits ({}) '.format(n_qubits) + - 'is greater than maximum ({}) '.format(max_qubits) + - 'for "{}" '.format(name) + - 'with {} GB system memory.'.format(int(local_hardware_info()['memory']))) + raise AerError( + 'Number of qubits ({}) is greater than max ({}) for "{}" with {} GB system memory.' + .format(n_qubits, max_qubits, name, int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) @@ -139,6 +138,6 @@ def _validate(self, qobj, backend_options, noise_model): experiment.config.shots = 1 for operation in experiment.instructions: if operation.name in ['measure', 'reset']: - raise AerError('Unsupported "%s" instruction "%s" ' + - 'in circuit "%s" ', name, - operation.name, exp_name) + raise AerError( + 'Unsupported {} instruction {} in circuit {}' + .format(name, operation.name, exp_name)) diff --git a/qiskit/providers/aer/noise/__init__.py b/qiskit/providers/aer/noise/__init__.py index 3af6e4f58f..f3d0b98b47 100644 --- a/qiskit/providers/aer/noise/__init__.py +++ b/qiskit/providers/aer/noise/__init__.py @@ -81,4 +81,4 @@ from .noise_model import NoiseModel from . import errors from . import device -from . import utils \ No newline at end of file +from . import utils diff --git a/qiskit/providers/aer/noise/device/models.py b/qiskit/providers/aer/noise/device/models.py index f86fa8ed47..bdbfba124b 100644 --- a/qiskit/providers/aer/noise/device/models.py +++ b/qiskit/providers/aer/noise/device/models.py @@ -405,8 +405,10 @@ def _depol_error_value_two_qubit(error_param, if gate_time is None: gate_time = 0 - if gate_time == 0 or (qubit0_t1 == inf and qubit0_t2 == inf - and qubit1_t1 == inf and qubit1_t2 == inf): + if gate_time == 0 or (qubit0_t1 == inf and + qubit0_t2 == inf and + qubit1_t1 == inf and + qubit1_t2 == inf): if error_param is not None and error_param > 0: return 4 * error_param / 3 else: diff --git a/qiskit/providers/aer/noise/device/parameters.py b/qiskit/providers/aer/noise/device/parameters.py index bff0b8c899..160ee423d7 100644 --- a/qiskit/providers/aer/noise/device/parameters.py +++ b/qiskit/providers/aer/noise/device/parameters.py @@ -30,8 +30,8 @@ def gate_param_values(properties): Returns: list: A list of tuples (name, qubits, time, error). If gate - error or gate_time information is not available None will be - returned for value. + error or gate_time information is not available None + will be returned for value. """ values = [] for gate in properties.gates: @@ -63,8 +63,8 @@ def gate_error_values(properties): Returns: list: A list of tuples (name, qubits, value). If gate - error information is not available None will be returned for - value. + error information is not available None will be returned for + value. """ values = [] for gate in properties.gates: @@ -88,8 +88,8 @@ def gate_time_values(properties): Returns: list: A list of tuples (name, qubits, value). If gate - time information is not available None will be returned for - value. + time information is not available None will be returned for + value. """ values = [] for gate in properties.gates: @@ -114,10 +114,11 @@ def readout_error_values(properties): Returns: list: A list of readout error values for qubits. If readout - error information is not available None will be returned for value. + error information is not available None will be returned + for value. """ values = [] - for qubit, qubit_props in enumerate(properties.qubits): + for qubit_props in properties.qubits: value = None # default value params = _check_for_item(qubit_props, 'readout_error') if hasattr(params, 'value'): @@ -142,7 +143,8 @@ def thermal_relaxation_values(properties): Numpy.inf will be used. """ values = [] - for qubit, qubit_props in enumerate(properties.qubits): + for qubit_props in properties.qubits: + # pylint: disable=invalid-name # Default values t1, t2, freq = inf, inf, inf @@ -180,7 +182,7 @@ def thermal_relaxation_values(properties): def _check_for_item(lst, name): """Search list for item with given name.""" filtered = [item for item in lst if item.name == name] - if len(filtered) == 0: + if not filtered: return None else: return filtered[0] diff --git a/qiskit/providers/aer/noise/errors/__init__.py b/qiskit/providers/aer/noise/errors/__init__.py index 52e0759dc9..5ac342cde7 100644 --- a/qiskit/providers/aer/noise/errors/__init__.py +++ b/qiskit/providers/aer/noise/errors/__init__.py @@ -24,4 +24,3 @@ from .standard_errors import phase_amplitude_damping_error from .standard_errors import amplitude_damping_error from .standard_errors import phase_damping_error - diff --git a/qiskit/providers/aer/noise/errors/errorutils.py b/qiskit/providers/aer/noise/errors/errorutils.py index 8530f08c30..6da19ca49f 100644 --- a/qiskit/providers/aer/noise/errors/errorutils.py +++ b/qiskit/providers/aer/noise/errors/errorutils.py @@ -41,6 +41,7 @@ def standard_gates_instructions(instructions): return output_instructions +# pylint: disable=too-many-return-statements def standard_gate_instruction(instruction, ignore_phase=True): """Convert a unitary matrix instruction into a standard gate instruction. @@ -241,7 +242,8 @@ def single_qubit_clifford_matrix(j): return mat -def single_qubit_clifford_instructions(j, qubit=0): +# pylint: disable=invalid-name +def single_qubit_clifford_instructions(index, qubit=0): """Return a list of qobj instructions for a single qubit cliffords. The instructions are returned in a basis set consisting of @@ -250,7 +252,7 @@ def single_qubit_clifford_instructions(j, qubit=0): decomposition. Args: - j (int): Clifford index 0, ..., 23. + index (int): Clifford index 0, ..., 23. qubit (int): the qubit to apply the Clifford to. Returns: @@ -260,73 +262,69 @@ def single_qubit_clifford_instructions(j, qubit=0): NoiseError: If index is out of range [0, 23] or qubit invalid. """ - if not isinstance(j, int) or j < 0 or j > 23: + if not isinstance(index, int) or index < 0 or index > 23: raise NoiseError( - "Index {} must be in the range [0, ..., 23]".format(j)) + "Index {} must be in the range [0, ..., 23]".format(index)) if not isinstance(qubit, int) or qubit < 0: raise NoiseError("qubit position must be positive integer.") instructions = [] - for gate in single_qubit_clifford_gates(j): + for gate in single_qubit_clifford_gates(index): instructions.append({"name": gate, "qubits": [qubit]}) return instructions def standard_gate_unitary(name): """Return the unitary matrix for a standard gate.""" - if name in ["id", "I"]: - return np.eye(2, dtype=complex) - if name in ["x", "X"]: - return np.array([[0, 1], [1, 0]], dtype=complex) - if name in ["y", "Y"]: - return np.array([[0, -1j], [1j, 0]], dtype=complex) - if name in ["z", "Z"]: - return np.array([[1, 0], [0, -1]], dtype=complex) - if name in ["h", "H"]: - return np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2) - if name in ["s", "S"]: - return np.array([[1, 0], [0, 1j]], dtype=complex) - if name in ["sdg", "Sdg"]: - return np.array([[1, 0], [0, -1j]], dtype=complex) - if name in ["t", "T"]: - return np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex) - if name in ["tdg", "Tdg"]: - return np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex) - if name in ["cx", "CX", "cx_01"]: - return np.array( - [[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], - dtype=complex) - if name == "cx_10": - return np.array( - [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]], - dtype=complex) - if name in ["cz", "CZ"]: - return np.array( - [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], - dtype=complex) - if name in ["swap", "SWAP"]: - return np.array( - [[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], - dtype=complex) - if name in ["ccx", "CCX", "ccx_012", "ccx_102"]: - return np.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, 1], - [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, 1, 0, 0, 0, 0]], - dtype=complex) - if name in ["ccx_021", "ccx_201"]: - return np.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, 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]], - dtype=complex) - if name in ["ccx_120", "ccx_210"]: - return np.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, 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, 1], [0, 0, 0, 0, 0, 0, 1, 0]], - dtype=complex) - return None + + unitary_matrices = { + ("id", "I"): + np.eye(2, dtype=complex), + ("x", "X"): + np.array([[0, 1], [1, 0]], dtype=complex), + ("y", "Y"): + np.array([[0, -1j], [1j, 0]], dtype=complex), + ("z", "Z"): + np.array([[1, 0], [0, -1]], dtype=complex), + ("h", "H"): + np.array([[1, 1], [1, -1]], dtype=complex) / np.sqrt(2), + ("s", "S"): + np.array([[1, 0], [0, 1j]], dtype=complex), + ("sdg", "Sdg"): + np.array([[1, 0], [0, -1j]], dtype=complex), + ("t", "T"): + np.array([[1, 0], [0, np.exp(1j * np.pi / 4)]], dtype=complex), + ("tdg", "Tdg"): + np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex), + ("cx", "CX", "cx_01"): + np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + ("cx_10"): + np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), + ("cz", "CZ"): + np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex), + ("swap", "SWAP"): + np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex), + ("ccx", "CCX", "ccx_012", "ccx_102"): + np.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, 1], + [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, 1, 0, 0, 0, 0]], + dtype=complex), + ("ccx_021", "ccx_201"): + np.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, 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]], + dtype=complex), + ("ccx_120", "ccx_210"): + np.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, 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, 1], [0, 0, 0, 0, 0, 0, 1, 0]], + dtype=complex) + } + + return next((value for key, value in unitary_matrices.items() if name in key), None) def reset_superop(num_qubits): @@ -454,8 +452,10 @@ def make_unitary_instruction(mat, qubits, standard_gates=True): """ if not is_unitary_matrix(mat): raise NoiseError("Input matrix is not unitary.") - elif isinstance(qubits, int): + + if isinstance(qubits, int): qubits = [qubits] + instruction = {"name": "unitary", "qubits": qubits, "params": [mat]} if standard_gates: return standard_gate_instruction(instruction) diff --git a/qiskit/providers/aer/noise/errors/quantum_error.py b/qiskit/providers/aer/noise/errors/quantum_error.py index 20d44db357..1008c2db94 100644 --- a/qiskit/providers/aer/noise/errors/quantum_error.py +++ b/qiskit/providers/aer/noise/errors/quantum_error.py @@ -41,6 +41,7 @@ class QuantumError: module. """ + # pylint: disable=invalid-name ATOL = ATOL_DEFAULT RTOL = RTOL_DEFAULT MAX_TOL = 1e-4 @@ -343,10 +344,10 @@ def compose(self, other, front=False): last_instr = combined_circuit[-1] last_name = last_instr['name'] name = instr['name'] - can_combine = (last_name in ['id', 'kraus', 'unitary'] - or name in ['id', 'kraus', 'unitary']) - if (can_combine and self._check_instr(last_name) - and self._check_instr(name)): + can_combine = (last_name in ['id', 'kraus', 'unitary'] or + name in ['id', 'kraus', 'unitary']) + if (can_combine and self._check_instr(last_name) and + self._check_instr(name)): combined_circuit[-1] = self._compose_instr( last_instr, instr, self.number_of_qubits) else: @@ -474,10 +475,10 @@ def _tensor_product(self, other, reverse=False): last_instr = combined_circuit[-1] last_name = last_instr['name'] name = instr['name'] - can_combine = (last_name in ['id', 'kraus', 'unitary'] - or name in ['id', 'kraus', 'unitary']) - if (can_combine and self._check_instr(last_name) - and self._check_instr(name)): + can_combine = (last_name in ['id', 'kraus', 'unitary'] or + name in ['id', 'kraus', 'unitary']) + if (can_combine and self._check_instr(last_name) and + self._check_instr(name)): combined_circuit[-1] = self._tensor_instr( last_instr, instr) else: @@ -517,12 +518,12 @@ def _combine_kraus(noise_ops): # remove from lists kraus_instr.pop(i) prob_i = kraus_probs.pop(i) - p = prob + prob_i - kraus = (prob / p) * Kraus( - instr['params']) + (prob_i / p) * Kraus(item['params']) + sum_prob = prob + prob_i + kraus = (prob / sum_prob) * Kraus( + instr['params']) + (prob_i / sum_prob) * Kraus(item['params']) # update instruction instr['param'] = kraus.data - prob = p + prob = sum_prob # append combined instruction to circuits new_circuits.append([instr]) new_probs.append(prob) diff --git a/qiskit/providers/aer/noise/errors/readout_error.py b/qiskit/providers/aer/noise/errors/readout_error.py index e5b05c21bb..30277ec3d4 100644 --- a/qiskit/providers/aer/noise/errors/readout_error.py +++ b/qiskit/providers/aer/noise/errors/readout_error.py @@ -18,7 +18,6 @@ import numpy as np from numpy.linalg import norm -from qiskit.quantum_info.operators.predicates import is_identity_matrix from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT from ..noiseerror import NoiseError @@ -29,7 +28,7 @@ class ReadoutError: """ Readout error class for Qiskit Aer noise model. """ - + # pylint: disable=invalid-name ATOL = ATOL_DEFAULT RTOL = RTOL_DEFAULT MAX_TOL = 1e-4 @@ -226,7 +225,9 @@ def expand(self, other): @staticmethod def _check_probabilities(probabilities, threshold): """Check probabilities are valid.""" - if len(probabilities) == 0: + # probabilities parameter can be a list or a numpy.ndarray + if (isinstance(probabilities, list) and not probabilities) or \ + (isinstance(probabilities, np.ndarray) and probabilities.size == 0): raise NoiseError("Input probabilities: empty.") num_outcomes = len(probabilities[0]) num_qubits = int(np.log2(num_outcomes)) @@ -243,7 +244,7 @@ def _check_probabilities(probabilities, threshold): if abs(sum(arr) - 1) > threshold: raise NoiseError("Invalid probabilities: sum({})= {} " "is not 1.".format(vec, sum(arr))) - if len(arr[arr < 0]) > 0: + if arr[arr < 0].size > 0: raise NoiseError( "Invalid probabilities: {} " "contains a negative probability.".format(vec)) diff --git a/qiskit/providers/aer/noise/errors/standard_errors.py b/qiskit/providers/aer/noise/errors/standard_errors.py index 69b1385383..ea91bd4bd6 100644 --- a/qiskit/providers/aer/noise/errors/standard_errors.py +++ b/qiskit/providers/aer/noise/errors/standard_errors.py @@ -191,7 +191,7 @@ def single_pauli(pauli): # Pauli strings go from qubit-0 on left to qubit-N on right # but pauli ops are tensor product of qubit-N on left to qubit-0 on right # We also drop identity operators to reduce dimension of matrix multiplication - mat = 1 + mat = np.identity(1) qubits = [] if isinstance(pauli, Pauli): pauli_str = pauli.to_label() @@ -203,7 +203,7 @@ def single_pauli(pauli): qubits.append(qubit) elif pstr != 'I': raise NoiseError("Invalid Pauli string.") - if mat is 1: + if mat.size == 1: prob_identity += prob else: circ = make_unitary_instruction( @@ -231,6 +231,7 @@ def single_pauli(pauli): return {'name': 'y'} if pauli == 'Z': return {'name': 'z'} + raise NoiseError("Invalid Pauli string.") prob_identity = 0.0 pauli_circuits = [] @@ -344,6 +345,7 @@ def reset_error(prob0, prob1=0): return QuantumError(noise_ops) +# pylint: disable=invalid-name def thermal_relaxation_error(t1, t2, time, excited_state_population=0): """ Single-qubit thermal relaxation quantum error channel. diff --git a/qiskit/providers/aer/noise/noise_model.py b/qiskit/providers/aer/noise/noise_model.py index 9be017c579..234ecf840e 100644 --- a/qiskit/providers/aer/noise/noise_model.py +++ b/qiskit/providers/aer/noise/noise_model.py @@ -173,10 +173,10 @@ def __eq__(self, other): # the same basis_gates # the same noise_qubits # the same noise_instructions - if (not isinstance(other, NoiseModel) - or self.basis_gates != other.basis_gates - or self.noise_qubits != other.noise_qubits - or self.noise_instructions != other.noise_instructions): + if (not isinstance(other, NoiseModel) or + self.basis_gates != other.basis_gates or + self.noise_qubits != other.noise_qubits or + self.noise_instructions != other.noise_instructions): return False # Check default readout errors is equal if not self._readout_errors_equal(other): diff --git a/qiskit/providers/aer/noise/utils/noise_transformation.py b/qiskit/providers/aer/noise/utils/noise_transformation.py index 2d9f6b2c17..9cd119a610 100644 --- a/qiskit/providers/aer/noise/utils/noise_transformation.py +++ b/qiskit/providers/aer/noise/utils/noise_transformation.py @@ -59,7 +59,8 @@ def approximate_quantum_error(error, *, Raises: NoiseError: if number of qubits is not supported or approximation - failsed. + failsed. + RuntimeError: If there's no information about the noise type Additional Information ---------------------- @@ -140,8 +141,8 @@ def approximate_noise_model(model, *, For further information see `NoiseTransformer.named_operators`. """ - #We need to iterate over all the errors in the noise model. - #No nice interface for this now, easiest way is to mimic as_dict + # We need to iterate over all the errors in the noise model. + # No nice interface for this now, easiest way is to mimic as_dict error_list = [] # Add default quantum errors @@ -228,8 +229,7 @@ def __init__(self): 'q': [{'name': 'reset', 'qubits': [0]}, {'name': 'x', 'qubits': [0]}] # reset to |1> }, - 'clifford': dict([(j, single_qubit_clifford_instructions(j)) - for j in range(1, 24)]) + 'clifford': {j: single_qubit_clifford_instructions(j) for j in range(1, 24)} } self.fidelity_data = None self.use_honesty_constraint = True @@ -257,7 +257,7 @@ def operator_circuit(self, operator): Args: operator (operator): operator representation. Can be a noise circuit or a matrix or a list of matrices. - Output: + Returns: List: The operator, converted to noise circuit representation. """ if isinstance(operator, numpy.ndarray): @@ -277,32 +277,34 @@ def transform_by_operator_list(self, transform_channel_operators, noise_kraus_operators): """ Args: - noise_kraus_operators: a list of matrices (Kraus operators) for the input channel - transform_channel_operators: a list of matrices or tuples of matrices - representing Kraus operators that can construct the output channel - e.g. [X,Y,Z] represent the Pauli channel - and [(|0><0|, |0><1|), |1><0|, |1><1|)] represents the relaxation channel - - Output: - A list of amplitudes that define the output channel. - In the case the input is a list [A1, A2, ..., An] of transform matrices - and [E0, E1, ..., Em] of noise kraus operators, the output is - a list [p1, p2, ..., pn] of probabilities such that: - 1) p_i >= 0 - 2) p1 + ... + pn <= 1 - 3) [sqrt(p1)A1, sqrt(p2)A2, ..., sqrt(pn)An, sqrt(1-(p1 + ... + pn))I] is - a list of kraus operators that define the output channel - (which is "close" to the input chanel given by [E0, ..., Em]) - - This channel can be thought of as choosing the operator Ai in probability pi and applying - this operator to the quantum state. - - More generally, if the input is a list of tuples (not neccesarily of the same size): - [(A1, B1, ...), (A2, B2, ...), ... (An, Bn, ...)] then the output is - still a list [p1, p2, ..., pn] and now the output channel is defined by the operators - [sqrt(p1)A1, sqrt(p1)B1, ..., sqrt(pn)An, sqrt(pn)Bn, ..., sqrt(1-(p1 + ... + pn))I] + noise_kraus_operators (List): a list of matrices (Kraus operators) for the input channel + transform_channel_operators (List): a list of matrices or tuples of matrices + representing Kraus operators that can construct the output channel + e.g. [X,Y,Z] represent the Pauli channel + and [(|0><0|, |0><1|), |1><0|, |1><1|)] represents the relaxation channel + + Returns: + List: A list of amplitudes that define the output channel. + In the case the input is a list [A1, A2, ..., An] of transform matrices + and [E0, E1, ..., Em] of noise kraus operators, the output is + a list [p1, p2, ..., pn] of probabilities such that: + 1) p_i >= 0 + 2) p1 + ... + pn <= 1 + 3) [sqrt(p1)A1, sqrt(p2)A2, ..., sqrt(pn)An, sqrt(1-(p1 + ... + pn))I] is + a list of kraus operators that define the output channel + (which is "close" to the input chanel given by [E0, ..., Em]) + + This channel can be thought of as choosing the operator Ai in probability pi and + applying this operator to the quantum state. + + More generally, if the input is a list of tuples (not neccesarily of the same size): + [(A1, B1, ...), (A2, B2, ...), ... (An, Bn, ...)] then the output is + still a list [p1, p2, ..., pn] and now the output channel is defined by theo + perators: + [sqrt(p1)A1, sqrt(p1)B1, ..., sqrt(pn)An, sqrt(pn)Bn, ..., sqrt(1-(p1 + ... + pn))I] """ self.noise_kraus_operators = noise_kraus_operators + # pylint: disable=invalid-name self.transform_channel_operators = transform_channel_operators full_transform_channel_operators = self.prepare_channel_operator_list( self.transform_channel_operators) @@ -315,6 +317,14 @@ def transform_by_operator_list(self, transform_channel_operators, @staticmethod def prepare_channel_operator_list(ops_list): + """ + Prepares a list of channel operators + Args: + ops_list (List): The list of operators to prepare + + Returns: + List: The channel operator list + """ # convert to sympy matrices and verify that each singleton is # in a tuple; also add identity matrix result = [[sympy.eye(2)]] @@ -324,9 +334,18 @@ def prepare_channel_operator_list(ops_list): result.append([sympy.Matrix(op) for op in ops]) return result + # pylint: disable=invalid-name def prepare_honesty_constraint(self, transform_channel_operators_list): + """ + Prepares the honesty constraint + + Args: + transform_channel_operators_list (list): A list of tuples of matrices which represent + Kraus operators. + """ if not self.use_honesty_constraint: return + goal = self.fidelity(self.noise_kraus_operators) coefficients = [ self.fidelity(ops) for ops in transform_channel_operators_list @@ -341,19 +360,23 @@ def prepare_honesty_constraint(self, transform_channel_operators_list): @staticmethod def fidelity(channel): + """ Calculates channel fidelity """ return sum([numpy.abs(numpy.trace(E))**2 for E in channel]) + # pylint: disable=invalid-name def generate_channel_matrices(self, transform_channel_operators_list): """ - Generates a list of 4x4 symbolic matrices describing the channel defined from the given operators + Generates a list of 4x4 symbolic matrices describing the channel defined from the given + operators Args: - transform_channel_operators_list: a list of tuples of matrices which represent Kraus operators + transform_channel_operators_list (list): A list of tuples of matrices which represent + Kraus operators. The identity matrix is assumed to be the first element in the list [(I, ), (A1, B1, ...), (A2, B2, ...), ..., (An, Bn, ...)] - e.g. for a Pauli channel, the matrices are + e.g. for a Pauli channel, the matrices are: [(I,), (X,), (Y,), (Z,)] - for relaxation they are + for relaxation they are: [(I, ), (|0><0|, |0><1|), |1><0|, |1><1|)] We consider this input to symbolically represent a channel in the following manner: @@ -364,10 +387,12 @@ def generate_channel_matrices(self, transform_channel_operators_list): This is the channel C symbolically represented by the operators - Output: - A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: - The matrix x1*D1 + ... + xn*Dn + E represents the operation of the channel C on the density operator - we find it easier to work with this representation of C when performing the combinatorial optimization + Returns: + list: A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: + The matrix x1*D1 + ... + xn*Dn + E represents the operation of the channel C + on the density operator. + we find it easier to work with this representation of C when performing the + combinatorial optimization. """ symbols_string = " ".join([ @@ -395,20 +420,46 @@ def generate_channel_matrices(self, transform_channel_operators_list): @staticmethod def compute_channel_operation(rho, operators): - # Given a quantum state's density function rho, the effect of the - # channel on this state is - # rho -> \sum_{i=1}^n E_i * rho * E_i^\dagger + """ + Given a quantum state's density function rho, the effect of the + channel on this state is: + rho -> sum_{i=1}^n E_i * rho * E_i^dagger + + Args: + rho (number): Density function + operators (list): List of operators + + Returns: + number: The result of applying the list of operators + """ return sum([E * rho * E.H for E in operators], sympy.zeros(operators[0].rows)) @staticmethod def flatten_matrix(m): + """ + Args: + m (Matrix): The matrix to flatten + + Returns: + list: A row vector repesenting the flattened matrix + """ + return [element for element in m] def channel_matrix_representation(self, operators): - # We convert the operators to a matrix by applying the channel to - # the four basis elements of the 2x2 matrix space representing - # density operators; this is standard linear algebra + """ + We convert the operators to a matrix by applying the channel to + the four basis elements of the 2x2 matrix space representing + density operators; this is standard linear algebra + + Args: + operators (list): The list of operators to transform into a Matrix + + Returns: + sympy.Matrix: The matrx representation of the operators + """ + standard_base = [ sympy.Matrix([[1, 0], [0, 0]]), sympy.Matrix([[0, 1], [0, 0]]), @@ -425,11 +476,11 @@ def generate_channel_quadratic_programming_matrices( self, channel, symbols): """ Args: - channel: a 4x4 symbolic matrix - symbols: the symbols x1, ..., xn which may occur in the matrix + channel (Matrix): a 4x4 symbolic matrix + symbols (list): the symbols x1, ..., xn which may occur in the matrix - Output: - A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: + Returns: + list: A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: channel == x1*D1 + ... + xn*Dn + E """ return ([ @@ -438,13 +489,14 @@ def generate_channel_quadratic_programming_matrices( @staticmethod def get_matrix_from_channel(channel, symbol): - """Extract the numeric parameter matrix. + """ + Extract the numeric parameter matrix. Args: channel (matrix): a 4x4 symbolic matrix. symbol (list): a symbol xi - Returns + Returns: matrix: a 4x4 numeric matrix. Additional Information @@ -462,14 +514,15 @@ def get_matrix_from_channel(channel, symbol): @staticmethod def get_const_matrix_from_channel(channel, symbols): - """Extract the numeric constant matrix. + """ + Extract the numeric constant matrix. Args: channel (matrix): a 4x4 symbolic matrix. symbols (list): The full list [x1, ..., xn] of symbols used in the matrix. - Returns + Returns: matrix: a 4x4 numeric matrix. Additional Information @@ -487,10 +540,19 @@ def get_const_matrix_from_channel(channel, symbols): def transform_by_given_channel(self, channel_matrices, const_channel_matrix): - # This method creates the quadratic programming instance for - # minimizing the Hilbert-Schmidt norm of the matrix (A-B) obtained - # as the difference of the input noise channel and the output - # channel we wish to determine. + """ + This method creates the quadratic programming instance for + minimizing the Hilbert-Schmidt norm of the matrix (A-B) obtained + as the difference of the input noise channel and the output + channel we wish to determine. + + Args: + channel_matrices (TODO): TODO + const_channel_matrix (TODO): TODD + + Returns: + quadratic_program: TODO + """ target_channel = SuperOp(Kraus(self.noise_kraus_operators)) target_channel_matrix = target_channel._data.T @@ -500,6 +562,14 @@ def transform_by_given_channel(self, channel_matrices, return self.solve_quadratic_program(P, q) def compute_P(self, As): + """ + TODO + Args: + As (TODO): TODO + + Returns: + Array: TODO + """ vs = [numpy.array(A).flatten() for A in As] n = len(vs) P = sympy.zeros(n, n) @@ -508,6 +578,15 @@ def compute_P(self, As): return P def compute_q(self, As, C): + """ + TODO + Args: + As (TODO): TODO + C (TODO): TODO + + Returns: + Array: TODO + """ vs = [numpy.array(A).flatten() for A in As] vC = numpy.array(C).flatten() n = len(vs) @@ -516,9 +595,24 @@ def compute_q(self, As, C): q[i] = 2 * numpy.real(numpy.dot(numpy.conj(vC), vs[i])) return q - # the following method is the only place in the code where we rely on the cvxopt library - # should we consider another library, only this method needs to change def solve_quadratic_program(self, P, q): + """ + TODO + Args: + P (TODO): TODO + q (TODO): TODO + + Returns: + Array: TODO + + Raises: + ImportError: If cvxopt external module is not installed + + Additional information + ====================== + This method is the only place in the code where we rely on the cvxopt library + should we consider another library, only this method needs to change + """ try: import cvxopt except ImportError: @@ -540,4 +634,4 @@ def solve_quadratic_program(self, P, q): G = cvxopt.matrix(numpy.array(G_data).astype(float)) h = cvxopt.matrix(numpy.array(h_data).astype(float)) cvxopt.solvers.options['show_progress'] = False - return cvxopt.solvers.qp(P, q, G, h)['x'] \ No newline at end of file + return cvxopt.solvers.qp(P, q, G, h)['x'] diff --git a/qiskit/providers/aer/utils/qobj_utils.py b/qiskit/providers/aer/utils/qobj_utils.py index 0da347742b..d8aba62393 100644 --- a/qiskit/providers/aer/utils/qobj_utils.py +++ b/qiskit/providers/aer/utils/qobj_utils.py @@ -29,6 +29,9 @@ def append_instr(qobj, exp_index, instruction): qobj (Qobj): a Qobj object. exp_index (int): The index of the experiment in the qobj. instruction (QasmQobjInstruction): instruction to insert. + + Returns: + qobj(Qobj): The Qobj object """ qobj.experiments[exp_index].instructions.append(instruction) return qobj @@ -40,8 +43,11 @@ def insert_instr(qobj, exp_index, item, pos): Args: qobj (Qobj): a Qobj object exp_index (int): The index of the experiment in the qobj. - instruction(QasmQobjInstruction): instruction to insert. + item (QasmQobjInstruction): instruction to insert. pos (int): the position to insert the item. + + Returns: + qobj(Qobj): The Qobj object """ qobj.experiments[exp_index].instructions.insert(pos, item) return qobj @@ -116,7 +122,7 @@ def measure_instr(qubits, memory, registers=None): if len(qubits) != len(registers): raise ValueError("Number of qubits does not match number of registers") return QasmQobjInstruction(name='measure', qubits=qubits, memory=memory, - register=registers) + register=registers) def reset_instr(qubits): @@ -197,6 +203,12 @@ def insert_snapshots_after_barriers(qobj, snapshot): qobj (Qobj): a qobj to insert snapshots into snapshot (QasmQobjInstruction): a snapshot instruction. + Returns: + qobj(Qobj): The Qobj object + + Raises: + ValueError: if the name of the instruction is not an snapshot + Additional Information: """ if snapshot.name != "snapshot": diff --git a/qiskit/providers/aer/version.py b/qiskit/providers/aer/version.py index 046824628e..1d2b31494e 100644 --- a/qiskit/providers/aer/version.py +++ b/qiskit/providers/aer/version.py @@ -10,8 +10,11 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. +""" +Helper tools for getting Terra addon version information +""" + import os -from pathlib import Path ROOT_DIR = os.path.dirname(__file__) with open(os.path.join(ROOT_DIR, "VERSION.txt"), "r") as version_file: diff --git a/requirements-dev.txt b/requirements-dev.txt index 59b22db41a..bd643411e2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,4 +3,6 @@ cmake scikit-build cython asv -cvxopt \ No newline at end of file +cvxopt +pylint +pycodestyle \ No newline at end of file diff --git a/test/benchmark/__init__.py b/test/benchmark/__init__.py index e10866e310..3b9d5acb9e 100644 --- a/test/benchmark/__init__.py +++ b/test/benchmark/__init__.py @@ -13,10 +13,12 @@ from qiskit.qobj import Qobj from qiskit.providers.aer.noise import NoiseModel + def qobj_repr_hook(self): """ This is needed for ASV to beauty-printing reports """ return "Num. qubits: {0}".format(self.config.n_qubits) + Qobj.__repr__ = qobj_repr_hook @@ -24,4 +26,5 @@ def noise_model_repr_hook(self): """ This is needed for ASV to beauty-printing reports """ return self.__class__.__name__.replace("_", " ").capitalize() + NoiseModel.__repr__ = noise_model_repr_hook diff --git a/test/benchmark/quantum_volume_benchmarks.py b/test/benchmark/quantum_volume_benchmarks.py index a891023037..04a9f8021f 100644 --- a/test/benchmark/quantum_volume_benchmarks.py +++ b/test/benchmark/quantum_volume_benchmarks.py @@ -20,6 +20,7 @@ from .tools import quantum_volume_circuit, mixed_unitary_noise_model, \ reset_noise_model, kraus_noise_model, no_noise + class QuantumVolumeTimeSuite: """ Benchmarking times for Quantum Volume with various noise configurations diff --git a/test/benchmark/tools.py b/test/benchmark/tools.py index b076df1c97..9b8bccad7a 100644 --- a/test/benchmark/tools.py +++ b/test/benchmark/tools.py @@ -24,6 +24,7 @@ from qiskit.providers.aer.noise.errors import amplitude_damping_error from qiskit.providers.aer.noise.errors import thermal_relaxation_error + class NoiseWithDescription: """ This is just a wrapper for adding a descriptive text to the noise model so ASV can print this text in its reports @@ -31,11 +32,14 @@ class NoiseWithDescription: def __init__(self, noise_model, description): self._noise_model = noise_model self._description = description + def __repr__(self): return self._description + def __call__(self): return self._noise_model + def _add_measurements(circuit, qr): cr = ClassicalRegister(qr.size) meas = QuantumCircuit(qr, cr) diff --git a/test/terra/backends/qasm_simulator/qasm_basics.py b/test/terra/backends/qasm_simulator/qasm_basics.py index ed5d1f99ed..7a1a427b66 100644 --- a/test/terra/backends/qasm_simulator/qasm_basics.py +++ b/test/terra/backends/qasm_simulator/qasm_basics.py @@ -32,7 +32,6 @@ def test_simulation_succeed(self): result = mocked_backend.run(qobj).result() self.is_completed(result) - def test_simulation_failed(self): """Test the we properly manage simulation failures.""" mocked_backend = FakeFailureQasmSimulator(time_alive=0) diff --git a/test/terra/backends/qasm_simulator/qasm_fusion.py b/test/terra/backends/qasm_simulator/qasm_fusion.py index ac6db34a9e..2f7179a1cd 100644 --- a/test/terra/backends/qasm_simulator/qasm_fusion.py +++ b/test/terra/backends/qasm_simulator/qasm_fusion.py @@ -13,24 +13,20 @@ """ QasmSimulator Integration Tests """ - -from test.benchmark.tools import quantum_volume_circuit +from test.terra.reference import ref_2q_clifford from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.compiler import assemble, transpile from qiskit.providers.aer import QasmSimulator from qiskit.providers.aer.noise import NoiseModel from qiskit.providers.aer.noise.errors import ReadoutError, depolarizing_error -from test.terra.reference import ref_1q_clifford -from test.terra.reference import ref_2q_clifford - - class QasmFusionTests: """QasmSimulator fusion tests.""" SIMULATOR = QasmSimulator() def create_statevector_circuit(self): + """ Creates a simple circuit for running in the statevector """ qr = QuantumRegister(10) cr = ClassicalRegister(10) circuit = QuantumCircuit(qr, cr) @@ -48,6 +44,7 @@ def create_statevector_circuit(self): return circuit def noise_model(self): + """ Creates a new noise model for testing purposes """ readout_error = [0.01, 0.1] depolarizing = {'u3': (1, 0.001), 'cx': (2, 0.02)} noise = NoiseModel() diff --git a/test/terra/backends/test_statevector_simulator.py b/test/terra/backends/test_statevector_simulator.py index d84484a9dd..6588b12222 100644 --- a/test/terra/backends/test_statevector_simulator.py +++ b/test/terra/backends/test_statevector_simulator.py @@ -140,7 +140,8 @@ def test_h_gate_deterministic_waltz_basis_gates(self): """Test h-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.h_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.h_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -167,7 +168,8 @@ def test_h_gate_nondeterministic_waltz_basis_gates(self): """Test h-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.h_gate_circuits_nondeterministic(final_measure=False) targets = ref_1q_clifford.h_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -197,7 +199,8 @@ def test_x_gate_deterministic_waltz_basis_gates(self): """Test x-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.x_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.x_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -206,7 +209,8 @@ def test_x_gate_deterministic_minimal_basis_gates(self): """Test x-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.x_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.x_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -227,7 +231,8 @@ def test_z_gate_deterministic_waltz_basis_gates(self): """Test z-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.z_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.z_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -236,7 +241,8 @@ def test_z_gate_deterministic_minimal_basis_gates(self): """Test z-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.z_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.z_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -257,7 +263,8 @@ def test_y_gate_deterministic_waltz_basis_gates(self): """Test y-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.y_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.y_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -266,7 +273,8 @@ def test_y_gate_deterministic_minimal_basis_gates(self): """Test y-gate gate circuits compiling to u3, cx.""" circuits = ref_1q_clifford.y_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.y_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -287,7 +295,8 @@ def test_s_gate_deterministic_waltz_basis_gates(self): """Test s-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.s_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.s_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -296,7 +305,8 @@ def test_s_gate_deterministic_minimal_basis_gates(self): """Test s-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.s_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.s_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -314,7 +324,8 @@ def test_s_gate_nondeterministic_waltz_basis_gates(self): """Test s-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.s_gate_circuits_nondeterministic(final_measure=False) targets = ref_1q_clifford.s_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -323,7 +334,8 @@ def test_s_gate_nondeterministic_minimal_basis_gates(self): """Test s-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.s_gate_circuits_nondeterministic(final_measure=False) targets = ref_1q_clifford.s_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -344,7 +356,8 @@ def test_sdg_gate_deterministic_waltz_basis_gates(self): """Test sdg-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.sdg_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.sdg_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -353,7 +366,8 @@ def test_sdg_gate_deterministic_minimal_basis_gates(self): """Test sdg-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.sdg_gate_circuits_deterministic(final_measure=False) targets = ref_1q_clifford.sdg_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -371,7 +385,8 @@ def test_sdg_gate_nondeterministic_waltz_basis_gates(self): """Test sdg-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_1q_clifford.sdg_gate_circuits_nondeterministic(final_measure=False) targets = ref_1q_clifford.sdg_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -380,7 +395,8 @@ def test_sdg_gate_nondeterministic_minimal_basis_gates(self): """Test sdg-gate gate circuits compiling to u3,cx""" circuits = ref_1q_clifford.sdg_gate_circuits_nondeterministic(final_measure=False) targets = ref_1q_clifford.sdg_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -401,7 +417,8 @@ def test_cx_gate_deterministic_waltz_basis_gates(self): """Test cx-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.cx_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.cx_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -410,7 +427,8 @@ def test_cx_gate_deterministic_minimal_basis_gates(self): """Test cx-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.cx_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.cx_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -428,7 +446,8 @@ def test_cx_gate_nondeterministic_waltz_basis_gates(self): """Test cx-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.cx_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.cx_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -437,7 +456,8 @@ def test_cx_gate_nondeterministic_minimal_basis_gates(self): """Test cx-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.cx_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.cx_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -458,7 +478,8 @@ def test_cz_gate_deterministic_waltz_basis_gates(self): """Test cz-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.cz_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.cz_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -467,7 +488,8 @@ def test_cz_gate_deterministic_minimal_basis_gates(self): """Test cz-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.cz_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.cz_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -485,7 +507,8 @@ def test_cz_gate_nondeterministic_waltz_basis_gates(self): """Test cz-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.cz_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.cz_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -494,7 +517,8 @@ def test_cz_gate_nondeterministic_minimal_basis_gates(self): """Test cz-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.cz_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.cz_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -515,7 +539,8 @@ def test_swap_gate_deterministic_waltz_basis_gates(self): """Test swap-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.swap_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.swap_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -524,7 +549,8 @@ def test_swap_gate_deterministic_minimal_basis_gates(self): """Test swap-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.swap_gate_circuits_deterministic(final_measure=False) targets = ref_2q_clifford.swap_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -542,7 +568,8 @@ def test_swap_gate_nondeterministic_waltz_basis_gates(self): """Test swap-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_2q_clifford.swap_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.swap_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -551,7 +578,8 @@ def test_swap_gate_nondeterministic_minimal_basis_gates(self): """Test swap-gate gate circuits compiling to u3,cx""" circuits = ref_2q_clifford.swap_gate_circuits_nondeterministic(final_measure=False) targets = ref_2q_clifford.swap_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -572,7 +600,8 @@ def test_t_gate_deterministic_waltz_basis_gates(self): """Test t-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.t_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.t_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -581,7 +610,8 @@ def test_t_gate_deterministic_minimal_basis_gates(self): """Test t-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.t_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.t_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -599,7 +629,8 @@ def test_t_gate_nondeterministic_waltz_basis_gates(self): """Test t-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.t_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.t_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -608,7 +639,8 @@ def test_t_gate_nondeterministic_minimal_basis_gates(self): """Test t-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.t_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.t_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -629,7 +661,8 @@ def test_tdg_gate_deterministic_waltz_basis_gates(self): """Test tdg-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.tdg_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.tdg_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -638,7 +671,8 @@ def test_tdg_gate_deterministic_minimal_basis_gates(self): """Test tdg-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.tdg_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.tdg_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -656,7 +690,8 @@ def test_tdg_gate_nondeterministic_waltz_basis_gates(self): """Test tdg-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.tdg_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.tdg_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -665,7 +700,8 @@ def test_tdg_gate_nondeterministic_minimal_basis_gates(self): """Test tdg-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.tdg_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.tdg_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -686,7 +722,8 @@ def test_ccx_gate_deterministic_waltz_basis_gates(self): """Test ccx-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.ccx_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.ccx_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -695,7 +732,8 @@ def test_ccx_gate_deterministic_minimal_basis_gates(self): """Test ccx-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.ccx_gate_circuits_deterministic(final_measure=False) targets = ref_non_clifford.ccx_gate_statevector_deterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -713,7 +751,8 @@ def test_ccx_gate_nondeterministic_waltz_basis_gates(self): """Test ccx-gate gate circuits compiling to u1,u2,u3,cx""" circuits = ref_non_clifford.ccx_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.ccx_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u1', 'u2', 'u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u1', 'u2', 'u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) @@ -722,7 +761,8 @@ def test_ccx_gate_nondeterministic_minimal_basis_gates(self): """Test ccx-gate gate circuits compiling to u3,cx""" circuits = ref_non_clifford.ccx_gate_circuits_nondeterministic(final_measure=False) targets = ref_non_clifford.ccx_gate_statevector_nondeterministic() - job = execute(circuits, StatevectorSimulator(), shots=1, basis_gates=['u3', 'cx']) + job = execute(circuits, StatevectorSimulator(), shots=1, + basis_gates=['u3', 'cx']) result = job.result() self.is_completed(result) self.compare_statevector(result, circuits, targets) diff --git a/test/terra/noise/test_noise_model.py b/test/terra/noise/test_noise_model.py index 57ad2fd5c5..f56d719ddd 100644 --- a/test/terra/noise/test_noise_model.py +++ b/test/terra/noise/test_noise_model.py @@ -756,6 +756,8 @@ def test_noise_models_equal(self): error1 = pauli_error([['X', 1]], standard_gates=False) error2 = pauli_error([['X', 1]], standard_gates=True) + print("error1: {}\nerror2: {}".format(error1, error2)) + model1 = NoiseModel() model1.add_all_qubit_quantum_error(error1, ['u3'], False) model1.add_quantum_error(error1, ['u3'], [2], False) From cf235c35afd57eaacab080b715d9f2a32259857b Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Thu, 6 Jun 2019 22:29:35 +0200 Subject: [PATCH 03/29] Remove deprecated warning from Terra and change our as_dict() to to_dict() (#228) * * Removing deprecation warnings from Terra by using to_dict() * Deprecating our as_dict() methods in favor of to_dict() * * Add helpers.py new file * Add entry in the Changelog * * Fixing a bad tuple * Removing a print from a test --- CHANGELOG.rst | 1 + qiskit/providers/aer/backends/aerbackend.py | 6 ++--- .../providers/aer/backends/qasm_simulator.py | 2 +- .../providers/aer/noise/errors/errorutils.py | 2 +- .../aer/noise/errors/quantum_error.py | 11 ++++++++ .../aer/noise/errors/readout_error.py | 11 ++++++++ qiskit/providers/aer/noise/noise_model.py | 27 +++++++++++++++---- .../aer/noise/utils/noise_remapper.py | 2 +- .../aer/noise/utils/noise_transformation.py | 10 +++---- qiskit/providers/aer/utils/__init__.py | 1 + qiskit/providers/aer/utils/helpers.py | 22 +++++++++++++++ test/terra/noise/test_noise_model.py | 2 -- 12 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 qiskit/providers/aer/utils/helpers.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 06b66e5ef1..56a192669b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -38,6 +38,7 @@ Added Changed ------- +- Deprecate the use of ".as_dict()" in favor of ".to_dict()" (#228) - Set simulator seed from "seed_simulator" in qobj (#210) Removed diff --git a/qiskit/providers/aer/backends/aerbackend.py b/qiskit/providers/aer/backends/aerbackend.py index f945067711..b676988025 100644 --- a/qiskit/providers/aer/backends/aerbackend.py +++ b/qiskit/providers/aer/backends/aerbackend.py @@ -50,8 +50,8 @@ def default(self, obj): return obj.tolist() if isinstance(obj, complex): return [obj.real, obj.imag] - if hasattr(obj, "as_dict"): - return obj.as_dict() + if hasattr(obj, "to_dict"): + return obj.to_dict() return super().default(obj) @@ -117,7 +117,7 @@ def _format_qobj_str(self, qobj, backend_options, noise_model): original_config = qobj.config # Convert to dictionary and add new parameters # from noise model and backend options - config = original_config.as_dict() + config = original_config.to_dict() if backend_options is not None: for key, val in backend_options.items(): config[key] = val diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index bbba34c3c9..64deb1ed1f 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -197,7 +197,7 @@ def _validate(self, qobj, backend_options, noise_model): if clifford_noise: if method != "stabilizer" and noise_model: - for error in noise_model.as_dict()['errors']: + for error in noise_model.to_dict()['errors']: if error['type'] == 'qerror': for circ in error["instructions"]: for instr in circ: diff --git a/qiskit/providers/aer/noise/errors/errorutils.py b/qiskit/providers/aer/noise/errors/errorutils.py index 6da19ca49f..9245411e9b 100644 --- a/qiskit/providers/aer/noise/errors/errorutils.py +++ b/qiskit/providers/aer/noise/errors/errorutils.py @@ -298,7 +298,7 @@ def standard_gate_unitary(name): np.array([[1, 0], [0, np.exp(-1j * np.pi / 4)]], dtype=complex), ("cx", "CX", "cx_01"): np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), - ("cx_10"): + ("cx_10",): np.array([[1, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]], dtype=complex), ("cz", "CZ"): np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=complex), diff --git a/qiskit/providers/aer/noise/errors/quantum_error.py b/qiskit/providers/aer/noise/errors/quantum_error.py index 1008c2db94..eb6d6bcea2 100644 --- a/qiskit/providers/aer/noise/errors/quantum_error.py +++ b/qiskit/providers/aer/noise/errors/quantum_error.py @@ -27,6 +27,7 @@ from .errorutils import circuit2superop from .errorutils import standard_instruction_channel from .errorutils import standard_instruction_operator +from ...utils.helpers import deprecation logger = logging.getLogger(__name__) @@ -284,6 +285,16 @@ def error_term(self, position): position) + "of error outcomes {}".format(self.size)) def as_dict(self): + """ + DEPRECATED: Use to_dict() + Returns: + dict: The current error as a dictionary. + """ + deprecation("QuantumError::as_dict() method is deprecated and will be removed after 0.3." + "Use '.to_dict()' instead") + return self.to_dict() + + def to_dict(self): """Return the current error as a dictionary.""" error = { "type": "qerror", diff --git a/qiskit/providers/aer/noise/errors/readout_error.py b/qiskit/providers/aer/noise/errors/readout_error.py index 30277ec3d4..4ca37372b0 100644 --- a/qiskit/providers/aer/noise/errors/readout_error.py +++ b/qiskit/providers/aer/noise/errors/readout_error.py @@ -22,6 +22,7 @@ from ..noiseerror import NoiseError from .errorutils import qubits_from_mat +from ...utils.helpers import deprecation class ReadoutError: @@ -141,6 +142,16 @@ def ideal(self): return False def as_dict(self): + """ + DEPRECATED: Use to_dict() + Returns: + dict: The current error as a dictionary. + """ + deprecation("ReadoutError::as_dict() method is deprecated and will be removed after 0.3." + "Use '.to_dict()' instead") + return self.to_dict() + + def to_dict(self): """Return the current error as a dictionary.""" error = { "type": "roerror", diff --git a/qiskit/providers/aer/noise/noise_model.py b/qiskit/providers/aer/noise/noise_model.py index 234ecf840e..21ce26cec0 100644 --- a/qiskit/providers/aer/noise/noise_model.py +++ b/qiskit/providers/aer/noise/noise_model.py @@ -23,6 +23,7 @@ from .noiseerror import NoiseError from .errors.quantum_error import QuantumError from .errors.readout_error import ReadoutError +from ..utils.helpers import deprecation logger = logging.getLogger(__name__) @@ -550,6 +551,22 @@ def add_readout_error(self, error, qubits, warnings=True): self._noise_instructions.add("measure") def as_dict(self, serializable=False): + """ + DEPRECATED: Use to_dict() + Returns a dictionary for noise model. + + Args: + serializable (bool): if `True`, return a dict containing only types + that can be serializable by the stdlib `json` module. + + Returns: + dict: a dictionary for a noise model. + """ + deprecation("NoiseModel::as_dict() method is deprecated and will be removed after 0.3." + "Use '.to_dict()' instead") + self.to_dict(serializable) + + def to_dict(self, serializable=False): """ Return dictionary for noise model. @@ -564,14 +581,14 @@ def as_dict(self, serializable=False): # Add default quantum errors for name, error in self._default_quantum_errors.items(): - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [name] error_list.append(error_dict) # Add specific qubit errors for name, qubit_dict in self._local_quantum_errors.items(): for qubits_str, error in qubit_dict.items(): - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [name] error_dict["gate_qubits"] = [self._str2qubits(qubits_str)] error_list.append(error_dict) @@ -580,7 +597,7 @@ def as_dict(self, serializable=False): for name, qubit_dict in self._nonlocal_quantum_errors.items(): for qubits_str, noise_qubit_dict in qubit_dict.items(): for noise_qubits_str, error in noise_qubit_dict.items(): - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [name] error_dict["gate_qubits"] = [self._str2qubits(qubits_str)] error_dict["noise_qubits"] = [ @@ -590,12 +607,12 @@ def as_dict(self, serializable=False): # Add default readout error if self._default_readout_error is not None: - error_dict = self._default_readout_error.as_dict() + error_dict = self._default_readout_error.to_dict() error_list.append(error_dict) # Add local readout error for qubits_str, error in self._local_readout_errors.items(): - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["gate_qubits"] = [self._str2qubits(qubits_str)] error_list.append(error_dict) diff --git a/qiskit/providers/aer/noise/utils/noise_remapper.py b/qiskit/providers/aer/noise/utils/noise_remapper.py index 948d987b62..0bdbca87c8 100644 --- a/qiskit/providers/aer/noise/utils/noise_remapper.py +++ b/qiskit/providers/aer/noise/utils/noise_remapper.py @@ -84,7 +84,7 @@ def remap_noise_model(noise_model, remapping, discard_qubits=False, warnings=Tru raise NoiseError('Duplicate qubits in remapping: {}'.format(inv_map)) # Convert noise model to dict - nm_dict = noise_model.as_dict() + nm_dict = noise_model.to_dict() # Indexes of errors to keep new_errors = [] diff --git a/qiskit/providers/aer/noise/utils/noise_transformation.py b/qiskit/providers/aer/noise/utils/noise_transformation.py index 9cd119a610..f6a1c9d901 100644 --- a/qiskit/providers/aer/noise/utils/noise_transformation.py +++ b/qiskit/providers/aer/noise/utils/noise_transformation.py @@ -152,7 +152,7 @@ def approximate_noise_model(model, *, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [operation] error_list.append(error_dict) @@ -164,7 +164,7 @@ def approximate_noise_model(model, *, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [operation] error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] error_list.append(error_dict) @@ -178,7 +178,7 @@ def approximate_noise_model(model, *, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["operations"] = [operation] error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] error_dict["noise_qubits"] = [model._str2qubits(noise_str)] @@ -191,7 +191,7 @@ def approximate_noise_model(model, *, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) - error_dict = error.as_dict() + error_dict = error.to_dict() error_list.append(error_dict) # Add local readout error @@ -201,7 +201,7 @@ def approximate_noise_model(model, *, operator_string=operator_string, operator_dict=operator_dict, operator_list=operator_list) - error_dict = error.as_dict() + error_dict = error.to_dict() error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] error_list.append(error_dict) diff --git a/qiskit/providers/aer/utils/__init__.py b/qiskit/providers/aer/utils/__init__.py index 4f3ab871f1..133948e85b 100644 --- a/qiskit/providers/aer/utils/__init__.py +++ b/qiskit/providers/aer/utils/__init__.py @@ -13,3 +13,4 @@ """Utilities""" from . import qobj_utils +from . import helpers diff --git a/qiskit/providers/aer/utils/helpers.py b/qiskit/providers/aer/utils/helpers.py new file mode 100644 index 0000000000..92b967aeec --- /dev/null +++ b/qiskit/providers/aer/utils/helpers.py @@ -0,0 +1,22 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +A bunch of usefull helper functions +""" + +from warnings import warn + + +def deprecation(message): + """ Shows a deprecation message to the user """ + warn(message, DeprecationWarning, stacklevel=2) diff --git a/test/terra/noise/test_noise_model.py b/test/terra/noise/test_noise_model.py index f56d719ddd..57ad2fd5c5 100644 --- a/test/terra/noise/test_noise_model.py +++ b/test/terra/noise/test_noise_model.py @@ -756,8 +756,6 @@ def test_noise_models_equal(self): error1 = pauli_error([['X', 1]], standard_gates=False) error2 = pauli_error([['X', 1]], standard_gates=True) - print("error1: {}\nerror2: {}".format(error1, error2)) - model1 = NoiseModel() model1.add_all_qubit_quantum_error(error1, ['u3'], False) model1.add_quantum_error(error1, ['u3'], [2], False) From ccb63a0042908c170392b2536a134fbaaee2d921 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Fri, 7 Jun 2019 14:59:00 -0400 Subject: [PATCH 04/29] Move CONTRIBUTING.md to top level (#229) * Move CONTRIBUTING.md to top level The CONTRIBUTING.md doc contains a lot of useful information about the not only contributing to the project but also some useful information about project policy and conventions. But it's currently in a hidden directory so that github ui will show it on a help menu. So unless you know to click on that help menu or that it's hidding in the .github directory you'll never see it. This commit addresses this by moving the file to the repo root so it's more obvious where it is. Mirror of Qiskit/qiskit-terra#2359 * Move code of conduct and convert changelog to md --- CHANGELOG.md | 149 ++++++++++++++++++ CHANGELOG.rst | 139 ---------------- .../CODE_OF_CONDUCT.md => CODE_OF_CONDUCT.md | 0 .github/CONTRIBUTING.md => CONTRIBUTING.md | 0 4 files changed, 149 insertions(+), 139 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 CHANGELOG.rst rename .github/CODE_OF_CONDUCT.md => CODE_OF_CONDUCT.md (100%) rename .github/CONTRIBUTING.md => CONTRIBUTING.md (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..10a4f26257 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,149 @@ +Changelog +========= + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a +Changelog](http://keepachangelog.com/en/1.0.0/). + +> **Types of changes:** +> +> - **Added**: for new features. +> - **Changed**: for changes in existing functionality. +> - **Deprecated**: for soon-to-be removed features. +> - **Removed**: for now removed features. +> - **Fixed**: for any bug fixes. +> - **Security**: in case of vulnerabilities. + +[UNRELEASED](https://github.com/Qiskit/qiskit-aer/compare/0.2.1...HEAD) +======================================================================= + +Added +----- + +Changed +------- + +Removed +------- + +Fixed +----- + +[0.2.1](https://github.com/Qiskit/qiskit-aer/compare/0.2.0...0.2.1) - 2019-05-20 +================================================================================ + +Added +----- + +Changed +------- + +- Deprecate the use of \".as\_dict()\" in favor of \".to\_dict()\" + (\#228) +- Set simulator seed from \"seed\_simulator\" in qobj (\#210) + +Removed +------- + +Fixed +----- + +- Fix memory error handling for huge circuits (\#216) +- Fix equality expressions in Python code (\#208) + +[0.2.0](https://github.com/Qiskit/qiskit-aer/compare/0.1.1...0.2.0) - 2019-05-02 +================================================================================ + +Added +----- + +- Add multiplexer gate (\#192) +- Add [remap\_noise\_model]{.title-ref} function to noise.utils + (\#181) +- Add [\_\_eq\_\_]{.title-ref} method to NoiseModel, QuantumError, + ReadoutError (\#181) +- Add support for labelled gates in noise models (\#175). +- Add optimized mcx, mcy, mcz, mcu1, mcu2, mcu3, gates to QubitVector + (\#124) +- Add optimized controlled-swap gate to QubitVector (\#142) +- Add gate-fusion optimization for QasmContoroller, which is enabled + by setting fusion\_enable=true (\#136) +- Add better management of failed simulations (\#167) +- Add qubits truncate optimization for unused qubits (\#164) +- Add ability to disable depolarizing error on device noise model + (\#131) +- Add initialise simulator instruction to statevector\_state (\#117, + \#137) +- Add coupling maps to simulators (\#93) +- Add circuit optimization framework (\#83) +- Add benchmarking (\#71, \#177) +- Add wheels support for Debian-like distributions (\#69) +- Add autoconfiguration of threads for qasm simulator (\#61) +- Add Simulation method based on Stabilizer Rank Decompositions (\#51) + +Changed +------- + +- Add basis\_gates kwarg to NoiseModel init (\#175). +- Depreciated \"initial\_statevector\" backend option for + QasmSimulator and StatevectorSimulator (\#185) +- Rename \"chop\_threshold\" backend option to \"zero\_threshold\" and + change default value to 1e-10 (\#185). +- Add an optional parameter to [NoiseModel.as\_dict()]{.title-ref} for + returning dictionaries that can be serialized using the standard + [json]{.title-ref} library directly. (\#165) +- Refactor thread management (\#50) + +Removed +------- + +Fixed +----- + +- Improve noise transformations (\#162) +- Improve error reporting (\#160) +- Improve efficiency of parallelization with max\_memory\_mb a new + parameter of backend\_opts (\#61) +- Improve u1 performance in statevector (\#123) +- Fix OpenMP clashing problems on MacOS for the Terra Addon (\#46) + +[0.1.1](https://github.com/Qiskit/qiskit-aer/compare/0.1.0...0.1.1) - 2019-01-24 +================================================================================ + +Added +----- + +- Adds version information when using the standalone simulator (\#36) +- Adds a Clifford stabilizer simulation method to the QasmSimulator + (\#13) +- Improve Circuit and NoiseModel instructions checking (\#31) +- Add reset\_error function to Noise models (\#34) +- Improve error reporting at installation time (\#29) +- Validate n\_qubits before execution (\#24) +- Add qobj method to AerJob (\#19) + +Removed +------- + +- Reference model tests removed from the codebase (\#27) + +Fixed +----- + +- Fix Contributing guide (\#33) +- Fix an import in Terra integration tests (\#33) +- Fix non-OpenMP builds (\#19) + +[0.1.0](https://github.com/Qiskit/qiskit-aer/compare/0.0.0...0.1.0) - 2018-12-19 +================================================================================ + +Added +----- + +- QASM Simulator +- Statevector Simulator +- Unitary Simulator +- Noise models +- Terra integration +- Standalone Simulators support diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 56a192669b..0000000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,139 +0,0 @@ -Changelog -========= - -All notable changes to this project will be documented in this file. - -The format is based on `Keep a Changelog`_. - - **Types of changes:** - - - **Added**: for new features. - - **Changed**: for changes in existing functionality. - - **Deprecated**: for soon-to-be removed features. - - **Removed**: for now removed features. - - **Fixed**: for any bug fixes. - - **Security**: in case of vulnerabilities. - -`UNRELEASED`_ -============= - -Added ------ - -Changed -------- - -Removed -------- - -Fixed ------ - - -`0.2.1`_ - 2019-05-20 -===================== - -Added ------ - -Changed -------- -- Deprecate the use of ".as_dict()" in favor of ".to_dict()" (#228) -- Set simulator seed from "seed_simulator" in qobj (#210) - -Removed -------- - -Fixed ------ -- Fix memory error handling for huge circuits (#216) -- Fix equality expressions in Python code (#208) - -`0.2.0`_ - 2019-05-02 -===================== - -Added ------ -- Add multiplexer gate (#192) -- Add `remap_noise_model` function to noise.utils (#181) -- Add `__eq__` method to NoiseModel, QuantumError, ReadoutError (#181) -- Add support for labelled gates in noise models (#175). -- Add optimized mcx, mcy, mcz, mcu1, mcu2, mcu3, gates to QubitVector (#124) -- Add optimized controlled-swap gate to QubitVector (#142) -- Add gate-fusion optimization for QasmContoroller, which is enabled by setting fusion_enable=true (#136) -- Add better management of failed simulations (#167) -- Add qubits truncate optimization for unused qubits (#164) -- Add ability to disable depolarizing error on device noise model (#131) -- Add initialise simulator instruction to statevector_state (#117, #137) -- Add coupling maps to simulators (#93) -- Add circuit optimization framework (#83) -- Add benchmarking (#71, #177) -- Add wheels support for Debian-like distributions (#69) -- Add autoconfiguration of threads for qasm simulator (#61) -- Add Simulation method based on Stabilizer Rank Decompositions (#51) - -Changed -------- -- Add basis_gates kwarg to NoiseModel init (#175). -- Depreciated "initial_statevector" backend option for QasmSimulator and StatevectorSimulator (#185) -- Rename "chop_threshold" backend option to "zero_threshold" and change default value to 1e-10 (#185). -- Add an optional parameter to `NoiseModel.as_dict()` for returning dictionaries that can be - serialized using the standard `json` library directly. (#165) -- Refactor thread management (#50) - -Removed -------- - -Fixed ------ -- Improve noise transformations (#162) -- Improve error reporting (#160) -- Improve efficiency of parallelization with max_memory_mb a new parameter of backend_opts (#61) -- Improve u1 performance in statevector (#123) -- Fix OpenMP clashing problems on MacOS for the Terra Addon (#46) - -`0.1.1`_ - 2019-01-24 -===================== - -Added ------ -- Adds version information when using the standalone simulator (#36) -- Adds a Clifford stabilizer simulation method to the QasmSimulator (#13) -- Improve Circuit and NoiseModel instructions checking (#31) -- Add reset_error function to Noise models (#34) -- Improve error reporting at installation time (#29) -- Validate n_qubits before execution (#24) -- Add qobj method to AerJob (#19) - -Removed -------- -- Reference model tests removed from the codebase (#27) - -Fixed ------ -- Fix Contributing guide (#33) -- Fix an import in Terra integration tests (#33) -- Fix non-OpenMP builds (#19) - - - -`0.1.0`_ - 2018-12-19 -===================== - -Added ------ -- QASM Simulator -- Statevector Simulator -- Unitary Simulator -- Noise models -- Terra integration -- Standalone Simulators support - - -.. _UNRELEASED: https://github.com/Qiskit/qiskit-aer/compare/0.2.1...HEAD -.. _0.2.1: https://github.com/Qiskit/qiskit-aer/compare/0.2.0...0.2.1 -.. _0.2.0: https://github.com/Qiskit/qiskit-aer/compare/0.1.1...0.2.0 -.. _0.1.1: https://github.com/Qiskit/qiskit-aer/compare/0.1.0...0.1.1 -.. _0.1.0: https://github.com/Qiskit/qiskit-aer/compare/0.0.0...0.1.0 - -.. _Keep a Changelog: http://keepachangelog.com/en/1.0.0/ diff --git a/.github/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md similarity index 100% rename from .github/CODE_OF_CONDUCT.md rename to CODE_OF_CONDUCT.md diff --git a/.github/CONTRIBUTING.md b/CONTRIBUTING.md similarity index 100% rename from .github/CONTRIBUTING.md rename to CONTRIBUTING.md From a33079134e13b46c135c06698367d2b6591b4ee3 Mon Sep 17 00:00:00 2001 From: hhorii Date: Tue, 18 Jun 2019 13:11:19 +0100 Subject: [PATCH 05/29] fix a critical bug in fusion and add tests (#238) --- src/simulators/statevector/qubitvector.hpp | 4 +- src/transpile/fusion.hpp | 8 +- test/benchmark/tools.py | 23 +++++ .../backends/qasm_simulator/qasm_fusion.py | 88 +++++++++++++++++-- 4 files changed, 110 insertions(+), 13 deletions(-) diff --git a/src/simulators/statevector/qubitvector.hpp b/src/simulators/statevector/qubitvector.hpp index 15aea83068..15c6462bbe 100755 --- a/src/simulators/statevector/qubitvector.hpp +++ b/src/simulators/statevector/qubitvector.hpp @@ -1901,10 +1901,10 @@ cvector_t QubitVector::expand_matrix(const reg_t& src_qubits, const reg_ // 1. identify low and high delta auto low = std::distance(dst_sorted_qubits.begin(), - std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), src_qubits[0])); + std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[0])); auto high = std::distance(dst_sorted_qubits.begin(), - std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), src_qubits[1])); + std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[1])); auto low_delta = BITS[low]; auto high_delta = BITS[high]; diff --git a/src/transpile/fusion.hpp b/src/transpile/fusion.hpp index 0a05b567f6..80447137ff 100644 --- a/src/transpile/fusion.hpp +++ b/src/transpile/fusion.hpp @@ -184,12 +184,12 @@ void Fusion::optimize_circuit(Circuit& circ, if (ret) { circ.ops = optimized_ops; + if (verbose_) { + data.add_additional_data("metadata", + json_t::object({{"fusion_verbose", optimized_ops}})); + } } - if (verbose_) { - data.add_additional_data("metadata", - json_t::object({{"fusion_verbose", optimized_ops}})); - } #ifdef DEBUG dump(optimized_ops); diff --git a/test/benchmark/tools.py b/test/benchmark/tools.py index 9b8bccad7a..16574c88a3 100644 --- a/test/benchmark/tools.py +++ b/test/benchmark/tools.py @@ -122,6 +122,29 @@ def quantum_volume_circuit(num_qubits, depth, measure=True, seed=None): # Transpile to force it into u1,u2,u3,cx basis gates return transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx']) +def qft_circuit(num_qubits, measure=True): + """Create a qft circuit. + + Args: + num_qubits (int): number of qubits + measure (bool): include measurement in circuit. + + Returns: + QftCircuit: A qft circuit. + """ + # Create quantum/classical registers of size n + qr = QuantumRegister(num_qubits) + circuit = QuantumCircuit(qr) + + for i in range(num_qubits): + for j in range(i): + circuit.cu1(math.pi/float(2**(i-j)), qr[i], qr[j]) + circuit.h(qr[i]) + + if measure is True: + circuit = _add_measurements(circuit, qr) + # Transpile to force it into u1,u2,u3,cx basis gates + return transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx']) def simple_u3_circuit(num_qubits, measure=True): """Creates a simple circuit composed by u3 gates, with measurements or not diff --git a/test/terra/backends/qasm_simulator/qasm_fusion.py b/test/terra/backends/qasm_simulator/qasm_fusion.py index 2f7179a1cd..e54e46f0d1 100644 --- a/test/terra/backends/qasm_simulator/qasm_fusion.py +++ b/test/terra/backends/qasm_simulator/qasm_fusion.py @@ -19,6 +19,7 @@ from qiskit.providers.aer import QasmSimulator from qiskit.providers.aer.noise import NoiseModel from qiskit.providers.aer.noise.errors import ReadoutError, depolarizing_error +from test.benchmark.tools import quantum_volume_circuit, qft_circuit class QasmFusionTests: """QasmSimulator fusion tests.""" @@ -269,17 +270,15 @@ def test_fusion_operations(self): circuit.h(qr[i]) circuit.barrier(qr) - circuit.x(qr[0]) + circuit.u3(0.1, 0.1, 0.1, qr[0]) circuit.barrier(qr) - circuit.x(qr[1]) + circuit.u3(0.1, 0.1, 0.1, qr[1]) circuit.barrier(qr) - circuit.x(qr[0]) + circuit.cx(qr[1], qr[0]) circuit.barrier(qr) - circuit.x(qr[1]) - circuit.barrier(qr) - circuit.cx(qr[2], qr[3]) + circuit.u3(0.1, 0.1, 0.1, qr[0]) circuit.barrier(qr) - circuit.u3(0.1, 0.1, 0.1, qr[3]) + circuit.u3(0.1, 0.1, 0.1, qr[1]) circuit.barrier(qr) circuit.u3(0.1, 0.1, 0.1, qr[3]) circuit.barrier(qr) @@ -347,3 +346,78 @@ def test_fusion_operations(self): result_nonfusion.get_counts(circuit), delta=0.0, msg="fusion x-x-x was failed") + + + def test_fusion_qv(self): + """Test Fusion with quantum volume""" + shots = 100 + + circuit = quantum_volume_circuit(10, 1, measure=True, seed=0) + qobj = assemble([circuit], self.SIMULATOR, shots=shots, seed_simulator=1) + + backend_options = self.BACKEND_OPTS.copy() + backend_options['fusion_enable'] = True + backend_options['fusion_verbose'] = True + backend_options['fusion_threshold'] = 1 + backend_options['optimize_ideal_threshold'] = 1 + backend_options['optimize_noise_threshold'] = 1 + + result_fusion = self.SIMULATOR.run( + qobj, + backend_options=backend_options).result() + self.is_completed(result_fusion) + + backend_options = self.BACKEND_OPTS.copy() + backend_options['fusion_enable'] = False + backend_options['fusion_verbose'] = True + backend_options['fusion_threshold'] = 1 + backend_options['optimize_ideal_threshold'] = 1 + backend_options['optimize_noise_threshold'] = 1 + + result_nonfusion = self.SIMULATOR.run( + qobj, + backend_options=backend_options).result() + self.is_completed(result_nonfusion) + + self.assertDictAlmostEqual( + result_fusion.get_counts(circuit), + result_nonfusion.get_counts(circuit), + delta=0.0, + msg="fusion for qv was failed") + + def test_fusion_qft(self): + """Test Fusion with qft""" + shots = 100 + + circuit = qft_circuit(10, measure=True) + qobj = assemble([circuit], self.SIMULATOR, shots=shots, seed_simulator=1) + + backend_options = self.BACKEND_OPTS.copy() + backend_options['fusion_enable'] = True + backend_options['fusion_verbose'] = True + backend_options['fusion_threshold'] = 1 + backend_options['optimize_ideal_threshold'] = 1 + backend_options['optimize_noise_threshold'] = 1 + + result_fusion = self.SIMULATOR.run( + qobj, + backend_options=backend_options).result() + self.is_completed(result_fusion) + + backend_options = self.BACKEND_OPTS.copy() + backend_options['fusion_enable'] = False + backend_options['fusion_verbose'] = True + backend_options['fusion_threshold'] = 1 + backend_options['optimize_ideal_threshold'] = 1 + backend_options['optimize_noise_threshold'] = 1 + + result_nonfusion = self.SIMULATOR.run( + qobj, + backend_options=backend_options).result() + self.is_completed(result_nonfusion) + + self.assertDictAlmostEqual( + result_fusion.get_counts(circuit), + result_nonfusion.get_counts(circuit), + delta=0.0, + msg="fusion for qft was failed") \ No newline at end of file From 2da46e9ca23ed62b0fb8e5ae5242a1b2266a3b67 Mon Sep 17 00:00:00 2001 From: gadial Date: Tue, 18 Jun 2019 16:09:45 +0300 Subject: [PATCH 06/29] Noise transformation qubit extension (#236) * Extending the noise transformation util to work with more than 1 qubit * Tests for multiple-qubits noise transformation * Style improvements * More style for noise transformation * Small change to support Python 3.5 on Windows --- .../aer/noise/utils/noise_transformation.py | 186 +++++++++++++----- test/terra/noise/test_noise_transformation.py | 51 +++++ 2 files changed, 183 insertions(+), 54 deletions(-) diff --git a/qiskit/providers/aer/noise/utils/noise_transformation.py b/qiskit/providers/aer/noise/utils/noise_transformation.py index f6a1c9d901..c37da91241 100644 --- a/qiskit/providers/aer/noise/utils/noise_transformation.py +++ b/qiskit/providers/aer/noise/utils/noise_transformation.py @@ -72,18 +72,22 @@ def approximate_quantum_error(error, *, if not isinstance(error, QuantumError): error = QuantumError(error) - if error.number_of_qubits > 1: - raise NoiseError("Only 1-qubit noises can be converted, {}-qubit " + if error.number_of_qubits > 2: + raise NoiseError("Only 1-qubit and 2-qubit noises can be converted, {}-qubit " "noise found in model".format(error.number_of_qubits)) error_kraus_operators = Kraus(error.to_quantumchannel()).data transformer = NoiseTransformer() if operator_string is not None: + no_info_error = "No information about noise type {}".format(operator_string) operator_string = operator_string.lower() if operator_string not in transformer.named_operators.keys(): + raise RuntimeError(no_info_error) + operator_lists = transformer.named_operators[operator_string] + if len(operator_lists) < error.number_of_qubits: raise RuntimeError( - "No information about noise type {}".format(operator_string)) - operator_dict = transformer.named_operators[operator_string] + no_info_error + " for {} qubits".format(error.number_of_qubits)) + operator_dict = operator_lists[error.number_of_qubits - 1] if operator_dict is not None: names, operator_list = zip(*operator_dict.items()) if operator_list is not None: @@ -95,8 +99,7 @@ def approximate_quantum_error(error, *, identity_prob = 1 - sum(probabilities) if identity_prob < 0 or identity_prob > 1: raise RuntimeError( - "Approximated channel operators probabilities sum to {}". - format(1 - identity_prob)) + "Channel probabilities sum to {}".format(1 - identity_prob)) quantum_error_spec = [([{'name': 'id', 'qubits': [0]}], identity_prob)] op_circuit_list = [ transformer.operator_circuit(operator) @@ -214,22 +217,76 @@ def approximate_noise_model(model, *, return approx_noise_model +def pauli_operators(): + """ + Returns: + list: a list of Pauli operators for 1 and 2 qubits + + """ + pauli_1_qubit = { + 'X': [{'name': 'x', 'qubits': [0]}], + 'Y': [{'name': 'y', 'qubits': [0]}], + 'Z': [{'name': 'z', 'qubits': [0]}] + } + pauli_2_qubit = { + 'XI': [{'name': 'x', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], + 'YI': [{'name': 'y', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], + 'ZI': [{'name': 'z', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], + 'IX': [{'name': 'id', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], + 'IY': [{'name': 'id', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], + 'IZ': [{'name': 'id', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], + 'XX': [{'name': 'x', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], + 'YY': [{'name': 'y', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], + 'ZZ': [{'name': 'z', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], + 'XY': [{'name': 'x', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], + 'XZ': [{'name': 'x', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], + 'YX': [{'name': 'y', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], + 'YZ': [{'name': 'y', 'qubits': [1]}, {'name': 'z', 'qubits': [0]}], + 'ZX': [{'name': 'z', 'qubits': [1]}, {'name': 'x', 'qubits': [0]}], + 'ZY': [{'name': 'z', 'qubits': [1]}, {'name': 'y', 'qubits': [0]}], + } + return [pauli_1_qubit, pauli_2_qubit] + + +def reset_operators(): + """ + Returns: + list: a list of reset operators for 1 and 2 qubits + + """ + reset_0_to_0 = [{'name': 'reset', 'qubits': [0]}] + reset_0_to_1 = [{'name': 'reset', 'qubits': [0]}, {'name': 'x', 'qubits': [0]}] + reset_1_to_0 = [{'name': 'reset', 'qubits': [1]}] + reset_1_to_1 = [{'name': 'reset', 'qubits': [1]}, {'name': 'x', 'qubits': [1]}] + id_0 = [{'name': 'id', 'qubits': [0]}] + id_1 = [{'name': 'id', 'qubits': [1]}] + + reset_1_qubit = { + 'p': reset_0_to_0, + 'q': reset_0_to_1 + } + + reset_2_qubit = { + 'p0': reset_0_to_0 + id_1, + 'q0': reset_0_to_1 + id_1, + 'p1': reset_1_to_0 + id_0, + 'q1': reset_1_to_1 + id_0, + 'p0_p1': reset_0_to_0 + reset_1_to_0, + 'p0_q1': reset_0_to_0 + reset_1_to_1, + 'q0_p1': reset_0_to_1 + reset_1_to_0, + 'q0_q1': reset_0_to_1 + reset_1_to_1, + } + return [reset_1_qubit, reset_2_qubit] + + class NoiseTransformer: """Transforms one quantum channel to another based on a specified criteria.""" def __init__(self): self.named_operators = { - 'pauli': { - 'X': [{'name': 'x', 'qubits': [0]}], - 'Y': [{'name': 'y', 'qubits': [0]}], - 'Z': [{'name': 'z', 'qubits': [0]}] - }, - 'reset': { - 'p': [{'name': 'reset', 'qubits': [0]}], # reset to |0> - 'q': [{'name': 'reset', 'qubits': [0]}, - {'name': 'x', 'qubits': [0]}] # reset to |1> - }, - 'clifford': {j: single_qubit_clifford_instructions(j) for j in range(1, 24)} + 'pauli': pauli_operators(), + 'reset': reset_operators(), + 'clifford': [{j: single_qubit_clifford_instructions(j) for j in range(24)}] } self.fidelity_data = None self.use_honesty_constraint = True @@ -327,11 +384,13 @@ def prepare_channel_operator_list(ops_list): """ # convert to sympy matrices and verify that each singleton is # in a tuple; also add identity matrix - result = [[sympy.eye(2)]] + result = [] for ops in ops_list: if not isinstance(ops, tuple) and not isinstance(ops, list): ops = [ops] result.append([sympy.Matrix(op) for op in ops]) + n = result[0][0].shape[0] # grab the dimensions from the first element + result = [[sympy.eye(n)]] + result return result # pylint: disable=invalid-name @@ -353,7 +412,7 @@ def prepare_honesty_constraint(self, transform_channel_operators_list): self.fidelity_data = { 'goal': goal, 'coefficients': - coefficients[1:] # coefficients[0] corresponds to I + coefficients[1:] # coefficients[0] corresponds to I } # methods relevant to the transformation to quadratic programming instance @@ -361,7 +420,7 @@ def prepare_honesty_constraint(self, transform_channel_operators_list): @staticmethod def fidelity(channel): """ Calculates channel fidelity """ - return sum([numpy.abs(numpy.trace(E))**2 for E in channel]) + return sum([numpy.abs(numpy.trace(E)) ** 2 for E in channel]) # pylint: disable=invalid-name def generate_channel_matrices(self, transform_channel_operators_list): @@ -411,7 +470,7 @@ def generate_channel_matrices(self, transform_channel_operators_list): symbolic_operators = [ op for ops in symbolic_operators_list for op in ops ] - # channel_matrix_representation() peforms the required linear + # channel_matrix_representation() performs the required linear # algebra to find the representing matrices. operators_channel = self.channel_matrix_representation( symbolic_operators).subs(symbols[0], 1 - exp) @@ -449,23 +508,25 @@ def flatten_matrix(m): def channel_matrix_representation(self, operators): """ - We convert the operators to a matrix by applying the channel to - the four basis elements of the 2x2 matrix space representing - density operators; this is standard linear algebra + We convert the operators to a matrix by applying the channel to + the four basis elements of the 2x2 matrix space representing + density operators; this is standard linear algebra - Args: - operators (list): The list of operators to transform into a Matrix + Args: + operators (list): The list of operators to transform into a Matrix - Returns: - sympy.Matrix: The matrx representation of the operators - """ + Returns: + sympy.Matrix: The matrx representation of the operators + """ + + shape = operators[0].shape + standard_base = [] + for i in range(shape[0]): + for j in range(shape[1]): + basis_element_ij = sympy.zeros(*shape) + basis_element_ij[(i, j)] = 1 + standard_base.append(basis_element_ij) - standard_base = [ - sympy.Matrix([[1, 0], [0, 0]]), - sympy.Matrix([[0, 1], [0, 0]]), - sympy.Matrix([[0, 0], [1, 0]]), - sympy.Matrix([[0, 0], [0, 1]]) - ] return (sympy.Matrix([ self.flatten_matrix( self.compute_channel_operation(rho, operators)) @@ -483,9 +544,10 @@ def generate_channel_quadratic_programming_matrices( list: A list of 4x4 complex matrices ([D1, D2, ..., Dn], E) such that: channel == x1*D1 + ... + xn*Dn + E """ - return ([ - self.get_matrix_from_channel(channel, symbol) for symbol in symbols - ], self.get_const_matrix_from_channel(channel, symbols)) + return ( + [self.get_matrix_from_channel(channel, symbol) for symbol in symbols], + self.get_const_matrix_from_channel(channel, symbols) + ) @staticmethod def get_matrix_from_channel(channel, symbol): @@ -541,17 +603,23 @@ def get_const_matrix_from_channel(channel, symbols): def transform_by_given_channel(self, channel_matrices, const_channel_matrix): """ - This method creates the quadratic programming instance for - minimizing the Hilbert-Schmidt norm of the matrix (A-B) obtained + This method creates objective function representing the + Hilbert-Schmidt norm of the matrix (A-B) obtained as the difference of the input noise channel and the output channel we wish to determine. + This function is represented by a matrix P and a vector q, such that + f(x) = 1/2(x*P*x)+q*x + where x is the vector we wish to minimize, where x represents + probabilities for the noise operators that construct the output channel + Args: - channel_matrices (TODO): TODO - const_channel_matrix (TODO): TODD + channel_matrices (list): A list of 4x4 symbolic matrices + const_channel_matrix (matrix): a 4x4 constant matrix Returns: - quadratic_program: TODO + list: a list of the optimal probabilities for the channel matrices, + determined by the quadratic program solver """ target_channel = SuperOp(Kraus(self.noise_kraus_operators)) target_channel_matrix = target_channel._data.T @@ -563,12 +631,14 @@ def transform_by_given_channel(self, channel_matrices, def compute_P(self, As): """ - TODO + This method creates the matrix P in the + f(x) = 1/2(x*P*x)+q*x + representation of the objective function Args: - As (TODO): TODO + As (list): list of symbolic matrices repersenting the channel matrices Returns: - Array: TODO + matrix: The matrix P for the description of the quadaric program """ vs = [numpy.array(A).flatten() for A in As] n = len(vs) @@ -579,13 +649,15 @@ def compute_P(self, As): def compute_q(self, As, C): """ - TODO + This method creates the vector q for the + f(x) = 1/2(x*P*x)+q*x + representation of the objective function Args: - As (TODO): TODO - C (TODO): TODO + As (list): list of symbolic matrices repersenting the quadratic program + C (matrix): matrix representing the the constant channel matrix Returns: - Array: TODO + list: The vector q for the description of the quadaric program """ vs = [numpy.array(A).flatten() for A in As] vC = numpy.array(C).flatten() @@ -597,13 +669,19 @@ def compute_q(self, As, C): def solve_quadratic_program(self, P, q): """ - TODO + This function solved the quadratic program to minimize the objective function + f(x) = 1/2(x*P*x)+q*x + subject to the additional constraints + Gx <= h + + Where P, q are given and G,h are computed to ensure that x represents + a probability vector and subject to honesty constraints if required Args: - P (TODO): TODO - q (TODO): TODO + P (matrix): A matrix representing the P component of the objective function + q (list): A vector representing the q component of the objective function Returns: - Array: TODO + list: The solution of the quadratic program (represents probabilites) Raises: ImportError: If cvxopt external module is not installed diff --git a/test/terra/noise/test_noise_transformation.py b/test/terra/noise/test_noise_transformation.py index 6906e002b2..8e7db93c80 100644 --- a/test/terra/noise/test_noise_transformation.py +++ b/test/terra/noise/test_noise_transformation.py @@ -23,6 +23,7 @@ from qiskit.providers.aer.noise.errors.standard_errors import amplitude_damping_error from qiskit.providers.aer.noise.errors.standard_errors import reset_error from qiskit.providers.aer.noise.errors.standard_errors import pauli_error +from qiskit.providers.aer.noise.errors.quantum_error import QuantumError try: import cvxopt @@ -234,6 +235,56 @@ def test_approx_names(self): results_2 = approximate_quantum_error(error, operator_string="Pauli") self.assertErrorsAlmostEqual(results_1, results_2) + def test_paulis_1_and_2_qubits(self): + probs = [0.5, 0.3, 0.2] + paulis_1q = ['X', 'Y', 'Z'] + paulis_2q = ['XI', 'YI', 'ZI'] + + error_1q = pauli_error(zip(paulis_1q, probs)) + error_2q = pauli_error(zip(paulis_2q, probs)) + + results_1q = approximate_quantum_error(error_1q, operator_string="pauli") + results_2q = approximate_quantum_error(error_2q, operator_string="pauli") + + self.assertErrorsAlmostEqual(error_1q, results_1q) + self.assertErrorsAlmostEqual(error_2q, results_2q, places = 2) + + paulis_2q = ['XY', 'ZZ', 'YI'] + error_2q = pauli_error(zip(paulis_2q, probs)) + results_2q = approximate_quantum_error(error_2q, operator_string="pauli") + self.assertErrorsAlmostEqual(error_2q, results_2q, places=2) + + def test_reset_2_qubit(self): + # approximating amplitude damping using relaxation operators + gamma = 0.23 + p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2 + q = 0 + A0 = [[1, 0], [0, numpy.sqrt(1 - gamma)]] + A1 = [[0, numpy.sqrt(gamma)], [0, 0]] + error_1 = QuantumError([([{'name': 'kraus', 'qubits': [0], 'params': [A0, A1]}, + {'name': 'id', 'qubits': [1]} + ], 1)]) + error_2 = QuantumError([([{'name': 'kraus', 'qubits': [1], 'params': [A0, A1]}, + {'name': 'id', 'qubits': [0]} + ], 1)]) + + expected_results_1 = QuantumError([ + ([{'name': 'id', 'qubits': [0]}, {'name': 'id', 'qubits': [1]}], 1-p), + ([{'name': 'reset', 'qubits': [0]}, {'name': 'id', 'qubits': [1]}],p), + ]) + expected_results_2 = QuantumError([ + ([{'name': 'id', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], 1 - p), + ([{'name': 'reset', 'qubits': [1]}, {'name': 'id', 'qubits': [0]}], p), + ]) + + results_1 = approximate_quantum_error(error_1, operator_string="reset") + results_2 = approximate_quantum_error(error_2, operator_string="reset") + + self.assertErrorsAlmostEqual(results_1, expected_results_1) + self.assertErrorsAlmostEqual(results_2, expected_results_2) + + + def test_errors(self): gamma = 0.23 error = amplitude_damping_error(gamma) From cedbab46f1b30bdfe08f517179d9aa7c52b19bc1 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Mon, 24 Jun 2019 17:18:16 +0200 Subject: [PATCH 07/29] Fix depolarizing error and basic device noise model (#243) * fix depolarizing-only error device noise model * Add max for p_depol for basic_device_noise_model * Allow depolarizing_error parameter greater than 1 if channel is still CPTP --- CHANGELOG.md | 4 ++ qiskit/providers/aer/noise/device/models.py | 41 ++++++++++++------- .../aer/noise/errors/standard_errors.py | 33 ++++++++++----- 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10a4f26257..a118392059 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,10 @@ Removed Fixed ----- +- Change maximum parameter for depolarizing_error to allow for error channel + with no identity component. (\#243) +- Fixed 2-qubit depolarizing-only error parameter calculation in + basic_device_noise_model (\#243) [0.2.1](https://github.com/Qiskit/qiskit-aer/compare/0.2.0...0.2.1) - 2019-05-20 ================================================================================ diff --git a/qiskit/providers/aer/noise/device/models.py b/qiskit/providers/aer/noise/device/models.py index bdbfba124b..6e6b2154f1 100644 --- a/qiskit/providers/aer/noise/device/models.py +++ b/qiskit/providers/aer/noise/device/models.py @@ -265,21 +265,28 @@ def _device_depolarizing_error(qubits, # with dim = 2 ** N for an N-qubit gate error. error = None + num_qubits = len(qubits) + dim = 2 ** num_qubits + if not thermal_relaxation: # Model gate error entirely as depolarizing error - p_depol = _depol_error_value_one_qubit(error_param) + if error_param is not None and error_param > 0: + dim = 2 ** num_qubits + depol_param = dim * error_param / (dim - 1) + else: + depol_param = 0 else: # Model gate error as thermal relaxation and depolarizing # error. # Get depolarizing probability - if len(qubits) == 1: + if num_qubits == 1: t1, t2, _ = relax_params[qubits[0]] - p_depol = _depol_error_value_one_qubit( + depol_param = _depol_error_value_one_qubit( error_param, gate_time, t1=t1, t2=t2) - elif len(qubits) == 2: + elif num_qubits == 2: q0_t1, q0_t2, _ = relax_params[qubits[0]] q1_t1, q1_t2, _ = relax_params[qubits[1]] - p_depol = _depol_error_value_two_qubit( + depol_param = _depol_error_value_two_qubit( error_param, gate_time, qubit0_t1=q0_t1, @@ -290,9 +297,15 @@ def _device_depolarizing_error(qubits, raise NoiseError("Device noise model only supports " "1 and 2-qubit gates when using " "thermal_relaxation=True.") - if p_depol > 0: + if depol_param > 0: + # If the device reports an error_param greater than the maximum + # allowed for a depolarzing error model we will get a non-physical + # depolarizing parameter. + # In this case we truncate it to 1 so that the error channel is a + # completely depolarizing channel E(rho) = id / d + depol_param = min(depol_param, 1.0) error = depolarizing_error( - p_depol, len(qubits), standard_gates=standard_gates) + depol_param, num_qubits, standard_gates=standard_gates) return error @@ -343,7 +356,7 @@ def _excited_population(freq, temperature): def _depol_error_value_one_qubit(error_param, gate_time=0, t1=inf, t2=inf): - """Return 2-qubit depolarizing channel probability for device model""" + """Return 2-qubit depolarizing channel parameter for device model""" # Check trivial case where there is no gate error if error_param is None: return None @@ -367,7 +380,7 @@ def _depol_error_value_one_qubit(error_param, gate_time=0, t1=inf, t2=inf): else: return 0 - # Otherwise we calculate the depolarizing error probability to account + # Otherwise we calculate the depolarizing error parameter to account # for the difference between the relaxation error and gate error if t1 == inf: par1 = 1 @@ -377,8 +390,8 @@ def _depol_error_value_one_qubit(error_param, gate_time=0, t1=inf, t2=inf): par2 = 1 else: par2 = exp(-gate_time / t2) - p_depol = 1 + 3 * (2 * error_param - 1) / (par1 + 2 * par2) - return p_depol + depol_param = 1 + 3 * (2 * error_param - 1) / (par1 + 2 * par2) + return depol_param def _depol_error_value_two_qubit(error_param, @@ -387,7 +400,7 @@ def _depol_error_value_two_qubit(error_param, qubit0_t2=inf, qubit1_t1=inf, qubit1_t2=inf): - """Return 2-qubit depolarizing channel probability for device model""" + """Return 2-qubit depolarizing channel parameter for device model""" # Check trivial case where there is no gate error if error_param is None: return None @@ -435,5 +448,5 @@ def _depol_error_value_two_qubit(error_param, denom = ( q0_par1 + q1_par1 + q0_par1 * q1_par1 + 4 * q0_par2 * q1_par2 + 2 * (q0_par2 + q1_par2) + 2 * (q1_par1 * q0_par2 + q0_par1 * q1_par2)) - p_depol = 1 + 5 * (4 * error_param - 3) / denom - return p_depol + depol_param = 1 + 5 * (4 * error_param - 3) / denom + return depol_param diff --git a/qiskit/providers/aer/noise/errors/standard_errors.py b/qiskit/providers/aer/noise/errors/standard_errors.py index ea91bd4bd6..58ac60f8e1 100644 --- a/qiskit/providers/aer/noise/errors/standard_errors.py +++ b/qiskit/providers/aer/noise/errors/standard_errors.py @@ -267,12 +267,12 @@ def single_pauli(pauli): return error -def depolarizing_error(prob, num_qubits, standard_gates=True): +def depolarizing_error(param, num_qubits, standard_gates=True): """ Depolarizing quantum error channel. Args: - prob (double): completely depolarizing channel error probability. + param (double): depolarizing error parameter. num_qubits (int): the number of qubits for the error channel. standard_gates (bool): if True return the operators as standard qobj Pauli gate instructions. If false return as @@ -284,21 +284,32 @@ def depolarizing_error(prob, num_qubits, standard_gates=True): Raises: NoiseError: If noise parameters are invalid. - """ - if prob < 0 or prob > 1: - raise NoiseError( - "Depolarizing probability must be in between 0 and 1.") + Additional Information: + The depolarizing channel is defined as: + E(ρ) = (1 - λ) ρ + λ * (I / 2 ** n) + with 0 <= λ <= 4 ** n / (4 ** n - 1) + where λ is the depolarizing error param and n is the number of + qubits. + If λ = 0 this is the identity channel E(ρ) = ρ + If λ = 1 this is a completely depolarizing channel E(ρ) = I / 2 ** n + if λ = 4 ** n / (4 ** n - 1) this is a uniform Pauli error channel + E(ρ) = sum_j P_j ρ P_j / (4 ** n - 1) for all P_j != I. + """ if not isinstance(num_qubits, int) or num_qubits < 1: raise NoiseError("num_qubits must be a positive integer.") + # Check that the depolarizing parameter gives a valid CPTP + num_terms = 4**num_qubits + max_param = num_terms / (num_terms - 1) + if param < 0 or param > max_param: + raise NoiseError("Depolarizing parameter must be in between 0 " + "and {}.".format(max_param)) # Rescale completely depolarizing channel error probs # with the identity component removed - num_terms = 4**num_qubits # terms in completely depolarizing channel - prob_error = prob / num_terms - prob_iden = 1 - ( - num_terms - 1) * prob_error # subtract off non-identity terms - probs = [prob_iden] + (num_terms - 1) * [prob_error] + prob_iden = 1 - param / max_param + prob_pauli = param / num_terms + probs = [prob_iden] + (num_terms - 1) * [prob_pauli] # Generate pauli strings. The order doesn't matter as long # as the all identity string is first. paulis = [ From cfdc246db851537cea0130b3ab23476424fb887c Mon Sep 17 00:00:00 2001 From: kanejess <39967034+kanejess@users.noreply.github.com> Date: Tue, 25 Jun 2019 09:52:29 -0400 Subject: [PATCH 08/29] Update README.md Updated the links for Code of Conduct and Contribution Guidelines --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ff3df4dd6..d6b39bdc00 100755 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ print(sim_result.get_counts(qc)) ## Contribution Guidelines If you'd like to contribute to Qiskit, please take a look at our -[contribution guidelines](.github/CONTRIBUTING.md). This project adheres to Qiskit's [code of conduct](.github/CODE_OF_CONDUCT.md). By participating, you are expect to uphold to this code. +[contribution guidelines](https://github.com/Qiskit/qiskit-aer/blob/master/CONTRIBUTING.md). This project adheres to Qiskit's [code of conduct](https://github.com/Qiskit/qiskit-aer/blob/master/CODE_OF_CONDUCT.md). By participating, you are expect to uphold to this code. We use [GitHub issues](https://github.com/Qiskit/qiskit-aer/issues) for tracking requests and bugs. Please use our [slack](https://qiskit.slack.com) for discussion and simple questions. To join our Slack community use the [link](https://join.slack.com/t/qiskit/shared_invite/enQtNDc2NjUzMjE4Mzc0LTMwZmE0YTM4ZThiNGJmODkzN2Y2NTNlMDIwYWNjYzA2ZmM1YTRlZGQ3OGM0NjcwMjZkZGE0MTA4MGQ1ZTVmYzk). For questions that are more suited for a forum we use the Qiskit tag in the [Stack Exchange](https://quantumcomputing.stackexchange.com/questions/tagged/qiskit). From 5c1acd12f7d67302c9edbd31255cf37dfa85a169 Mon Sep 17 00:00:00 2001 From: kanejess <39967034+kanejess@users.noreply.github.com> Date: Tue, 25 Jun 2019 10:43:55 -0400 Subject: [PATCH 09/29] Update README.md Fixing one more broken link I missed on the first round! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d6b39bdc00..85ea1756c5 100755 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ pip install qiskit PIP will handle all dependencies automatically for us and you will always install the latest (and well-tested) version. -To install from source, follow the instructions in the [contribution guidelines](.github/CONTRIBUTING.md). +To install from source, follow the instructions in the [contribution guidelines](https://github.com/Qiskit/qiskit-aer/blob/master/CONTRIBUTING.md). ## Simulating your first quantum program with Qiskit Aer Now that you have Qiskit Aer installed, you can start simulating quantum circuits with noise. Here is a basic example: From 31e8b12804fa40a411e610578ead6a26736449d9 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:13:03 -0400 Subject: [PATCH 10/29] Set up CI with Azure Pipelines to replace appveyor This commit adds a yaml config to start using azure pipelines instead of using appveyor for our windows testing. For our purposes it's mostly the same except that azure is faster and provides much more capacity. [skip ci] --- azure-pipelines.yml | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000000..d7881a6e30 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,38 @@ +# Python package +# Create and test a Python package on multiple Python versions. +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- master +- stable + +pool: + vmImage: 'vs2017-win2016' +strategy: + matrix: + Python35: + python.version: '3.5' + Python36: + python.version: '3.6' + Python37: + python.version: '3.7' + +steps: + - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" + displayName: Add conda to PATH + - script: conda create --yes --quiet --name qiskit-aer + displayName: Create Anaconda environment + - script: | + call activate qiskit-aer + conda install --yes --quiet --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas +- script: | + call activate qiskit-aer + python -m pip install --disable-pip-version-check pip==18 + pip install cython + pip install git+https://github.com/Qiskit/qiskit-terra.git + pip install --ignore-installed -r requirements-dev.txt + python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" + pip install --find-links=dist\ qiskit_aer + python -m unittest discover -s test/terra -v + displayName: 'Install dependencies and Run Tests' \ No newline at end of file From 4e3ddb5f627666417741897be5d8c4588ae341e8 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:15:40 -0400 Subject: [PATCH 11/29] Update yaml syntax --- azure-pipelines.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d7881a6e30..f1c5108e7f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -24,8 +24,9 @@ steps: - script: conda create --yes --quiet --name qiskit-aer displayName: Create Anaconda environment - script: | - call activate qiskit-aer - conda install --yes --quiet --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas + call activate qiskit-aer + conda install --yes --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas + displayName: Install Anaconda Packages - script: | call activate qiskit-aer python -m pip install --disable-pip-version-check pip==18 @@ -35,4 +36,4 @@ steps: python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" pip install --find-links=dist\ qiskit_aer python -m unittest discover -s test/terra -v - displayName: 'Install dependencies and Run Tests' \ No newline at end of file + displayName: 'Install dependencies and Run Tests' From f19776a7b2cca34c6d2014d023086bf575944193 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:15:54 -0400 Subject: [PATCH 12/29] Remove appveyor --- appveyor.yml | 56 ---------------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index b11ce2c5a9..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,56 +0,0 @@ -image: Visual Studio 2017 -environment: - global: - # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the - # /E:ON and /V:ON options are not enabled in the batch script intepreter - # See: http://stackoverflow.com/a/13751649/163740 - CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd" - - matrix: - - PYTHON: "C:\\Miniconda37-x64" - PYTHON_VERSION: "3.7.0" - PYTHON_ARCH: "64" - - PYTHON: "C:\\Miniconda35-x64" - PYTHON_VERSION: "3.5.3" - PYTHON_ARCH: "64" - - PYTHON: "C:\\Miniconda36-x64" - PYTHON_VERSION: "3.6.1" - PYTHON_ARCH: "64" - - -install: - # If there is a newer build queued for the same PR, cancel this one. - # The AppVeyor 'rollout builds' option is supposed to serve the same - # purpose but it is problematic because it tends to cancel builds pushed - # directly to master instead of just PR builds (or the converse). - # credits: JuliaLang developers. - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } - # Prepend newly installed Python to the PATH of this build (this cannot be - # done from inside the powershell script as it would require to restart - # the parent CMD process). - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - # Check that we have the expected version and architecture for Python - - python --version - - # Because of issue #193, we have to keep pip in version 18. - - python -m pip install --disable-pip-version-check pip==18 - - pip install cython - - pip install https://github.com/Qiskit/qiskit-terra/archive/master.zip - # Due to https://github.com/ContinuumIO/anaconda-issues/issues/542 - # we need to pass --ignore-installed, otherwise Python3.5 will fail - # while upgrading setuptools. - - pip install --ignore-installed -r requirements-dev.txt - - python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" - - pip install --find-links=dist\ qiskit_aer - - -build: off - -test_script: - # Run the project tests - - python -m unittest discover -s test/terra -v - From 90d36167f9bc07a6691ae38acfd877c62ad57ff3 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:16:59 -0400 Subject: [PATCH 13/29] More yaml lint --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f1c5108e7f..519e876cd8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -27,7 +27,7 @@ steps: call activate qiskit-aer conda install --yes --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas displayName: Install Anaconda Packages -- script: | + - script: | call activate qiskit-aer python -m pip install --disable-pip-version-check pip==18 pip install cython From aaae9c7b81014f39a74b3e47d30e7e2762f084ed Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:17:47 -0400 Subject: [PATCH 14/29] Even more yaml lint Using a visual text editor in a web browser is much more difficult than vim. These sorts of issues are easily caught when editing with a real text editor. --- azure-pipelines.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 519e876cd8..c28e6fdb39 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -28,12 +28,12 @@ steps: conda install --yes --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas displayName: Install Anaconda Packages - script: | - call activate qiskit-aer - python -m pip install --disable-pip-version-check pip==18 - pip install cython - pip install git+https://github.com/Qiskit/qiskit-terra.git - pip install --ignore-installed -r requirements-dev.txt - python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" - pip install --find-links=dist\ qiskit_aer - python -m unittest discover -s test/terra -v - displayName: 'Install dependencies and Run Tests' + call activate qiskit-aer + python -m pip install --disable-pip-version-check pip==18 + pip install cython + pip install git+https://github.com/Qiskit/qiskit-terra.git + pip install --ignore-installed -r requirements-dev.txt + python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" + pip install --find-links=dist\ qiskit_aer + python -m unittest discover -s test/terra -v + displayName: 'Install dependencies and Run Tests' From b93da804e48218206377f0ef56498a9c8579f474 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:26:10 -0400 Subject: [PATCH 15/29] Remove conda package stage --- azure-pipelines.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c28e6fdb39..cfd22dadc5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,10 +23,6 @@ steps: displayName: Add conda to PATH - script: conda create --yes --quiet --name qiskit-aer displayName: Create Anaconda environment - - script: | - call activate qiskit-aer - conda install --yes --name qiskit-aer python=%PYTHON_VERSION% -c conda-forge openblas - displayName: Install Anaconda Packages - script: | call activate qiskit-aer python -m pip install --disable-pip-version-check pip==18 From 9b9b614304df14dadbfe254ee2a9eb74205ba1ad Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Tue, 25 Jun 2019 17:35:19 -0400 Subject: [PATCH 16/29] Re-add anaconda packages and install numpy --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index cfd22dadc5..92f3ad0e1f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -23,6 +23,10 @@ steps: displayName: Add conda to PATH - script: conda create --yes --quiet --name qiskit-aer displayName: Create Anaconda environment + - script: | + call activate qiskit-aer + conda install --yes --quiet --name qiskit-aer python=%PYTHON_VERSION% numpy + displayName: Install Anaconda packages - script: | call activate qiskit-aer python -m pip install --disable-pip-version-check pip==18 From 7fd42330cdbd0c8523ab0724c7c77764079c8419 Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Wed, 26 Jun 2019 11:05:49 +0200 Subject: [PATCH 17/29] Issue 232: Fix #elif preprocessor definitions --- src/base/controller.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 78f7ed2f16..0277acc316 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -26,7 +26,7 @@ #if defined(__linux__) || defined(__APPLE__) #include -#elif _WIN64 +#elif defined(_WIN64) // This is needed because windows.h redefine min()/max() so interferes with std::min/max #define NOMINMAX #include @@ -373,7 +373,7 @@ size_t Controller::get_system_memory_mb(void){ auto pages = sysconf(_SC_PHYS_PAGES); auto page_size = sysconf(_SC_PAGE_SIZE); total_physical_memory = pages * page_size; -#elif _WIN64 +#elif defined(_WIN64) MEMORYSTATUSEX status; status.dwLength = sizeof(status); GlobalMemoryStatusEx(&status); From 535fc8c5bd06655f6341f63952fcdc73d04ad759 Mon Sep 17 00:00:00 2001 From: hhorii Date: Tue, 2 Jul 2019 00:58:31 +0900 Subject: [PATCH 18/29] Debug thread management for shot-level and add explicit parallelization (#230) * add test to limit shot parallel * add explicit parallelization * make explicit thread management for debug. change the default max_parallel_experiments and max_parallel_shots --- src/base/controller.hpp | 85 +++++++++++++------ .../qasm_simulator/qasm_thread_management.py | 63 ++++++++++++++ 2 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 0277acc316..0385089cfd 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -98,11 +98,11 @@ namespace Base { * be used across all levels of parallelization. Set to 0 for maximum * available. [Default : 0] * - "max_parallel_experiments" (int): Set number of circuits that may be - * executed in parallel. Set to 0 to use the number of max parallel - * threads [Default: 1] + * executed in parallel. Set to 0 to automatically select a number of + * parallel threads. [Default: 0] * - "max_parallel_shots" (int): Set number of shots that maybe be executed - * in parallel for each circuit. Set to 0 to use the number of max - * parallel threads [Default: 1]. + * in parallel for each circuit. Set to 0 to automatically select a + * number of parallel threads. [Default: 0]. * - "max_memory_mb" (int): Sets the maximum size of memory for a store. * If a state needs more, an error is thrown. If set to 0, the maximum * will be automatically set to the system memory size [Default: 0]. @@ -241,10 +241,14 @@ class Controller { int max_parallel_shots_; size_t max_memory_mb_; + // use explicit parallelization + bool explicit_parallelization_; + // Parameters for parallelization management for experiments int parallel_experiments_; int parallel_shots_; int parallel_state_update_; + }; @@ -265,15 +269,14 @@ void Controller::set_config(const json_t &config) { noise_model_ = Noise::NoiseModel(config["noise_model"]); // Load OpenMP maximum thread settings - JSON::get_value(max_parallel_threads_, "max_parallel_threads", config); - JSON::get_value(max_parallel_shots_, "max_parallel_shots", config); - JSON::get_value(max_parallel_experiments_, "max_parallel_experiments", config); - - // Prevent using both parallel circuits and parallel shots - // with preference given to parallel circuit execution - if (max_parallel_experiments_ > 1) - max_parallel_shots_ = 1; - + if (JSON::check_key("max_parallel_threads", config)) + JSON::get_value(max_parallel_threads_, "max_parallel_threads", config); + + // Load configurations for parallelization + if (JSON::check_key("max_parallel_experiments", config)) + JSON::get_value(max_parallel_experiments_, "max_parallel_experiments", config); + if (JSON::check_key("max_parallel_shots", config)) + JSON::get_value(max_parallel_shots_, "max_parallel_shots", config); if (JSON::check_key("max_memory_mb", config)) { JSON::get_value(max_memory_mb_, "max_memory_mb", config); } else { @@ -284,6 +287,30 @@ void Controller::set_config(const json_t &config) { for (std::shared_ptr opt: optimizations_) opt->set_config(config_); + // for debugging + if (JSON::check_key("_parallel_experiments", config)) { + JSON::get_value(parallel_experiments_, "_parallel_experiments", config); + explicit_parallelization_ = true; + } + + // for debugging + if (JSON::check_key("_parallel_shots", config)) { + JSON::get_value(parallel_shots_, "_parallel_shots", config); + explicit_parallelization_ = true; + } + + // for debugging + if (JSON::check_key("_parallel_state_update", config)) { + JSON::get_value(parallel_state_update_, "_parallel_state_update", config); + explicit_parallelization_ = true; + } + + if (explicit_parallelization_) { + parallel_experiments_ = std::max( { parallel_experiments_, 1 }); + parallel_shots_ = std::max( { parallel_shots_, 1 }); + parallel_state_update_ = std::max( { parallel_state_update_, 1 }); + } + std::string path; JSON::get_value(path, "library_dir", config); // Fix for MacOS and OpenMP library double initialization crash. @@ -299,12 +326,14 @@ void Controller::clear_config() { void Controller::clear_parallelization() { max_parallel_threads_ = 0; - max_parallel_experiments_ = 1; - max_parallel_shots_ = 1; + max_parallel_experiments_ = 0; + max_parallel_shots_ = 0; parallel_experiments_ = 1; parallel_shots_ = 1; parallel_state_update_ = 1; + + explicit_parallelization_ = false; } void Controller::set_parallelization_experiments(const std::vector& circuits) { @@ -334,6 +363,7 @@ void Controller::set_parallelization_experiments(const std::vector& cir max_parallel_experiments_, max_parallel_threads_, static_cast(circuits.size()) }); + max_parallel_shots_ = 1; } } @@ -347,22 +377,26 @@ void Controller::set_parallelization_circuit(const Circuit& circ) { if (max_memory_mb_ < circ_memory_mb) throw std::runtime_error("a circuit requires more memory than max_memory_mb."); - if (circ_memory_mb == 0) + if (circ_memory_mb == 0) { parallel_shots_ = max_parallel_threads_; - else + parallel_state_update_ = 1; + } else if (max_parallel_shots_ > 0) { + parallel_shots_ = std::min ({ static_cast(max_memory_mb_ / circ_memory_mb), + max_parallel_shots_, + static_cast(circ.shots) }); + parallel_state_update_ = max_parallel_threads_ / parallel_shots_; + } else { + // try to use all the threads for shot-level parallelization + // no nested parallelization if max_parallel_shots is not configured parallel_shots_ = std::min ({ static_cast(max_memory_mb_ / circ_memory_mb), max_parallel_threads_, static_cast(circ.shots) }); - - if (max_parallel_shots_ < 1) { // no nested parallelization if max_parallel_shots is not configured if (parallel_shots_ == max_parallel_threads_) { parallel_state_update_ = 1; } else { parallel_shots_ = 1; parallel_state_update_ = max_parallel_threads_; } - } else { - parallel_state_update_ = max_parallel_threads_ / parallel_shots_; } } @@ -507,8 +541,10 @@ json_t Controller::execute(const json_t &qobj_js) { max_parallel_threads_ = 1; #endif - // set parallelization for experiments - set_parallelization_experiments(qobj.circuits); + if (!explicit_parallelization_) { + // set parallelization for experiments + set_parallelization_experiments(qobj.circuits); + } #ifdef _OPENMP result["metadata"]["omp_enabled"] = true; @@ -575,8 +611,9 @@ json_t Controller::execute_circuit(Circuit &circ) { // for individual circuit failures. try { // set parallelization for this circuit - if (parallel_experiments_ == 1) + if (!explicit_parallelization_ && parallel_experiments_ == 1) { set_parallelization_circuit(circ); + } // Single shot thread execution if (parallel_shots_ <= 1) { result["data"] = run_circuit(circ, circ.shots, circ.seed); diff --git a/test/terra/backends/qasm_simulator/qasm_thread_management.py b/test/terra/backends/qasm_simulator/qasm_thread_management.py index 0173b15793..aac796db87 100644 --- a/test/terra/backends/qasm_simulator/qasm_thread_management.py +++ b/test/terra/backends/qasm_simulator/qasm_thread_management.py @@ -311,3 +311,66 @@ def test_qasm_auto_disable_shot_parallelization_with_memory_shortage(self): multiprocessing.cpu_count(), msg="parallel_state_update should be " + str( multiprocessing.cpu_count())) + + + def test_qasm_auto_disable_shot_parallelization_with_max_parallel_shots(self): + """test disabling parallel shots because max_parallel_shots is 1""" + # Test circuit + shots = multiprocessing.cpu_count() + circuit = quantum_volume_circuit(16, 1, measure=True, seed=0) + + backend_opts = self.BACKEND_OPTS.copy() + backend_opts['max_parallel_shots'] = 1 + backend_opts['noise_model'] = self.dummy_noise_model() + + result = execute( + circuit, self.SIMULATOR, shots=shots, + backend_options=backend_opts).result() + if result.metadata['omp_enabled']: + self.assertEqual( + result.metadata['parallel_experiments'], + 1, + msg="parallel_experiments should be 1") + self.assertEqual( + result.to_dict()['results'][0]['metadata']['parallel_shots'], + 1, + msg="parallel_shots must be 1") + self.assertEqual( + result.to_dict()['results'][0]['metadata'] + ['parallel_state_update'], + multiprocessing.cpu_count(), + msg="parallel_state_update should be " + str( + multiprocessing.cpu_count())) + + + def _test_qasm_explicit_parallelization(self): + """test disabling parallel shots because max_parallel_shots is 1""" + # Test circuit + shots = multiprocessing.cpu_count() + circuit = quantum_volume_circuit(16, 1, measure=True, seed=0) + + backend_opts = self.BACKEND_OPTS.copy() + backend_opts['max_parallel_shots'] = 1 + backend_opts['max_parallel_experiments'] = 1 + backend_opts['noise_model'] = self.dummy_noise_model() + backend_opts['_parallel_experiments'] = 2 + backend_opts['_parallel_shots'] = 3 + backend_opts['_parallel_state_update'] = 4 + + result = execute( + circuit, self.SIMULATOR, shots=shots, + backend_options=backend_opts).result() + if result.metadata['omp_enabled']: + self.assertEqual( + result.metadata['parallel_experiments'], + 2, + msg="parallel_experiments should be 2") + self.assertEqual( + result.to_dict()['results'][0]['metadata']['parallel_shots'], + 3, + msg="parallel_shots must be 3") + self.assertEqual( + result.to_dict()['results'][0]['metadata'] + ['parallel_state_update'], + 4, + msg="parallel_state_update should be 4") From f2bd261ace1b35cc96531e1ad01981ada569c030 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Tue, 2 Jul 2019 04:21:36 +0200 Subject: [PATCH 19/29] Add to_instruction to ReadoutError (#257) --- CHANGELOG.md | 1 + .../providers/aer/backends/qasm_simulator.py | 4 ++-- .../aer/noise/errors/readout_error.py | 5 +++++ src/framework/operations.hpp | 7 ++++--- test/terra/noise/test_readout_error.py | 18 ++++++++++++++++++ 5 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a118392059..a0ee2532db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Fixed Added ----- +- Added `to_instruction` method to `ReadoutError` (\#257). Changed ------- diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index 64deb1ed1f..7fdef0a38a 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -159,7 +159,7 @@ class QasmSimulator(AerBackend): 'basis_gates': [ 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot', 'unitary', 'reset', - 'initialize', 'kraus' + 'initialize', 'kraus', 'roerror' ], 'gates': [{ 'name': 'TODO', @@ -185,7 +185,7 @@ def _validate(self, qobj, backend_options, noise_model): """ clifford_instructions = [ "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", - "barrier", "reset", "measure" + "barrier", "reset", "measure", 'roerror' ] unsupported_ch_instructions = ["u2", "u3"] # Check if noise model is Clifford: diff --git a/qiskit/providers/aer/noise/errors/readout_error.py b/qiskit/providers/aer/noise/errors/readout_error.py index 4ca37372b0..e98ae17337 100644 --- a/qiskit/providers/aer/noise/errors/readout_error.py +++ b/qiskit/providers/aer/noise/errors/readout_error.py @@ -18,6 +18,7 @@ import numpy as np from numpy.linalg import norm +from qiskit.circuit import Instruction from qiskit.quantum_info.operators.predicates import ATOL_DEFAULT, RTOL_DEFAULT from ..noiseerror import NoiseError @@ -141,6 +142,10 @@ def ideal(self): return True return False + def to_instruction(self): + """Convet the ReadoutError to a circuit Instruction.""" + return Instruction("roerror", 0, self.number_of_qubits, self._probabilities) + def as_dict(self): """ DEPRECATED: Use to_dict() diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 2c34704a3d..b3f820f160 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -105,7 +105,7 @@ struct Op { uint_t conditional_reg; // (opt) the (single) register location to look up for conditional RegComparison bfunc; // (opt) boolean function relation - // DEPRECATED: old style conditionals (will be removed when Terra supports new style) + // DEPRECATED: Old style conditionals (remove in 0.3) bool old_conditional = false; // is gate old style conditional gate std::string old_conditional_mask; // hex string for conditional mask std::string old_conditional_val; // hex string for conditional value @@ -634,7 +634,7 @@ Op json_to_op_gate(const json_t &js) { op.conditional_reg = js["conditional"]; op.conditional = true; } else { - // DEPRECIATED: old style conditional + // DEPRECATED: old style conditional (remove in 0.3) JSON::get_value(op.old_conditional_mask, "mask", js["conditional"]); JSON::get_value(op.old_conditional_val, "val", js["conditional"]); op.old_conditional = true; @@ -771,7 +771,8 @@ Op json_to_op_roerror(const json_t &js) { op.name = "roerror"; JSON::get_value(op.memory, "memory", js); JSON::get_value(op.registers, "register", js); - JSON::get_value(op.probs, "probabilities", js); + JSON::get_value(op.probs, "probabilities", js); // DEPRECATED: Remove in 0.4 + JSON::get_value(op.probs, "params", js); return op; } diff --git a/test/terra/noise/test_readout_error.py b/test/terra/noise/test_readout_error.py index 49443ef90d..a9e33171e3 100644 --- a/test/terra/noise/test_readout_error.py +++ b/test/terra/noise/test_readout_error.py @@ -176,6 +176,24 @@ def test_equal(self): error2 = ReadoutError(np.array([[0.9, 0.1], [0.5, 0.5]])) self.assertEqual(error1, error2) + def test_to_instruction(self): + """Test conversion of ReadoutError to Instruction.""" + # 1-qubit case + probs1 = [[0.8, 0.2], [0.5, 0.5]] + instr1 = ReadoutError(probs1).to_instruction() + self.assertEqual(instr1.name, "roerror") + self.assertEqual(instr1.num_clbits, 1) + self.assertEqual(instr1.num_qubits, 0) + self.assertTrue(np.allclose(instr1.params, probs1)) + + # 2-qubit case + probs2 = np.kron(probs1, probs1) + instr2 = ReadoutError(probs2).to_instruction() + self.assertEqual(instr2.name, "roerror") + self.assertEqual(instr2.num_clbits, 2) + self.assertEqual(instr2.num_qubits, 0) + self.assertTrue(np.allclose(instr2.params, probs2)) + if __name__ == '__main__': unittest.main() From 4806447284f23f1a14bfe19e044c6ea0ed4ba96e Mon Sep 17 00:00:00 2001 From: hitomi Date: Tue, 2 Jul 2019 15:58:03 +0900 Subject: [PATCH 20/29] Set max_workers to ThreadPoolExecutor --- CHANGELOG.md | 1 + qiskit/providers/aer/aerjob.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0ee2532db..800d7cc7fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ Fixed with no identity component. (\#243) - Fixed 2-qubit depolarizing-only error parameter calculation in basic_device_noise_model (\#243) +- Set maximum workers to ThreadPoolExecutor in AerJob to limit thread creation (\#259) [0.2.1](https://github.com/Qiskit/qiskit-aer/compare/0.2.0...0.2.1) - 2019-05-20 ================================================================================ diff --git a/qiskit/providers/aer/aerjob.py b/qiskit/providers/aer/aerjob.py index 8258132a74..cc43432505 100644 --- a/qiskit/providers/aer/aerjob.py +++ b/qiskit/providers/aer/aerjob.py @@ -50,7 +50,7 @@ class AerJob(BaseJob): _executor (futures.Executor): executor to handle asynchronous jobs """ - _executor = futures.ThreadPoolExecutor() + _executor = futures.ThreadPoolExecutor(max_workers=1) def __init__(self, backend, job_id, fn, qobj, *args): super().__init__(backend, job_id) From 3d4106ce25e1804180388d6f2ba0f066ae7a2c69 Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Wed, 3 Jul 2019 12:12:23 -0400 Subject: [PATCH 21/29] Avoid reuse in azure pipelines and split build out (#260) * Avoid reuse in azure pipelines job configuration In the azure pipelines runs for #258 the jobs are failing for what looks like a mismatch with the installed aer python package and what should have been built. Just to rule that out for runs this commit makes 2 changes. First it creates a conda environment/virtualenv with a unique name for each run. Secondly it runs git clean to remove any old built wheels (assuming there are any). Hopefully this will provide more reliability in future runs. --- azure-pipelines.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 92f3ad0e1f..b004a74a58 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -21,19 +21,25 @@ strategy: steps: - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" displayName: Add conda to PATH - - script: conda create --yes --quiet --name qiskit-aer + - script: conda create --yes --quiet --name qiskit-aer-$(Build.BuildNumber) displayName: Create Anaconda environment - script: | - call activate qiskit-aer - conda install --yes --quiet --name qiskit-aer python=%PYTHON_VERSION% numpy + call activate qiskit-aer-$(Build.BuildNumber) + conda install --yes --quiet --name qiskit-aer-$(Build.BuildNumber) python=%PYTHON_VERSION% numpy displayName: Install Anaconda packages - - script: | - call activate qiskit-aer + - bash: | + set -e + source activate qiskit-aer-$(Build.BuildNumber) + git clean -fdX python -m pip install --disable-pip-version-check pip==18 pip install cython pip install git+https://github.com/Qiskit/qiskit-terra.git pip install --ignore-installed -r requirements-dev.txt python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" - pip install --find-links=dist\ qiskit_aer + displayName: 'Install Dependencies and Build Aer' + - bash: | + set -e + source activate qiskit-aer-$(Build.BuildNumber) + pip install dist/qiskit_aer*.whl python -m unittest discover -s test/terra -v - displayName: 'Install dependencies and Run Tests' + displayName: 'Install Aer and Run Tests' From a5e3aa265ccd0f1b3fafadbe0207c185a6a30406 Mon Sep 17 00:00:00 2001 From: hhorii Date: Thu, 4 Jul 2019 02:42:05 +0900 Subject: [PATCH 22/29] Refactor and improve fusion (#255) * refactor fusion by deleting matrix_sequence and fix bugs in mcu in qubitvector. * CH supports nop * specialize u1 and cu1 fusion * refactor fusion-related codes * remove nop operation type --- src/base/controller.hpp | 12 +- src/framework/operations.hpp | 30 +- src/framework/utils.hpp | 11 +- src/simulators/qasm/qasm_controller.hpp | 6 +- src/simulators/statevector/qubitvector.hpp | 260 +------------- .../statevector/statevector_state.hpp | 45 +-- src/transpile/basic_opts.hpp | 20 +- src/transpile/fusion.hpp | 327 +++++++++++++----- src/transpile/truncate_qubits.hpp | 23 +- 9 files changed, 342 insertions(+), 392 deletions(-) diff --git a/src/base/controller.hpp b/src/base/controller.hpp index 0385089cfd..f78a3de8c1 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -194,7 +194,7 @@ class Controller { // Generate an equivalent circuit with input_circ as output_circ. template - Circuit optimize_circuit(const Circuit &input_circ, + void optimize_circuit(Circuit &input_circ, state_t& state, OutputData &data) const; @@ -477,20 +477,18 @@ bool Controller::validate_memory_requirements(state_t &state, // Circuit optimization //------------------------------------------------------------------------- template -Circuit Controller::optimize_circuit(const Circuit &input_circ, +void Controller::optimize_circuit(Circuit &input_circ, state_t& state, OutputData &data) const { - Circuit working_circ = input_circ; Operations::OpSet allowed_opset; allowed_opset.optypes = state.allowed_ops(); allowed_opset.gates = state.allowed_gates(); allowed_opset.snapshots = state.allowed_snapshots(); - for (std::shared_ptr opt: optimizations_) - opt->optimize_circuit(working_circ, allowed_opset, data); - - return working_circ; + for (std::shared_ptr opt: optimizations_) { + opt->optimize_circuit(input_circ, allowed_opset, data); + } } //------------------------------------------------------------------------- diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index b3f820f160..290f197d51 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -36,7 +36,7 @@ enum class RegComparison {Equal, NotEqual, Less, LessEqual, Greater, GreaterEqua // Enum class for operation types enum class OpType { gate, measure, reset, bfunc, barrier, snapshot, - matrix, matrix_sequence, multiplexer, kraus, roerror, noise_switch, initialize + matrix, multiplexer, kraus, roerror, noise_switch, initialize }; inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { @@ -62,9 +62,6 @@ inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { case OpType::matrix: stream << "matrix"; break; - case OpType::matrix_sequence: - stream << "matrix_sequence"; - break; case OpType::multiplexer: stream << "multiplexer"; break; @@ -138,6 +135,20 @@ inline std::ostream& operator<<(std::ostream& s, const Op& op) { s << qubit; first = false; } + s << "],["; + first = true; + for (reg_t reg: op.regs) { + if (!first) s << ","; + s << "["; + bool first0 = true; + for (size_t qubit: reg) { + if (!first0) s << ","; + s << qubit; + first0 = false; + } + s << "]"; + first = false; + } s << "]"; return s; } @@ -390,14 +401,15 @@ inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, std::string la return op; } -inline Op make_matrix_sequence(const std::vector ®s, const std::vector &mats, std::string label = "") { +inline Op make_fusion(const reg_t &qubits, const cmatrix_t &mat, const std::vector& fusioned_ops, std::string label = "") { Op op; - op.type = OpType::matrix_sequence; - op.name = "matrix_sequence"; - op.regs = regs; - op.mats = mats; + op.type = OpType::matrix; + op.name = "fusion"; + op.qubits = qubits; + op.mats = {mat}; if (label != "") op.string_params = {label}; + return op; } diff --git a/src/framework/utils.hpp b/src/framework/utils.hpp index 4a12b5c816..fc3320ea46 100755 --- a/src/framework/utils.hpp +++ b/src/framework/utils.hpp @@ -92,6 +92,7 @@ class Matrix { template matrix make_matrix(const std::vector> &mat); template matrix devectorize_matrix(const std::vector &vec); template std::vector vectorize_matrix(const matrix &mat); +template std::vector vectorize_diagonal_matrix(const matrix& mat); // Transformations template matrix transpose(const matrix &A); @@ -496,7 +497,6 @@ matrix devectorize_matrix(const std::vector& vec) { return mat; } - template std::vector vectorize_matrix(const matrix& mat) { std::vector vec; @@ -510,6 +510,15 @@ std::vector vectorize_matrix(const matrix& mat) { return vec; } +template +std::vector vectorize_diagonal_matrix(const matrix& mat) { + std::vector vec; + size_t size = std::min(mat.GetRows(), mat.GetColumns()); + vec.resize(size, 0.); + for (size_t i=0; i < size; i++) + vec[i] = mat(i, i); + return vec; +} template matrix make_matrix(const std::vector> & mat) { diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 61d0e7f2fc..48e5dbeede 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -251,7 +251,7 @@ class QasmController : public Base::Controller { // Constructor //------------------------------------------------------------------------- QasmController::QasmController() { - add_circuit_optimization(Transpile::ReduceNop()); + add_circuit_optimization(Transpile::ReduceBarrier()); add_circuit_optimization(Transpile::Fusion()); add_circuit_optimization(Transpile::TruncateQubits()); } @@ -501,7 +501,7 @@ void QasmController::run_circuit_with_noise(const Circuit &circ, while(shots-- > 0) { Circuit noise_circ = noise_model_.sample_noise(circ, rng); if (noise_circ.num_qubits > circuit_opt_noise_threshold_) { - noise_circ = optimize_circuit(noise_circ, state, data); + optimize_circuit(noise_circ, state, data); } run_single_shot(noise_circ, state, initial_state, data, rng); } @@ -518,7 +518,7 @@ void QasmController::run_circuit_without_noise(const Circuit &circ, // Optimize circuit for state type Circuit opt_circ = circ; if (circ.num_qubits > circuit_opt_ideal_threshold_) { - opt_circ = optimize_circuit(opt_circ, state, data); + optimize_circuit(opt_circ, state, data); } // Check if measure sampler and optimization are valid diff --git a/src/simulators/statevector/qubitvector.hpp b/src/simulators/statevector/qubitvector.hpp index 15c6462bbe..87f90053da 100755 --- a/src/simulators/statevector/qubitvector.hpp +++ b/src/simulators/statevector/qubitvector.hpp @@ -245,11 +245,6 @@ class QubitVector { // The matrix is input as vector of the column-major vectorized N-qubit matrix. void apply_matrix(const reg_t &qubits, const cvector_t &mat); - // Apply a N-qubit matrix constructed from composition of 1 and 2 qubit matrices. - // The sets of qubits and matrices are passed as vectors, where each individual matrix - // is input as a column-major vectorized matrix. - void apply_matrix_sequence(const std::vector ®s, const std::vector &mats); - // Apply a stacked set of 2^control_count target_count--qubit matrix to the state vector. // The matrix is input as vector of the column-major vectorized N-qubit matrix. void apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, const cvector_t &mat); @@ -528,21 +523,6 @@ class QubitVector { complex_t apply_reduction_lambda(Lambda&& func, const list_t &qubits, const param_t ¶ms) const; - - // Permute an N-qubit vectorized matrix to match a reordering of qubits - cvector_t sort_matrix(const reg_t &src, - const reg_t &sorted, - const cvector_t &mat) const; - - // Swap cols and rows of vectorized matrix - void swap_cols_and_rows(const uint_t idx1, const uint_t idx2, - cvector_t &mat, uint_t dim) const; - - // Extend a matrix for qubits (src_qubits) to a matrix for more qubits (dst_sorted_qubits) - // qubits in dst_sorted_qubits must contains all the qubits in src_qubits - cvector_t expand_matrix(const reg_t& src_qubits, - const reg_t& dst_sorted_qubits, - const cvector_t& vmat) const; }; /******************************************************************************* @@ -1090,7 +1070,7 @@ complex_t QubitVector::apply_reduction_lambda(Lambda&& func, template void QubitVector::apply_matrix(const reg_t &qubits, const cvector_t &mat) { - + const size_t N = qubits.size(); // Error checking #ifdef DEBUG @@ -1173,69 +1153,6 @@ void QubitVector::apply_matrix(const reg_t &qubits, } // end switch } -template -void QubitVector::apply_matrix_sequence(const std::vector ®s, - const std::vector &mats) { - if (mats.size() == 0) - return; - - -#ifdef DEBUG - if (regs.size() != mats.size()); - throw std::runtime_error("QubitVector::apply_matrix_sequence allows same size of qubitss and mats."); -#endif - - bool at_most_two = true; - // check 1 or 2 qubits - for (const reg_t& reg: regs) { - if (reg.size() > 2) { - at_most_two = false; - break; - } - } - - if (!at_most_two) { - for (size_t i = 0; i < regs.size(); ++i) - apply_matrix(regs[i], mats[i]); - return; - } - - - reg_t sorted_qubits; - for (const reg_t& reg: regs) - for (const uint_t qubit: reg) - if (std::find(sorted_qubits.begin(), sorted_qubits.end(), qubit) == sorted_qubits.end()) - sorted_qubits.push_back(qubit); - - std::sort(sorted_qubits.begin(), sorted_qubits.end()); - - std::vector sorted_mats; - - for (size_t i = 0; i < regs.size(); ++i) { - const reg_t& reg = regs[i]; - const cvector_t& mat = mats[i]; - sorted_mats.push_back(expand_matrix(reg, sorted_qubits, mat)); - } - - auto U = sorted_mats[0]; - const auto dim = BITS[sorted_qubits.size()]; - - for (size_t m = 1; m < sorted_mats.size(); m++) { - - cvector_t u_tmp(U.size(), 0.); - const cvector_t& u = sorted_mats[m]; - - for (size_t i = 0; i < dim; ++i) - for (size_t j = 0; j < dim; ++j) - for (size_t k = 0; k < dim; ++k) - u_tmp[i + j * dim] += u[i + k * dim] * U[k + j * dim]; - - U = u_tmp; - } - - apply_matrix(sorted_qubits, U); -} - template void QubitVector::apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, @@ -1597,8 +1514,8 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for CU gate auto lambda = [&](const areg_t<4> &inds, const cvector_t &_diag)->void { - data_[pos0] = _diag[0] * data_[pos0]; - data_[pos1] = _diag[1] * data_[pos1]; + data_[inds[pos0]] = _diag[0] * data_[inds[pos0]]; + data_[inds[pos1]] = _diag[1] * data_[inds[pos1]]; }; apply_lambda(lambda, areg_t<2>({{qubits[0], qubits[1]}}), diag); return; @@ -1607,8 +1524,8 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for CCU gate auto lambda = [&](const areg_t<8> &inds, const cvector_t &_diag)->void { - data_[pos0] = _diag[0] * data_[pos0]; - data_[pos1] = _diag[1] * data_[pos1]; + data_[inds[pos0]] = _diag[0] * data_[inds[pos0]]; + data_[inds[pos1]] = _diag[1] * data_[inds[pos1]]; }; apply_lambda(lambda, areg_t<3>({{qubits[0], qubits[1], qubits[2]}}), diag); return; @@ -1617,8 +1534,8 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for general multi-controlled U gate auto lambda = [&](const indexes_t &inds, const cvector_t &_diag)->void { - data_[pos0] = _diag[0] * data_[pos0]; - data_[pos1] = _diag[1] * data_[pos1]; + data_[inds[pos0]] = _diag[0] * data_[inds[pos0]]; + data_[inds[pos1]] = _diag[1] * data_[inds[pos1]]; }; apply_lambda(lambda, qubits, diag); return; @@ -1637,9 +1554,9 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for CU gate auto lambda = [&](const areg_t<4> &inds, const cvector_t &_mat)->void { - const auto cache = data_[pos0]; - data_[pos0] = _mat[0] * data_[pos0] + _mat[2] * data_[pos1]; - data_[pos1] = _mat[1] * cache + _mat[3] * data_[pos1]; + const auto cache = data_[inds[pos0]]; + data_[inds[pos0]] = _mat[0] * data_[inds[pos0]] + _mat[2] * data_[inds[pos1]]; + data_[inds[pos1]] = _mat[1] * cache + _mat[3] * data_[inds[pos1]]; }; apply_lambda(lambda, areg_t<2>({{qubits[0], qubits[1]}}), mat); return; @@ -1648,9 +1565,9 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for CCU gate auto lambda = [&](const areg_t<8> &inds, const cvector_t &_mat)->void { - const auto cache = data_[pos0]; - data_[pos0] = _mat[0] * data_[pos0] + _mat[2] * data_[pos1]; - data_[pos1] = _mat[1] * cache + _mat[3] * data_[pos1]; + const auto cache = data_[inds[pos0]]; + data_[inds[pos0]] = _mat[0] * data_[inds[pos0]] + _mat[2] * data_[inds[pos1]]; + data_[inds[pos1]] = _mat[1] * cache + _mat[3] * data_[inds[pos1]]; }; apply_lambda(lambda, areg_t<3>({{qubits[0], qubits[1], qubits[2]}}), mat); return; @@ -1659,9 +1576,9 @@ void QubitVector::apply_mcu(const reg_t &qubits, // Lambda function for general multi-controlled U gate auto lambda = [&](const indexes_t &inds, const cvector_t &_mat)->void { - const auto cache = data_[pos0]; - data_[pos0] = _mat[0] * data_[pos0] + _mat[2] * data_[pos1]; - data_[pos1] = _mat[1] * cache + _mat[3] * data_[pos1]; + const auto cache = data_[inds[pos0]]; + data_[inds[pos0]] = _mat[0] * data_[inds[pos0]] + _mat[2] * data_[inds[pos1]]; + data_[inds[pos1]] = _mat[1] * cache + _mat[3] * data_[inds[pos1]]; }; apply_lambda(lambda, qubits, mat); return; @@ -1801,151 +1718,6 @@ void QubitVector::apply_diagonal_matrix(const uint_t qubit, } } -//------------------------------------------------------------------------------ -// Gate-swap optimized helper functions -//------------------------------------------------------------------------------ -template -void QubitVector::swap_cols_and_rows(const uint_t idx1, - const uint_t idx2, - cvector_t &mat, - uint_t dim) const { - - uint_t mask1 = BITS[idx1]; - uint_t mask2 = BITS[idx2]; - - for (uint_t first = 0; first < dim; ++first) { - if ((first & mask1) && !(first & mask2)) { - uint_t second = (first ^ mask1) | mask2; - - for (uint_t i = 0; i < dim; ++i) { - complex_t cache = mat[first * dim + i]; - mat[first * dim + i] = mat[second * dim + i]; - mat[second * dim + i] = cache; - } - for (uint_t i = 0; i < dim; ++i) { - complex_t cache = mat[i * dim + first]; - mat[i * dim + first] = mat[i * dim + second]; - mat[i * dim + second] = cache; - } - } - } -} - -template -cvector_t QubitVector::sort_matrix(const reg_t& src, - const reg_t& sorted, - const cvector_t &mat) const { - - const uint_t N = src.size(); - const uint_t DIM = BITS[N]; - auto ret = mat; - auto current = src; - - while (current != sorted) { - uint_t from; - uint_t to; - for (from = 0; from < current.size(); ++from) - if (current[from] != sorted[from]) - break; - if (from == current.size()) - break; - for (to = from + 1; to < current.size(); ++to) - if (current[from] == sorted[to]) - break; - if (to == current.size()) { - throw std::runtime_error("QubitVector::sort_matrix we should not reach here"); - } - swap_cols_and_rows(from, to, ret, DIM); - std::swap(current[from], current[to]); - } - - return ret; -} - -template -cvector_t QubitVector::expand_matrix(const reg_t& src_qubits, const reg_t& dst_sorted_qubits, const cvector_t& vmat) const { - - const auto dst_dim = BITS[dst_sorted_qubits.size()]; - const auto dst_vmat_size = dst_dim * dst_dim; - const auto src_dim = BITS[src_qubits.size()]; - - // generate a matrix for op - cvector_t u(dst_vmat_size, .0); - std::vector filled(dst_dim, false); - - if (src_qubits.size() == 1) { //1-qubit operation - // 1. identify delta - const auto index = std::distance(dst_sorted_qubits.begin(), - std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), src_qubits[0])); - - const auto delta = BITS[index]; - - // 2. find vmat(0, 0) position in U - for (uint_t i = 0; i < dst_dim; ++i) { - - if (filled[i]) - continue; - - // 3. allocate op.u to u based on u(i, i) and delta - u[i + (i + 0) * dst_dim] = vmat[0 + 0 * src_dim]; - u[i + (i + delta) * dst_dim] = vmat[0 + 1 * src_dim]; - u[(i + delta) + (i + 0) * dst_dim] = vmat[1 + 0 * src_dim]; - u[(i + delta) + (i + delta) * dst_dim] = vmat[1 + 1 * src_dim]; - filled[i] = filled[i + delta] = true; - } - } else if (src_qubits.size() == 2) { //2-qubit operation - - reg_t sorted_src_qubits = src_qubits; - std::sort(sorted_src_qubits.begin(), sorted_src_qubits.end()); - const cvector_t sorted_vmat = sort_matrix(src_qubits, sorted_src_qubits, vmat); - - // 1. identify low and high delta - auto low = std::distance(dst_sorted_qubits.begin(), - std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[0])); - - auto high = std::distance(dst_sorted_qubits.begin(), - std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[1])); - - auto low_delta = BITS[low]; - auto high_delta = BITS[high]; - - // 2. find op.u(0, 0) position in U - for (uint_t i = 0; i < dst_dim; ++i) { - if (filled[i]) - continue; - - // 3. allocate vmat to u based on u(i, i) and delta - u[i + (i + 0) * dst_dim] = sorted_vmat[0 + 0 * src_dim]; - u[i + (i + low_delta) * dst_dim] = sorted_vmat[0 + 1 * src_dim]; - u[i + (i + high_delta) * dst_dim] = sorted_vmat[0 + 2 * src_dim]; - u[i + (i + low_delta + high_delta) * dst_dim] = sorted_vmat[0 + 3 * src_dim]; - u[(i + low_delta) + (i + 0) * dst_dim] = sorted_vmat[1 + 0 * src_dim]; - u[(i + low_delta) + (i + low_delta) * dst_dim] = sorted_vmat[1 + 1 * src_dim]; - u[(i + low_delta) + (i + high_delta) * dst_dim] = sorted_vmat[1 + 2 * src_dim]; - u[(i + low_delta) + (i + low_delta + high_delta) * dst_dim] = sorted_vmat[1 + 3 * src_dim]; - u[(i + high_delta) + (i + 0) * dst_dim] = sorted_vmat[2 + 0 * src_dim]; - u[(i + high_delta) + (i + low_delta) * dst_dim] = sorted_vmat[2 + 1 * src_dim]; - u[(i + high_delta) + (i + high_delta) * dst_dim] = sorted_vmat[2 + 2 * src_dim]; - u[(i + high_delta) + (i + low_delta + high_delta) * dst_dim] = sorted_vmat[2 + 3 * src_dim]; - u[(i + low_delta + high_delta) + (i + 0) * dst_dim] = sorted_vmat[3 + 0 * src_dim]; - u[(i + low_delta + high_delta) + (i + low_delta) * dst_dim] = sorted_vmat[3 + 1 * src_dim]; - u[(i + low_delta + high_delta) + (i + high_delta) * dst_dim] = sorted_vmat[3 + 2 * src_dim]; - u[(i + low_delta + high_delta) + (i + low_delta + high_delta) * dst_dim] = sorted_vmat[3 + 3 * src_dim]; - - filled[i] = true; - filled[i + low_delta] = true; - filled[i + high_delta] = true; - filled[i + low_delta + high_delta] = true; - } - //TODO: } else if (src_qubits.size() == 3) { - } else { - std::stringstream ss; - ss << "Fusion::illegal qubit number:" << src_qubits.size(); - throw std::runtime_error(ss.str()); - } - - return u; -} /******************************************************************************* * * NORMS diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 67795682d6..7a2f9d8f46 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -74,7 +74,6 @@ class State : public Base::State { Operations::OpType::bfunc, Operations::OpType::roerror, Operations::OpType::matrix, - Operations::OpType::matrix_sequence, Operations::OpType::multiplexer, Operations::OpType::kraus }); @@ -169,7 +168,7 @@ class State : public Base::State { virtual void apply_snapshot(const Operations::Op &op, OutputData &data); // Apply a matrix to given qubits (identity on all other qubits) - void apply_matrix(const reg_t &qubits, const cmatrix_t & mat); + void apply_matrix(const Operations::Op &op); // Apply a vectorized matrix to given qubits (identity on all other qubits) void apply_matrix(const reg_t &qubits, const cvector_t & vmat); @@ -181,9 +180,6 @@ class State : public Base::State { void apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, const cmatrix_t &mat); - // Apply multiple gate operations - void apply_matrix_sequence(const std::vector ®s, const std::vector& mats); - // Apply a Kraus error operation void apply_kraus(const reg_t &qubits, const std::vector &krausops, @@ -419,6 +415,7 @@ template void State::apply_ops(const std::vector &ops, OutputData &data, RngEngine &rng) { + // Simple loop over vector of input operations for (const auto & op: ops) { switch (op.type) { @@ -447,11 +444,8 @@ void State::apply_ops(const std::vector &ops, apply_snapshot(op, data); break; case Operations::OpType::matrix: - apply_matrix(op.qubits, op.mats[0]); + apply_matrix(op); break; - case Operations::OpType::matrix_sequence: - apply_matrix_sequence(op.regs, op.mats); - break; case Operations::OpType::multiplexer: apply_multiplexer(op.regs[0], op.regs[1], op.mats); // control qubits ([0]) & target qubits([1]) break; @@ -710,13 +704,6 @@ void State::apply_gate(const Operations::Op &op) { } -template -void State::apply_matrix(const reg_t &qubits, const cmatrix_t &mat) { - if (qubits.empty() == false && mat.size() > 0) { - apply_matrix(qubits, Utils::vectorize_matrix(mat)); - } -} - template void State::apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, const cmatrix_t &mat) { if (control_qubits.empty() == false && target_qubits.empty() == false && mat.size() > 0) { @@ -725,6 +712,17 @@ void State::apply_multiplexer(const reg_t &control_qubits, const reg } } +template +void State::apply_matrix(const Operations::Op &op) { + if (op.qubits.empty() == false && op.mats[0].size() > 0) { + if (Utils::is_diagonal(op.mats[0], .0)) { + BaseState::qreg_.apply_diagonal_matrix(op.qubits, Utils::vectorize_diagonal_matrix(op.mats[0])); + } else { + BaseState::qreg_.apply_matrix(op.qubits, Utils::vectorize_matrix(op.mats[0])); + } + } +} + template void State::apply_matrix(const reg_t &qubits, const cvector_t &vmat) { // Check if diagonal matrix @@ -736,21 +734,6 @@ void State::apply_matrix(const reg_t &qubits, const cvector_t &vmat) } - -template -void State::apply_matrix_sequence(const std::vector ®s, const std::vector& mats) { - - if (regs.empty()) - return; - - std::vector vmats; - for (const cmatrix_t& mat: mats) - vmats.push_back(Utils::vectorize_matrix(mat)); - - BaseState::qreg_.apply_matrix_sequence(regs, vmats); -} - - template void State::apply_gate_mcu3(const reg_t& qubits, double theta, diff --git a/src/transpile/basic_opts.hpp b/src/transpile/basic_opts.hpp index 7df6b28ce9..cb85e4b678 100644 --- a/src/transpile/basic_opts.hpp +++ b/src/transpile/basic_opts.hpp @@ -27,24 +27,28 @@ using oplist_t = std::vector; using opset_t = Operations::OpSet; using reg_t = std::vector; -class ReduceNop : public CircuitOptimization { +class ReduceBarrier : public CircuitOptimization { public: void optimize_circuit(Circuit& circ, const opset_t &opset, OutputData &data) const override; }; -void ReduceNop::optimize_circuit(Circuit& circ, +void ReduceBarrier::optimize_circuit(Circuit& circ, const opset_t &allowed_opset, OutputData &data) const { - oplist_t::iterator it = circ.ops.begin(); - while (it != circ.ops.end()) { - if (it->type == optype_t::barrier) - it = circ.ops.erase(it); - else - ++it; + size_t idx = 0; + for (size_t i = 0; i < circ.ops.size(); ++i) { + if (circ.ops[i].type != optype_t::barrier) { + if (idx != i) + circ.ops[idx] = circ.ops[i]; + ++idx; + } } + + if (idx != circ.ops.size()) + circ.ops.erase(circ.ops.begin() + idx, circ.ops.end()); } class Debug : public CircuitOptimization { diff --git a/src/transpile/fusion.hpp b/src/transpile/fusion.hpp index 80447137ff..5a45a3492d 100644 --- a/src/transpile/fusion.hpp +++ b/src/transpile/fusion.hpp @@ -29,19 +29,33 @@ using reg_t = std::vector; class Fusion : public CircuitOptimization { public: - Fusion(uint_t max_qubit = 5, uint_t threshold = 16, double cost_factor = 2.5); - + // constructor + Fusion(uint_t max_qubit = 5, uint_t threshold = 16, double cost_factor = 1.8); + + /* + * Fusion optimization uses following configuration options + * - fusion_verbose (bool): if true, output generated gates in metadata (default: false) + * - fusion_enable (bool): if true, activate fusion optimization (default: false) + * - fusion_max_qubit (int): maximum number of qubits for a operation (default: 5) + * - fusion_threshold (int): a threshold to activate fusion optimization when fusion_enable is true (default: 16) + * - fusion_cost_factor (double): a cost function to estimate an aggregate gate (default: 1.8) + */ void set_config(const json_t &config) override; void optimize_circuit(Circuit& circ, const opset_t &opset, OutputData &data) const override; +private: bool can_ignore(const op_t& op) const; bool can_apply_fusion(const op_t& op) const; - oplist_t aggregate(const oplist_t& buffer) const; + double get_cost(const op_t& op) const; + + bool aggregate_operations(oplist_t& ops, const int fusion_start, const int fusion_end) const; + + op_t generate_fusion_operation(const std::vector& fusioned_ops) const; void swap_cols_and_rows(const uint_t idx1, const uint_t idx2, @@ -52,6 +66,14 @@ class Fusion : public CircuitOptimization { const reg_t &sorted, const cmatrix_t &mat) const; + cmatrix_t expand_matrix(const reg_t& src_qubits, + const reg_t& dst_sorted_qubits, + const cmatrix_t& mat) const; + + bool only_u1(const oplist_t& ops, + const uint_t from, + const uint_t until) const; + double estimate_cost(const oplist_t& ops, const uint_t from, const uint_t until) const; @@ -69,7 +91,7 @@ class Fusion : public CircuitOptimization { private: uint_t max_qubit_; uint_t threshold_; - const double cost_factor_; + double cost_factor_; bool verbose_; bool active_; }; @@ -120,6 +142,8 @@ void Fusion::set_config(const json_t &config) { if (JSON::check_key("fusion_threshold", config_)) JSON::get_value(threshold_, "fusion_threshold", config_); + if (JSON::check_key("fusion_cost_factor", config_)) + JSON::get_value(cost_factor_, "fusion_cost_factor", config_); } @@ -149,50 +173,46 @@ void Fusion::optimize_circuit(Circuit& circ, OutputData &data) const { if (circ.num_qubits < threshold_ - || !active_ - || allowed_opset.optypes.find(optype_t::matrix_sequence) == allowed_opset.optypes.end()) + || !active_) return; - bool ret = false; + bool applied = false; + + uint_t fusion_start = 0; + for (uint_t op_idx = 0; op_idx < circ.ops.size(); ++op_idx) { + if (can_ignore(circ.ops[op_idx])) + continue; + if (!can_apply_fusion(circ.ops[op_idx])) { + applied |= fusion_start != op_idx && aggregate_operations(circ.ops, fusion_start, op_idx); + fusion_start = op_idx + 1; + } + } - oplist_t optimized_ops; + if (fusion_start < circ.ops.size() + && aggregate_operations(circ.ops, fusion_start, circ.ops.size())) + applied = true; - optimized_ops.clear(); - oplist_t buffer; + if (applied) { - for (const op_t op: circ.ops) { - if (can_ignore(op)) - continue; - if (!can_apply_fusion(op)) { - if (!buffer.empty()) { - ret = true; - oplist_t optimized_buffer = aggregate(buffer); - optimized_ops.insert(optimized_ops.end(), optimized_buffer.begin(), optimized_buffer.end()); - buffer.clear(); + size_t idx = 0; + for (size_t i = 0; i < circ.ops.size(); ++i) { + if (circ.ops[i].name != "nop") { + if (i != idx) + circ.ops[idx] = circ.ops[i]; + ++idx; } - optimized_ops.push_back(op); - } else { - buffer.push_back(op); } - } - if (!buffer.empty()) { - oplist_t optimized_buffer = aggregate(buffer); - optimized_ops.insert(optimized_ops.end(), optimized_buffer.begin(), optimized_buffer.end()); - ret = true; - } + if (idx != circ.ops.size()) + circ.ops.erase(circ.ops.begin() + idx, circ.ops.end()); - if (ret) { - circ.ops = optimized_ops; - if (verbose_) { + if (verbose_) data.add_additional_data("metadata", - json_t::object({{"fusion_verbose", optimized_ops}})); - } + json_t::object({{"fusion_verbose", circ.ops}})); } - #ifdef DEBUG - dump(optimized_ops); + dump(circ.ops); #endif } @@ -228,83 +248,202 @@ bool Fusion::can_apply_fusion(const op_t& op) const { } } -oplist_t Fusion::aggregate(const oplist_t& original) const { +double Fusion::get_cost(const op_t& op) const { + if (can_ignore(op)) + return .0; + else + return cost_factor_; +} + +bool Fusion::aggregate_operations(oplist_t& ops, const int fusion_start, const int fusion_end) const { // costs[i]: estimated cost to execute from 0-th to i-th in original.ops std::vector costs; // fusion_to[i]: best path to i-th in original.ops std::vector fusion_to; - int applied_total = 0; - // calculate the minimal path to each operation in the circuit - for (size_t i = 0; i < original.size(); ++i) { - bool applied = false; + // set costs and fusion_to of fusion_start + fusion_to.push_back(fusion_start); + costs.push_back(get_cost(ops[fusion_start])); - // first, fusion from i-th to i-th + bool applied = false; + // calculate the minimal path to each operation in the circuit + for (int i = fusion_start + 1; i < fusion_end; ++i) { + // init with fusion from i-th to i-th fusion_to.push_back(i); + costs.push_back(costs[i - fusion_start - 1] + get_cost(ops[i])); - // calculate the initial cost from i-th to i-th - if (i == 0) { - // if this is the first op, no fusion - costs.push_back(cost_factor_); - continue; - } - // otherwise, i-th cost is calculated from (i-1)-th cost - costs.push_back(costs[i - 1] + cost_factor_); - - for (uint_t num_fusion = 2; num_fusion <= max_qubit_; ++num_fusion) { + for (int num_fusion = 2; num_fusion <= static_cast (max_qubit_); ++num_fusion) { // calculate cost if {num_fusion}-qubit fusion is applied reg_t fusion_qubits; - add_fusion_qubits(fusion_qubits, original[i]); + add_fusion_qubits(fusion_qubits, ops[i]); - for (int j = i - 1; j >= 0; --j) { - add_fusion_qubits(fusion_qubits, original[j]); + for (int j = i - 1; j >= fusion_start; --j) { + add_fusion_qubits(fusion_qubits, ops[j]); - if (fusion_qubits.size() > num_fusion) // exceed the limit of fusion + if (static_cast (fusion_qubits.size()) > num_fusion) // exceed the limit of fusion break; // calculate a new cost of (i-th) by adding - double estimated_cost = estimate_cost(original, (uint_t) j, i) // fusion gate from j-th to i-th, and - + (j == 0 ? 0.0 : costs[j - 1]); // cost of (j-1)-th + double estimated_cost = estimate_cost(ops, (uint_t) j, i) // fusion gate from j-th to i-th, and + + (j == 0 ? 0.0 : costs[j - 1 - fusion_start]); // cost of (j-1)-th // update cost - if (estimated_cost < costs[i]) { - costs[i] = estimated_cost; - fusion_to[i] = j; + if (estimated_cost <= costs[i - fusion_start]) { + costs[i - fusion_start] = estimated_cost; + fusion_to[i - fusion_start] = j; applied = true; } } } - if (applied) - ++applied_total; } - if (applied_total / static_cast (original.size()) < 0.25) - return original; + if (!applied) + return false; // generate a new circuit with the minimal path to the last operation in the circuit - oplist_t optimized; + for (int i = fusion_end - 1; i >= fusion_start;) { - for (int i = original.size() - 1; i >= 0;) { - int to = fusion_to[i]; + int to = fusion_to[i - fusion_start]; - if (to == i) { - optimized.push_back(original[i]); - } else { - std::vector regs; - std::vector mats; + if (to != i) { + std::vector fusioned_ops; for (int j = to; j <= i; ++j) { - regs.push_back(original[j].qubits); - mats.push_back(matrix(original[j])); + fusioned_ops.push_back(ops[j]); + ops[j].name = "nop"; } - optimized.push_back(Operations::make_matrix_sequence(regs, mats)); + if (!fusioned_ops.empty()) + ops[i] = generate_fusion_operation(fusioned_ops); } i = to - 1; } - std::reverse(optimized.begin(), optimized.end()); + return true; +} + +op_t Fusion::generate_fusion_operation(const std::vector& fusioned_ops) const { + + std::vector regs; + std::vector mats; + + for (const op_t& fusioned_op: fusioned_ops) { + regs.push_back(fusioned_op.qubits); + mats.push_back(matrix(fusioned_op)); + } + + reg_t sorted_qubits; + for (const reg_t& reg: regs) + for (const uint_t qubit: reg) + if (std::find(sorted_qubits.begin(), sorted_qubits.end(), qubit) == sorted_qubits.end()) + sorted_qubits.push_back(qubit); + + std::sort(sorted_qubits.begin(), sorted_qubits.end()); + + std::vector sorted_mats; + + for (size_t i = 0; i < regs.size(); ++i) { + const reg_t& reg = regs[i]; + const cmatrix_t& mat = mats[i]; + sorted_mats.push_back(expand_matrix(reg, sorted_qubits, mat)); + } + + auto U = sorted_mats[0]; + const auto dim = 1ULL << sorted_qubits.size(); + + for (size_t m = 1; m < sorted_mats.size(); m++) { + + cmatrix_t u_tmp(U.GetRows(), U.GetColumns()); + const cmatrix_t& u = sorted_mats[m]; + + for (size_t i = 0; i < dim; ++i) + for (size_t j = 0; j < dim; ++j) + for (size_t k = 0; k < dim; ++k) + u_tmp(i, j) += u(i, k) * U(k, j); + + U = u_tmp; + } + + return Operations::make_fusion(sorted_qubits, U, fusioned_ops); +} + +cmatrix_t Fusion::expand_matrix(const reg_t& src_qubits, const reg_t& dst_sorted_qubits, const cmatrix_t& mat) const { + + const auto dst_dim = 1ULL << dst_sorted_qubits.size(); + + // generate a matrix for op + cmatrix_t u(dst_dim, dst_dim); + std::vector filled(dst_dim, false); + + if (src_qubits.size() == 1) { //1-qubit operation + // 1. identify delta + const auto index = std::distance(dst_sorted_qubits.begin(), + std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), src_qubits[0])); + + const auto delta = 1ULL << index; - return optimized; + // 2. find vmat(0, 0) position in U + for (uint_t i = 0; i < dst_dim; ++i) { + + if (filled[i]) + continue; + + // 3. allocate op.u to u based on u(i, i) and delta + u(i , (i + 0) ) = mat(0, 0); + u(i , (i + delta)) = mat(0, 1); + u((i + delta), (i + 0) ) = mat(1, 0); + u((i + delta), (i + delta)) = mat(1, 1); + filled[i] = filled[i + delta] = true; + } + } else if (src_qubits.size() == 2) { //2-qubit operation + + reg_t sorted_src_qubits = src_qubits; + std::sort(sorted_src_qubits.begin(), sorted_src_qubits.end()); + const cmatrix_t sorted_mat = sort_matrix(src_qubits, sorted_src_qubits, mat); + + // 1. identify low and high delta + auto low = std::distance(dst_sorted_qubits.begin(), + std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[0])); + + auto high = std::distance(dst_sorted_qubits.begin(), + std::find(dst_sorted_qubits.begin(), dst_sorted_qubits.end(), sorted_src_qubits[1])); + + auto low_delta = 1UL << low; + auto high_delta = 1UL << high; + + // 2. find op.u(0, 0) position in U + for (uint_t i = 0; i < dst_dim; ++i) { + if (filled[i]) + continue; + + // 3. allocate vmat to u based on u(i, i) and delta + u(i , (i + 0) ) = sorted_mat(0, 0); + u(i , (i + low_delta) ) = sorted_mat(0, 1); + u(i , (i + high_delta) ) = sorted_mat(0, 2); + u(i , (i + low_delta + high_delta)) = sorted_mat(0, 3); + u((i + low_delta) , (i + 0) ) = sorted_mat(1, 0); + u((i + low_delta) , (i + low_delta) ) = sorted_mat(1, 1); + u((i + low_delta) , (i + high_delta) ) = sorted_mat(1, 2); + u((i + low_delta) , (i + low_delta + high_delta)) = sorted_mat(1, 3); + u((i + high_delta) , (i + 0) ) = sorted_mat(2, 0); + u((i + high_delta) , (i + low_delta) ) = sorted_mat(2, 1); + u((i + high_delta) , (i + high_delta) ) = sorted_mat(2, 2); + u((i + high_delta) , (i + low_delta + high_delta)) = sorted_mat(2, 3); + u((i + low_delta + high_delta), (i + 0) ) = sorted_mat(3, 0); + u((i + low_delta + high_delta), (i + low_delta) ) = sorted_mat(3, 1); + u((i + low_delta + high_delta), (i + high_delta) ) = sorted_mat(3, 2); + u((i + low_delta + high_delta), (i + low_delta + high_delta)) = sorted_mat(3, 3); + + filled[i] = true; + filled[i + low_delta] = true; + filled[i + high_delta] = true; + filled[i + low_delta + high_delta] = true; + } + //TODO: } else if (src_qubits.size() == 3) { + } else { + throw std::runtime_error("Fusion::illegal qubit number: " + std::to_string(src_qubits.size())); + } + + return u; } //------------------------------------------------------------------------------ @@ -368,13 +507,41 @@ cmatrix_t Fusion::sort_matrix(const reg_t &src, return ret; } +bool Fusion::only_u1(const std::vector& ops, + const uint_t from, + const uint_t until) const { + + for (uint_t i = from; i <= until; ++i) { + if (ops[i].name == "u1") + continue; + if (from < i && (i + 2) <= until + && ops[i - 1].name == "u1" + && ops[i ].name == "cx" + && ops[i + 1].name == "u1" + && ops[i + 2].name == "cx" + && ops[i - 1].qubits[0] == ops[i ].qubits[1] + && ops[i ].qubits[1] == ops[i + 1].qubits[0] + && ops[i + 1].qubits[0] == ops[i + 2].qubits[1] + && ops[i ].qubits[0] == ops[i + 2].qubits[0] ) + { + i += 2; + continue; + } + return false; + } + return true; +} + double Fusion::estimate_cost(const std::vector& ops, const uint_t from, const uint_t until) const { + if (only_u1(ops, from, until)) + return cost_factor_; + reg_t fusion_qubits; for (uint_t i = from; i <= until; ++i) add_fusion_qubits(fusion_qubits, ops[i]); - return pow(cost_factor_, (double) fusion_qubits.size()); + return pow(cost_factor_, (double) std::max(fusion_qubits.size() - 1, size_t(1))); } void Fusion::add_fusion_qubits(reg_t& fusion_qubits, const op_t& op) const { @@ -432,7 +599,9 @@ cmatrix_t Fusion::matrix(const op_t& op) const { } else if (op.type == optype_t::matrix) { return op.mats[0]; } else { - throw std::runtime_error("Fusion: unexpected operation type"); + std::stringstream msg; + msg << "Fusion: unexpected operation type:" << op.type; + throw std::runtime_error(msg.str()); } } diff --git a/src/transpile/truncate_qubits.hpp b/src/transpile/truncate_qubits.hpp index 9ab4a73b8d..ed49b0572a 100644 --- a/src/transpile/truncate_qubits.hpp +++ b/src/transpile/truncate_qubits.hpp @@ -78,19 +78,18 @@ void TruncateQubits::optimize_circuit(Circuit& circ, if (new_mapping.size() == circ.num_qubits) return; - std::vector new_ops; - for (const Operations::Op& old_op: circ.ops) { - auto op = old_op; + for (Operations::Op& op: circ.ops) { // Remap qubits - op.qubits = remap_qubits(op.qubits, new_mapping); + reg_t new_qubits = remap_qubits(op.qubits, new_mapping); // Remap regs - for (reg_t ® : op.regs) { - reg = remap_qubits(reg, new_mapping); - } - new_ops.push_back(op); + std::vector new_regs; + for (reg_t ® : op.regs) + new_regs.push_back(remap_qubits(reg, new_mapping)); + + op.qubits = new_qubits; + op.regs = new_regs; } - circ.ops = new_ops; circ.num_qubits = new_mapping.size(); if (verbose_) { @@ -104,9 +103,13 @@ reg_t TruncateQubits::generate_mapping(const Circuit& circ) const { size_t not_used = circ.num_qubits + 1; reg_t mapping = reg_t(circ.num_qubits, not_used); - for (const Operations::Op& op: circ.ops) + for (const Operations::Op& op: circ.ops) { for (size_t qubit: op.qubits) mapping[qubit] = qubit; + for (const reg_t ®: op.regs) + for (size_t qubit: reg) + mapping[qubit] = qubit; + } mapping.erase(std::remove(mapping.begin(), mapping.end(), not_used), mapping.end()); From d0cc516173183aa8cda12741014a43d7eced6175 Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Mon, 8 Jul 2019 22:07:56 +0200 Subject: [PATCH 23/29] * Fix non-OpenMP builds (#272) * Better build messages while looking for external libraries --- CMakeLists.txt | 12 ++++++++---- src/base/controller.hpp | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 04d4f50fc2..53dbc5ad03 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,7 @@ endif() # # Looking for external libraries # +message(STATUS "Looking for OpenMP support...") find_package(OpenMP QUIET) # This is a hack for building with Apple's LLVM, which doesn't support OpenMP yet # so we need to link with an external library: libomp. @@ -136,12 +137,15 @@ if(OPENMP_FOUND) # the Terra Addon, see issue: https://github.com/Qiskit/qiskit-aer/issues/1 if(NOT SKBUILD) set(OPENMP_EXTERNAL_LIB "${OpenMP_${OpenMP_CXX_LIB_NAMES}_LIBRARY}") - message("Adding Clang: ${OPENMP_EXTERNAL_LIB}") + message(STATUS "Adding Clang: ${OPENMP_EXTERNAL_LIB}") endif() endif() - message("OpenMP found!") + message(STATUS "OpenMP found!") +else() + message(STATUS "WARNING: No OpenMP support found!") endif() +message(STATUS "Looking for NLOHMANN Json library...") set(NLOHMANN_JSON_PATH ${AER_SIMULATOR_CPP_EXTERNAL_LIBS}) find_package(nlohmann_json REQUIRED) find_package(Threads) @@ -160,7 +164,7 @@ else() endif() if(WIN32) - message("Uncompressing OpenBLAS static library...") + message(STATUS "Uncompressing OpenBLAS static library...") execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvfj" "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/win64/lib/openblas.7z" WORKING_DIRECTORY "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/win64/lib/") endif() @@ -172,7 +176,7 @@ if(NOT BLAS_FOUND) find_package(BLAS REQUIRED) endif() -message("BLAS: ${BLAS_LIBRARIES}") +message(STATUS "BLAS library found: ${BLAS_LIBRARIES}") # Set dependent libraries set(AER_LIBRARIES diff --git a/src/base/controller.hpp b/src/base/controller.hpp index f78a3de8c1..0c43bee878 100755 --- a/src/base/controller.hpp +++ b/src/base/controller.hpp @@ -42,8 +42,8 @@ #ifdef _OPENMP #include -#include "misc/hacks.hpp" #endif +#include "misc/hacks.hpp" namespace AER { From 5991f0675c0b4159c0316b96bcdd456bc694359d Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Tue, 9 Jul 2019 16:21:16 +0200 Subject: [PATCH 24/29] Add controlled-u1 to basis_gates (#258) * Fix cu1 gate and add to basis_gates * Improves efficiency of simulation of cu1, cuz gates * Optimize anti-diagonal single qubit gates * Rename vectorize_matrix_diagonal * Add Quantum Fourier Transform benchmark (#1) --- CHANGELOG.md | 7 + .../providers/aer/backends/qasm_simulator.py | 4 +- .../aer/backends/statevector_simulator.py | 2 +- .../aer/backends/unitary_simulator.py | 2 +- src/framework/utils.hpp | 41 ++++-- src/simulators/statevector/qubitvector.hpp | 137 ++++++++++++------ .../statevector/statevector_state.hpp | 34 ++--- src/simulators/unitary/unitary_state.hpp | 13 +- .../quantum_fourier_transform_benchmarks.py | 78 ++++++++++ test/benchmark/tools.py | 27 +++- 10 files changed, 255 insertions(+), 90 deletions(-) create mode 100644 test/benchmark/quantum_fourier_transform_benchmarks.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 800d7cc7fe..f91573a162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,12 +20,18 @@ Changelog](http://keepachangelog.com/en/1.0.0/). Added ----- +- Added multi-controlled phase gate to `QubitVector` and changed + multi-controlled Z and multi-controlled u1 gates to use this method (\# 258) +- Added optimized anti-diagonal single-qubit gates to QubitVector (\# 258) Changed ------- +- Improve performance of matrix fusion circuit optimization and move fusion + code out of `QubitVector` class and into Fusion optimization class (\#255) Removed ------- +- Remove `matrix_sequence` Op type from `Op` class (\#255) Fixed ----- @@ -40,6 +46,7 @@ Fixed Added ----- +- Added 2-qubit Pauli and reset approximation to noise transformation (\#236) - Added `to_instruction` method to `ReadoutError` (\#257). Changed diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index 7fdef0a38a..6007f0fafd 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -157,7 +157,7 @@ class QasmSimulator(AerBackend): 'description': 'A C++ simulator with realistic noise for qobj files', 'coupling_map': None, 'basis_gates': [ - 'u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', + 'u1', 'u2', 'u3', 'cx', 'cz', 'cu1', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot', 'unitary', 'reset', 'initialize', 'kraus', 'roerror' ], @@ -187,7 +187,7 @@ def _validate(self, qobj, backend_options, noise_model): "id", "x", "y", "z", "h", "s", "sdg", "CX", "cx", "cz", "swap", "barrier", "reset", "measure", 'roerror' ] - unsupported_ch_instructions = ["u2", "u3"] + unsupported_ch_instructions = ["u2", "u3", "cu1"] # Check if noise model is Clifford: method = "automatic" if backend_options and "method" in backend_options: diff --git a/qiskit/providers/aer/backends/statevector_simulator.py b/qiskit/providers/aer/backends/statevector_simulator.py index d1e55146bc..5471e454fe 100644 --- a/qiskit/providers/aer/backends/statevector_simulator.py +++ b/qiskit/providers/aer/backends/statevector_simulator.py @@ -83,7 +83,7 @@ class StatevectorSimulator(AerBackend): 'max_shots': 1, 'description': 'A C++ statevector simulator for qobj files', 'coupling_map': None, - 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', + 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'cu1', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot', 'unitary', 'reset', 'initialize'], 'gates': [ diff --git a/qiskit/providers/aer/backends/unitary_simulator.py b/qiskit/providers/aer/backends/unitary_simulator.py index 02d831edda..58811368e9 100644 --- a/qiskit/providers/aer/backends/unitary_simulator.py +++ b/qiskit/providers/aer/backends/unitary_simulator.py @@ -88,7 +88,7 @@ class UnitarySimulator(AerBackend): 'description': 'A Python simulator for computing the unitary' 'matrix for experiments in qobj files', 'coupling_map': None, - 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'id', 'x', 'y', 'z', + 'basis_gates': ['u1', 'u2', 'u3', 'cx', 'cz', 'cu1', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 't', 'tdg', 'ccx', 'swap', 'multiplexer', 'snapshot', 'unitary'], 'gates': [ diff --git a/src/framework/utils.hpp b/src/framework/utils.hpp index fc3320ea46..80a27cc7dc 100755 --- a/src/framework/utils.hpp +++ b/src/framework/utils.hpp @@ -88,21 +88,35 @@ class Matrix { // Matrix Functions //------------------------------------------------------------------------------ -// Vector conversion +// Construct a matrix from a vector of matrix-row vectors template matrix make_matrix(const std::vector> &mat); + +// Reshape a length column-major vectorized matrix into a square matrix template matrix devectorize_matrix(const std::vector &vec); + +// Vectorize a matrix by stacking matrix columns (column-major vectorization) template std::vector vectorize_matrix(const matrix &mat); -template std::vector vectorize_diagonal_matrix(const matrix& mat); -// Transformations +// Return the transpose a matrix template matrix transpose(const matrix &A); + +// Return the adjoing (Hermitian-conjugate) of a matrix template matrix> dagger(const matrix> &A); + +// Return the complex conjugate of a matrix template matrix> conjugate(const matrix> &A); + +// Given a list of matrices for a multiplexer stacks and packs them 0/1/2/... +// into a single 2^control x (2^target x 2^target) cmatrix_t) +// Equivalent to a 2^qubits x 2^target "flat" matrix template matrix stacked_matrix(const std::vector> &mmat); +// Return a vector containing the diagonal of a matrix +template std::vector matrix_diagonal(const matrix& mat); + // Tracing template T trace(const matrix &A); template matrix partial_trace_a(const matrix &rho, size_t dimA); @@ -510,16 +524,6 @@ std::vector vectorize_matrix(const matrix& mat) { return vec; } -template -std::vector vectorize_diagonal_matrix(const matrix& mat) { - std::vector vec; - size_t size = std::min(mat.GetRows(), mat.GetColumns()); - vec.resize(size, 0.); - for (size_t i=0; i < size; i++) - vec[i] = mat(i, i); - return vec; -} - template matrix make_matrix(const std::vector> & mat) { size_t nrows = mat.size(); @@ -574,8 +578,6 @@ matrix> conj(const matrix> &A) { return temp; } -// Given a list of matrices for a multiplexer, stacks and packs them 0/1/2/... into a single 2^control x (2^target x 2^target) cmatrix_t) -// Equivalent to a 2^qubits x 2^target "flat" matrix template matrix stacked_matrix(const std::vector> &mmat){ size_t size_of_controls = mmat[0].GetRows(); // or GetColumns, as these matrices are (should be) square @@ -605,6 +607,15 @@ matrix stacked_matrix(const std::vector> &mmat){ return stacked_matrix; } +template +std::vector matrix_diagonal(const matrix& mat) { + std::vector vec; + size_t size = std::min(mat.GetRows(), mat.GetColumns()); + vec.resize(size, 0.); + for (size_t i=0; i < size; i++) + vec[i] = mat(i, i); + return vec; +} template T trace(const matrix &A) { diff --git a/src/simulators/statevector/qubitvector.hpp b/src/simulators/statevector/qubitvector.hpp index 87f90053da..25c928003f 100755 --- a/src/simulators/statevector/qubitvector.hpp +++ b/src/simulators/statevector/qubitvector.hpp @@ -276,15 +276,17 @@ class QubitVector { // If N=2 this implements an optimized CY gate // If N=3 this implements an optimized CCY gate void apply_mcy(const reg_t &qubits); - - // Apply a general multi-controlled Z-gate - // If N=1 this implements an optimized Z gate - // If N=2 this implements an optimized CZ gate - // If N=3 this implements an optimized CCZ gate - void apply_mcz(const reg_t &qubits); + // Apply a general multi-controlled single-qubit phase gate + // with diagonal [1, ..., 1, phase] + // If N=1 this implements an optimized single-qubit phase gate + // If N=2 this implements an optimized CPhase gate + // If N=3 this implements an optimized CCPhase gate + // if phase = -1 this is a Z, CZ, CCZ gate + void apply_mcphase(const reg_t &qubits, const complex_t phase); + // Apply a general multi-controlled single-qubit unitary gate - // If N=1 this implements an optimized single-qubit gate + // If N=1 this implements an optimized single-qubit U gate // If N=2 this implements an optimized CU gate // If N=3 this implements an optimized CCU gate void apply_mcu(const reg_t &qubits, const cvector_t &mat); @@ -1416,74 +1418,72 @@ void QubitVector::apply_mcy(const reg_t &qubits) { } template -void QubitVector::apply_mcz(const reg_t &qubits) { +void QubitVector::apply_mcswap(const reg_t &qubits) { + // Calculate the swap positions for the last two qubits. + // If N = 2 this is just a regular SWAP gate rather than a controlled-SWAP gate. const size_t N = qubits.size(); + const size_t pos0 = MASKS[N - 1]; + const size_t pos1 = pos0 + BITS[N - 2]; switch (N) { - case 1: { - // Lambda function for Z gate - auto lambda = [&](const areg_t<2> &inds)->void { - data_[inds[1]] *= -1.; - }; - apply_lambda(lambda, areg_t<1>({{qubits[0]}})); - return; - } case 2: { - // Lambda function for CZ gate + // Lambda function for SWAP gate auto lambda = [&](const areg_t<4> &inds)->void { - data_[inds[3]] *= -1.; + std::swap(data_[inds[pos0]], data_[inds[pos1]]); }; apply_lambda(lambda, areg_t<2>({{qubits[0], qubits[1]}})); return; } case 3: { - // Lambda function for CCZ gate + // Lambda function for C-SWAP gate auto lambda = [&](const areg_t<8> &inds)->void { - data_[inds[7]] *= -1.; + std::swap(data_[inds[pos0]], data_[inds[pos1]]); }; apply_lambda(lambda, areg_t<3>({{qubits[0], qubits[1], qubits[2]}})); return; } default: { - // Lambda function for general multi-controlled X gate + // Lambda function for general multi-controlled SWAP gate auto lambda = [&](const indexes_t &inds)->void { - data_[inds[MASKS[N]]] *= -1.; + std::swap(data_[inds[pos0]], data_[inds[pos1]]); }; apply_lambda(lambda, qubits); } } // end switch } - template -void QubitVector::apply_mcswap(const reg_t &qubits) { - // Calculate the swap positions for the last two qubits. - // If N = 2 this is just a regular SWAP gate rather than a controlled-SWAP gate. +void QubitVector::apply_mcphase(const reg_t &qubits, const complex_t phase) { const size_t N = qubits.size(); - const size_t pos0 = MASKS[N - 1]; - const size_t pos1 = pos0 + BITS[N - 2]; - switch (N) { + case 1: { + // Lambda function for arbitrary Phase gate with diagonal [1, phase] + auto lambda = [&](const areg_t<2> &inds)->void { + data_[inds[1]] *= phase; + }; + apply_lambda(lambda, areg_t<1>({{qubits[0]}})); + return; + } case 2: { - // Lambda function for SWAP gate + // Lambda function for CPhase gate with diagonal [1, 1, 1, phase] auto lambda = [&](const areg_t<4> &inds)->void { - std::swap(data_[inds[pos0]], data_[inds[pos1]]); + data_[inds[3]] *= phase; }; apply_lambda(lambda, areg_t<2>({{qubits[0], qubits[1]}})); return; } case 3: { - // Lambda function for C-SWAP gate auto lambda = [&](const areg_t<8> &inds)->void { - std::swap(data_[inds[pos0]], data_[inds[pos1]]); + data_[inds[7]] *= phase; }; apply_lambda(lambda, areg_t<3>({{qubits[0], qubits[1], qubits[2]}})); return; } default: { - // Lambda function for general multi-controlled SWAP gate + // Lambda function for general multi-controlled Phase gate + // with diagonal [1, ..., 1, phase] auto lambda = [&](const indexes_t &inds)->void { - std::swap(data_[inds[pos0]], data_[inds[pos1]]); + data_[inds[MASKS[N]]] *= phase; }; apply_lambda(lambda, qubits); } @@ -1502,6 +1502,12 @@ void QubitVector::apply_mcu(const reg_t &qubits, // diagonal matrix lambda function // TODO: this should be changed to not check doubles with == if (mat[1] == 0.0 && mat[2] == 0.0) { + // Check if actually a phase gate + if (mat[0] == 1.0) { + apply_mcphase(qubits, mat[3]); + return; + } + // Otherwise apply general diagonal gate const cvector_t diag = {{mat[0], mat[3]}}; // Diagonal version switch (N) { @@ -1593,25 +1599,62 @@ void QubitVector::apply_mcu(const reg_t &qubits, template void QubitVector::apply_matrix(const uint_t qubit, const cvector_t& mat) { - // Check if matrix is actually diagonal and if so use - // apply_diagonal_matrix - // TODO: this should be changed to not check doubles with == + // Check if matrix is diagonal and if so use optimized lambda if (mat[1] == 0.0 && mat[2] == 0.0) { const cvector_t diag = {{mat[0], mat[3]}}; apply_diagonal_matrix(qubit, diag); return; } + + // Convert qubit to array register for lambda functions + areg_t<1> qubits = {{qubit}}; - // Lambda function for single-qubit matrix multiplication - auto lambda = [&](const areg_t<2> &inds, - const cvector_t &_mat)->void { - const auto pos0 = inds[0]; - const auto pos1 = inds[1]; - const auto cache = data_[pos0]; - data_[pos0] = _mat[0] * data_[pos0] + _mat[2] * data_[pos1]; - data_[pos1] = _mat[1] * cache + _mat[3] * data_[pos1]; + // Check if anti-diagonal matrix and if so use optimized lambda + if(mat[0] == 0.0 && mat[3] == 0.0) { + if (mat[1] == 1.0 && mat[2] == 1.0) { + // X-matrix + auto lambda = [&](const areg_t<2> &inds)->void { + std::swap(data_[inds[0]], data_[inds[1]]); + }; + apply_lambda(lambda, qubits); + return; + } + if (mat[2] == 0.0) { + // Non-unitary projector + // possibly used in measure/reset/kraus update + auto lambda = [&](const areg_t<2> &inds)->void { + data_[inds[1]] = mat[1] * data_[inds[0]]; + data_[inds[0]] = 0.0; + }; + apply_lambda(lambda, qubits); + return; + } + if (mat[1] == 0.0) { + // Non-unitary projector + // possibly used in measure/reset/kraus update + auto lambda = [&](const areg_t<2> &inds)->void { + data_[inds[0]] = mat[2] * data_[inds[1]]; + data_[inds[1]] = 0.0; + }; + apply_lambda(lambda, qubits); + return; + } + // else we have a general anti-diagonal matrix + auto lambda = [&](const areg_t<2> &inds)->void { + const complex_t cache = data_[inds[0]]; + data_[inds[0]] = mat[2] * data_[inds[1]]; + data_[inds[1]] = mat[1] * cache; + }; + apply_lambda(lambda, qubits); + return; + } + // Otherwise general single-qubit matrix multiplication + auto lambda = [&](const areg_t<2> &inds)->void { + const auto cache = data_[inds[0]]; + data_[inds[0]] = mat[0] * cache + mat[2] * data_[inds[1]]; + data_[inds[1]] = mat[1] * cache + mat[3] * data_[inds[1]]; }; - apply_lambda(lambda, areg_t<1>({{qubit}}), mat); + apply_lambda(lambda, qubits); } template diff --git a/src/simulators/statevector/statevector_state.hpp b/src/simulators/statevector/statevector_state.hpp index 7a2f9d8f46..2300fbcb8b 100755 --- a/src/simulators/statevector/statevector_state.hpp +++ b/src/simulators/statevector/statevector_state.hpp @@ -81,9 +81,9 @@ class State : public Base::State { // Return the set of qobj gate instruction names supported by the State virtual stringset_t allowed_gates() const override { - return {"u1", "u2", "u3", "cx", "cz", "cy", "swap", + return {"u1", "u2", "u3", "cx", "cz", "cy", "cu1", "swap", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "ccx", - "mcx", "mcz", "mcy", "mcu1", "mcu2", "mcu3", "mcswap"}; + "mcx", "mcz", "mcy", "mcz", "mcu1", "mcu2", "mcu3", "mcswap"}; } // Return the set of qobj snapshot types supported by the State @@ -285,23 +285,24 @@ template const stringmap_t State::gateset_({ // Single qubit gates {"id", Gates::id}, // Pauli-Identity gate - {"x", Gates::mcx}, // Pauli-X gate - {"y", Gates::mcy}, // Pauli-Y gate - {"z", Gates::mcz}, // Pauli-Z gate + {"x", Gates::mcx}, // Pauli-X gate + {"y", Gates::mcy}, // Pauli-Y gate + {"z", Gates::mcz}, // Pauli-Z gate {"s", Gates::s}, // Phase gate (aka sqrt(Z) gate) {"sdg", Gates::sdg}, // Conjugate-transpose of Phase gate {"h", Gates::h}, // Hadamard gate (X + Z / sqrt(2)) {"t", Gates::t}, // T-gate (sqrt(S)) {"tdg", Gates::tdg}, // Conjguate-transpose of T gate // Waltz Gates - {"u1", Gates::mcu1}, // zero-X90 pulse waltz gate - {"u2", Gates::mcu2}, // single-X90 pulse waltz gate - {"u3", Gates::mcu3}, // two X90 pulse waltz gate + {"u1", Gates::mcu1}, // zero-X90 pulse waltz gate + {"u2", Gates::mcu2}, // single-X90 pulse waltz gate + {"u3", Gates::mcu3}, // two X90 pulse waltz gate // Two-qubit gates - {"cx", Gates::mcx}, // Controlled-X gate (CNOT) - {"cy", Gates::mcy}, // Controlled-Y gate - {"cz", Gates::mcz}, // Controlled-Z gate - {"swap", Gates::mcswap}, // SWAP gate + {"cx", Gates::mcx}, // Controlled-X gate (CNOT) + {"cy", Gates::mcy}, // Controlled-Y gate + {"cz", Gates::mcz}, // Controlled-Z gate + {"cu1", Gates::mcu1}, // Controlled-u1 gate + {"swap", Gates::mcswap}, // SWAP gate {"mcswap", Gates::mcswap}, // Multi-controlled SWAP gate // Multi-qubit controlled gates {"ccx", Gates::mcx}, // Controlled-CX gate (Toffoli) @@ -561,7 +562,7 @@ void State::snapshot_pauli_expval(const Operations::Op &op, BaseState::qreg_.apply_mcy({op.qubits[pos]}); break; case 'Z': - BaseState::qreg_.apply_mcz({op.qubits[pos]}); + BaseState::qreg_.apply_mcphase({op.qubits[pos]}, -1); break; default: { std::stringstream msg; @@ -652,7 +653,7 @@ void State::apply_gate(const Operations::Op &op) { break; case Gates::mcz: // Includes Z, CZ, CCZ, etc - BaseState::qreg_.apply_mcz(op.qubits); + BaseState::qreg_.apply_mcphase(op.qubits, -1); break; case Gates::id: break; @@ -693,9 +694,8 @@ void State::apply_gate(const Operations::Op &op) { break; case Gates::mcu1: // Includes u1, cu1, etc - apply_gate_mcu3(op.qubits, 0., 0., std::real(op.params[0])); + BaseState::qreg_.apply_mcphase(op.qubits, std::exp(complex_t(0, 1) * op.params[0])); break; - default: // We shouldn't reach here unless there is a bug in gateset throw std::invalid_argument("QubitVector::State::invalid gate instruction \'" + @@ -716,7 +716,7 @@ template void State::apply_matrix(const Operations::Op &op) { if (op.qubits.empty() == false && op.mats[0].size() > 0) { if (Utils::is_diagonal(op.mats[0], .0)) { - BaseState::qreg_.apply_diagonal_matrix(op.qubits, Utils::vectorize_diagonal_matrix(op.mats[0])); + BaseState::qreg_.apply_diagonal_matrix(op.qubits, Utils::matrix_diagonal(op.mats[0])); } else { BaseState::qreg_.apply_matrix(op.qubits, Utils::vectorize_matrix(op.mats[0])); } diff --git a/src/simulators/unitary/unitary_state.hpp b/src/simulators/unitary/unitary_state.hpp index 1ceb75f22e..efc5172ead 100755 --- a/src/simulators/unitary/unitary_state.hpp +++ b/src/simulators/unitary/unitary_state.hpp @@ -67,7 +67,7 @@ class State : public Base::State> { // Return the set of qobj gate instruction names supported by the State virtual stringset_t allowed_gates() const override { - return {"u1", "u2", "u3", "cx", "cz", "cy", "swap", + return {"u1", "u2", "u3", "cx", "cz", "cy", "cu1", "swap", "id", "x", "y", "z", "h", "s", "sdg", "t", "tdg", "ccx", "mcx", "mcz", "mcy", "mcu1", "mcu2", "mcu3", "mcswap"}; } @@ -187,9 +187,10 @@ const stringmap_t State::gateset_({ {"u2", Gates::mcu2}, // single-X90 pulse waltz gate {"u3", Gates::mcu3}, // two X90 pulse waltz gate // Two-qubit gates - {"cx", Gates::mcx}, // Controlled-X gate (CNOT) - {"cy", Gates::mcy}, // Controlled-Z gate - {"cz", Gates::mcz}, // Controlled-Z gate + {"cx", Gates::mcx}, // Controlled-X gate (CNOT) + {"cy", Gates::mcy}, // Controlled-Z gate + {"cz", Gates::mcz}, // Controlled-Z gate + {"cu1", Gates::mcu1}, // Controlled-u1 gate {"swap", Gates::mcswap}, // SWAP gate // Three-qubit gates {"ccx", Gates::mcx}, // Controlled-CX gate (Toffoli) @@ -322,7 +323,7 @@ void State::apply_gate(const Operations::Op &op) { break; case Gates::mcz: // Includes Z, CZ, CCZ, etc - BaseState::qreg_.apply_mcz(op.qubits); + BaseState::qreg_.apply_mcphase(op.qubits, -1); break; case Gates::id: break; @@ -363,7 +364,7 @@ void State::apply_gate(const Operations::Op &op) { break; case Gates::mcu1: // Includes u1, cu1, etc - apply_gate_mcu3(op.qubits, 0., 0., std::real(op.params[0])); + BaseState::qreg_.apply_mcphase(op.qubits, std::exp(complex_t(0, 1) * op.params[0])); break; default: // We shouldn't reach here unless there is a bug in gateset diff --git a/test/benchmark/quantum_fourier_transform_benchmarks.py b/test/benchmark/quantum_fourier_transform_benchmarks.py new file mode 100644 index 0000000000..2fa82477e8 --- /dev/null +++ b/test/benchmark/quantum_fourier_transform_benchmarks.py @@ -0,0 +1,78 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2019. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Quantum Fourier Transform benchmark suite""" +# Write the benchmarking functions here. +# See "Writing benchmarks" in the asv docs for more information. + +from qiskit import QiskitError +from qiskit.compiler import transpile, assemble +from qiskit.providers.aer import QasmSimulator +from .tools import quantum_fourier_transform_circuit, mixed_unitary_noise_model, \ + reset_noise_model, kraus_noise_model, no_noise + + +class QuantumFourierTransformTimeSuite: + """ + Benchmarking times for Quantum Fourier Transform with various noise configurations + - ideal (no noise) + - mixed state + - reset + - kraus + + For each noise model, we want to test various configurations of number of + qubits + + The methods defined in this class will be executed by ASV framework as many + times as the combination of all parameters exist in `self.params`, for + exmaple: self.params = ([1,2,3],[4,5,6]), will run all methdos 9 times: + time_method(1,4) + time_method(1,5) + time_method(1,6) + time_method(2,4) + time_method(2,5) + time_method(2,6) + time_method(3,4) + time_method(3,5) + time_method(3,6) + """ + + def __init__(self): + self.timeout = 60 * 20 + self.qft_circuits = [] + self.backend = QasmSimulator() + for num_qubits in (5, 10, 15): + circ = quantum_fourier_transform_circuit(num_qubits) + circ = transpile(circ) + qobj = assemble(circ, self.backend, shots=1) + self.qft_circuits.append(qobj) + + self.param_names = ["Quantum Fourier Transform", "Noise Model"] + # This will run every benchmark for one of the combinations we have here: + # bench(qft_circuits, None) => bench(qft_circuits, mixed()) => + # bench(qft_circuits, reset) => bench(qft_circuits, kraus()) + self.params = (self.qft_circuits, [ + no_noise(), + mixed_unitary_noise_model(), + reset_noise_model(), + kraus_noise_model() + ]) + + + def setup(self, qobj, noise_model_wrapper): + pass + + + def time_quantum_fourier_transform(self, qobj, noise_model_wrapper): + result = self.backend.run(qobj, noise_model=noise_model_wrapper()).result() + if result.status != 'COMPLETED': + raise QiskitError("Simulation failed. Status: " + result.status) diff --git a/test/benchmark/tools.py b/test/benchmark/tools.py index 16574c88a3..ab73920c3a 100644 --- a/test/benchmark/tools.py +++ b/test/benchmark/tools.py @@ -135,7 +135,7 @@ def qft_circuit(num_qubits, measure=True): # Create quantum/classical registers of size n qr = QuantumRegister(num_qubits) circuit = QuantumCircuit(qr) - + for i in range(num_qubits): for j in range(i): circuit.cu1(math.pi/float(2**(i-j)), qr[i], qr[j]) @@ -188,3 +188,28 @@ def simple_cnot_circuit(num_qubits, measure=True): if measure: circuit = _add_measurements(circuit, qr) return circuit + + +# pylint: disable=invalid-name +def quantum_fourier_transform_circuit(num_qubits): + """Create quantum fourier transform circuit. + + Args: + num_qubits (int): Number of qubits + + Returns: + QuantumCircuit: QFT circuit + """ + qreg = QuantumRegister(num_qubits) + creg = ClassicalRegister(num_qubits) + + circuit = QuantumCircuit(qreg, creg, name="qft") + + n = len(qreg) + + for i in range(n): + for j in range(i): + circuit.cu1(math.pi/float(2**(i-j)), qreg[i], qreg[j]) + circuit.h(qreg[i]) + circuit.measure(qreg, creg) + return circuit From 241c3d2f445cd55978da06dc0f51df80743d1a9d Mon Sep 17 00:00:00 2001 From: hhorii Date: Wed, 10 Jul 2019 00:11:11 +0900 Subject: [PATCH 25/29] fix sampling bug in qasm simulator (#270) --- src/simulators/qasm/qasm_controller.hpp | 2 +- test/terra/reference/ref_measure.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/simulators/qasm/qasm_controller.hpp b/src/simulators/qasm/qasm_controller.hpp index 48e5dbeede..72f9fc155a 100755 --- a/src/simulators/qasm/qasm_controller.hpp +++ b/src/simulators/qasm/qasm_controller.hpp @@ -574,7 +574,7 @@ QasmController::check_measure_sampling_opt(const Circuit &circ) const { auto start_meas = start; // Check all remaining operations are measurements while (start != circ.ops.end()) { - if (start->type != Operations::OpType::measure) { + if (start->type != Operations::OpType::measure && start->conditional) { return std::make_pair(false, 0); } ++start; diff --git a/test/terra/reference/ref_measure.py b/test/terra/reference/ref_measure.py index 31255ca0dd..7fd7262bfb 100644 --- a/test/terra/reference/ref_measure.py +++ b/test/terra/reference/ref_measure.py @@ -119,7 +119,6 @@ def measure_statevector_deterministic(): targets.append(array([0, 0, 0, 1])) return targets - # ========================================================================== # Non-Deterministic output # ========================================================================== From 48111ea07eef1844cddb6f927ad8d9b575aec617 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 10 Jul 2019 10:23:41 +0200 Subject: [PATCH 26/29] Add check if ops can be conditionals (#271) * Add check if ops can be conditionals --- CHANGELOG.md | 2 + src/framework/operations.hpp | 77 ++++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f91573a162..5c339e0667 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ Added Changed ------- +- When loading qobj check if all instructions are conditional and raise an + exception if an unsupported instruction is conditional (\#271) - Deprecate the use of \".as\_dict()\" in favor of \".to\_dict()\" (\#228) - Set simulator seed from \"seed\_simulator\" in qobj (\#210) diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 290f197d51..e28f650155 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -555,6 +555,9 @@ Op json_to_op_noise_switch(const json_t &js); // Classical bits Op json_to_op_roerror(const json_t &js); +// Optional instruction parameters +enum class Allowed {Yes, No}; +void add_condtional(const Allowed val, Op& op, const json_t &js); //------------------------------------------------------------------------------ @@ -623,6 +626,29 @@ json_t op_to_json(const Op &op) { // Implementation: Gates, measure, reset deserialization //------------------------------------------------------------------------------ + +void add_condtional(const Allowed allowed, Op& op, const json_t &js) { + // Check conditional + if (JSON::check_key("conditional", js)) { + // If instruction isn't allow to be conditional throw an exception + if (allowed == Allowed::No) { + throw std::invalid_argument("Invalid instruction: \"" + op.name + "\" cannot be conditional."); + } + // If instruction is allowed to be conditional add parameters + if (js["conditional"].is_number()) { + // New style conditional + op.conditional_reg = js["conditional"]; + op.conditional = true; + } else { + // DEPRECATED: old style conditional (remove in 0.3) + JSON::get_value(op.old_conditional_mask, "mask", js["conditional"]); + JSON::get_value(op.old_conditional_val, "val", js["conditional"]); + op.old_conditional = true; + } + } +} + + Op json_to_op_gate(const json_t &js) { Op op; op.type = OpType::gate; @@ -639,19 +665,8 @@ Op json_to_op_gate(const json_t &js) { else op.string_params = {op.name}; - // Check conditional - if (JSON::check_key("conditional", js)) { - if (js["conditional"].is_number()) { - // New style conditional - op.conditional_reg = js["conditional"]; - op.conditional = true; - } else { - // DEPRECATED: old style conditional (remove in 0.3) - JSON::get_value(op.old_conditional_mask, "mask", js["conditional"]); - JSON::get_value(op.old_conditional_val, "val", js["conditional"]); - op.old_conditional = true; - } - } + // Conditional + add_condtional(Allowed::Yes, op, js); // Validation check_empty_name(op); @@ -672,6 +687,8 @@ Op json_to_op_barrier(const json_t &js) { op.type = OpType::barrier; op.name = "barrier"; JSON::get_value(op.qubits, "qubits", js); + // Check conditional + add_condtional(Allowed::No, op, js); return op; } @@ -684,6 +701,9 @@ Op json_to_op_measure(const json_t &js) { JSON::get_value(op.memory, "memory", js); JSON::get_value(op.registers, "register", js); + // Conditional + add_condtional(Allowed::No, op, js); + // Validation check_empty_qubits(op); check_duplicate_qubits(op); @@ -703,6 +723,9 @@ Op json_to_op_reset(const json_t &js) { op.name = "reset"; JSON::get_value(op.qubits, "qubits", js); + // Conditional + add_condtional(Allowed::No, op, js); + // Validation check_empty_qubits(op); check_duplicate_qubits(op); @@ -716,6 +739,10 @@ Op json_to_op_initialize(const json_t &js) { op.name = "initialize"; JSON::get_value(op.qubits, "qubits", js); JSON::get_value(op.params, "params", js); + + // Conditional + add_condtional(Allowed::No, op, js); + // Validation check_empty_qubits(op); check_duplicate_qubits(op); @@ -768,11 +795,13 @@ Op json_to_op_bfunc(const json_t &js) { op.bfunc = it->second; } + // Conditional + add_condtional(Allowed::No, op, js); + // Validation if (op.registers.empty()) { throw std::invalid_argument("Invalid measure operation: \"register\" is empty."); } - return op; } @@ -785,6 +814,8 @@ Op json_to_op_roerror(const json_t &js) { JSON::get_value(op.registers, "register", js); JSON::get_value(op.probs, "probabilities", js); // DEPRECATED: Remove in 0.4 JSON::get_value(op.probs, "params", js); + // Conditional + add_condtional(Allowed::No, op, js); return op; } @@ -813,11 +844,13 @@ Op json_to_op_unitary(const json_t &js) { std::string label; JSON::get_value(label, "label", js); op.string_params.push_back(label); + + // Conditional + add_condtional(Allowed::Yes, op, js); return op; } Op json_to_op_multiplexer(const json_t &js) { - // Parse parameters reg_t qubits; std::vector mats; @@ -826,7 +859,10 @@ Op json_to_op_multiplexer(const json_t &js) { JSON::get_value(mats, "params", js); JSON::get_value(label, "label", js); // Construct op - return make_multiplexer(qubits, mats, label); + auto op = make_multiplexer(qubits, mats, label); + // Conditional + add_condtional(Allowed::Yes, op, js); + return op; } Op json_to_op_kraus(const json_t &js) { @@ -839,6 +875,8 @@ Op json_to_op_kraus(const json_t &js) { // Validation check_empty_qubits(op); check_duplicate_qubits(op); + // Conditional + add_condtional(Allowed::No, op, js); return op; } @@ -848,6 +886,8 @@ Op json_to_op_noise_switch(const json_t &js) { op.type = OpType::noise_switch; op.name = "noise_switch"; JSON::get_value(op.params, "params", js); + // Conditional + add_condtional(Allowed::No, op, js); return op; } @@ -866,7 +906,10 @@ Op json_to_op_snapshot(const json_t &js) { snapshot_type == "expectation_value_matrix_with_variance") return json_to_op_snapshot_matrix(js); // Default snapshot: has "type", "label", "qubits" - return json_to_op_snapshot_default(js); + auto op = json_to_op_snapshot_default(js); + // Conditional + add_condtional(Allowed::No, op, js); + return op; } From 04cf2bb1882ebe0c0db5d55b9f180074fb0f04df Mon Sep 17 00:00:00 2001 From: hhorii Date: Wed, 10 Jul 2019 20:10:58 +0900 Subject: [PATCH 27/29] Improve diagonal matrix multiplication (#262) * Optimize qubitvector for diagonal matrix --- src/simulators/statevector/qubitvector.hpp | 66 ++++++---------------- 1 file changed, 17 insertions(+), 49 deletions(-) diff --git a/src/simulators/statevector/qubitvector.hpp b/src/simulators/statevector/qubitvector.hpp index 25c928003f..66c518ef52 100755 --- a/src/simulators/statevector/qubitvector.hpp +++ b/src/simulators/statevector/qubitvector.hpp @@ -1192,61 +1192,29 @@ void QubitVector::apply_multiplexer(const reg_t &control_qubits, template void QubitVector::apply_diagonal_matrix(const reg_t &qubits, const cvector_t &diag) { - const size_t N = qubits.size(); + const int_t N = qubits.size(); // Error checking #ifdef DEBUG check_vector(diag, N); #endif - switch (N) { - case 1: - apply_diagonal_matrix(qubits[0], diag); - return; - case 2: { - // Lambda function for 2-qubit diagional matrix multiplication - auto lambda = [&](const areg_t<4> &inds, - const cvector_t &_mat)->void { - for (size_t i = 0; i < 4; i++) { - data_[inds[i]] *= _mat[i]; - } - }; - apply_lambda(lambda, areg_t<2>({{qubits[0], qubits[1]}}), diag); - return; - } - case 3: { - // Lambda function for 3-qubit diagional matrix multiplication - auto lambda = [&](const areg_t<8> &inds, - const cvector_t &_mat)->void { - for (size_t i = 0; i < 8; i++) { - data_[inds[i]] *= _mat[i]; - } - }; - apply_lambda(lambda, areg_t<3>({{qubits[0], qubits[1], qubits[2]}}), diag); - return; - } - case 4: { - // Lambda function for 4-qubit diagional matrix multiplication - auto lambda = [&](const areg_t<16> &inds, - const cvector_t &_mat)->void { - for (size_t i = 0; i < 16; i++) { - data_[inds[i]] *= _mat[i]; - } - }; - apply_lambda(lambda, areg_t<4>({{qubits[0], qubits[1], qubits[2], qubits[3]}}), diag); - return; - } - default: { - const uint_t DIM = BITS[N]; - // Lambda function for N-qubit diagional matrix multiplication - auto lambda = [&](const indexes_t &inds, - const cvector_t &_mat)->void { - for (size_t i = 0; i < DIM; i++) { - data_[inds[i]] *= _mat[i]; - } - }; - apply_lambda(lambda, qubits, diag); + if (N == 1) { + apply_diagonal_matrix(qubits[0], diag); + return; + } + + auto lambda = [&](const areg_t<2> &inds, const cvector_t &_diag)->void { + for (int_t i = 0; i < 2; ++i) { + const int_t k = inds[i]; + int_t iv = 0; + for (int_t j = 0; j < N; j++) + if ((k & (1ULL << qubits[j])) != 0) + iv += (1 << j); + if (diag[iv] != 1.0) + data_[k] *= diag[iv]; } - } // end switch + }; + apply_lambda(lambda, areg_t<1>({{qubits[0]}}), diag); } template From cf5d8027d5a70f45751f1369f548b65c666442e1 Mon Sep 17 00:00:00 2001 From: "Christopher J. Wood" Date: Wed, 10 Jul 2019 15:20:09 +0200 Subject: [PATCH 28/29] fix bug in NoiseModel for nonlocal errors (#276) --- qiskit/providers/aer/noise/noise_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/aer/noise/noise_model.py b/qiskit/providers/aer/noise/noise_model.py index 21ce26cec0..170564b4b0 100644 --- a/qiskit/providers/aer/noise/noise_model.py +++ b/qiskit/providers/aer/noise/noise_model.py @@ -424,7 +424,7 @@ def add_nonlocal_quantum_error(self, qs_str = self._qubits2str(qubits) nqs_str = self._qubits2str(noise_qubits) if qs_str in gate_qubit_dict: - noise_qubit_dict = gate_qubit_dict[nqs_str] + noise_qubit_dict = gate_qubit_dict[qs_str] if nqs_str in noise_qubit_dict: new_error = noise_qubit_dict[nqs_str].compose(error) noise_qubit_dict[nqs_str] = new_error From f9bceca808eda50d4028c2a5783b5548a535ff1d Mon Sep 17 00:00:00 2001 From: Juan Gomez Date: Wed, 10 Jul 2019 15:51:52 +0200 Subject: [PATCH 29/29] pip release: 0.2.2 --- qiskit/providers/aer/VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/providers/aer/VERSION.txt b/qiskit/providers/aer/VERSION.txt index 0c62199f16..ee1372d33a 100644 --- a/qiskit/providers/aer/VERSION.txt +++ b/qiskit/providers/aer/VERSION.txt @@ -1 +1 @@ -0.2.1 +0.2.2