diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..cc3f2683f8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,3 @@ +BasedOnStyle: LLVM +AlwaysBreakTemplateDeclarations: Yes +RemoveBracesLLVM: No diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..b73ce87e6e --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1 @@ +8c9cfb3a2cde607dd0fe3236cc5ea53e6d06b2fe diff --git a/.github/workflows/docs-publish.yml b/.github/workflows/docs-publish.yml index 4cdb823bfb..6c5ffaae34 100644 --- a/.github/workflows/docs-publish.yml +++ b/.github/workflows/docs-publish.yml @@ -1,5 +1,6 @@ name: Docs Publish on: + workflow_dispatch: push: tags: - "*" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 3b0d94d2c2..da877479ac 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,7 +37,7 @@ jobs: pip install -U pip virtualenv wheel pip install -U tox sudo apt-get update - sudo apt-get install -y build-essential libopenblas-dev + sudo apt-get install -y build-essential libopenblas-dev pandoc shell: bash - name: Run Docs Build run: tox -edocs @@ -50,7 +50,7 @@ jobs: needs: [docs] strategy: matrix: - python-version: [3.7] + python-version: [3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d540b0978e..2f43de1ca8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,10 +34,15 @@ jobs: pip install -U pip wheel pip install -U -c constraints.txt -r requirements-dev.txt qiskit-terra numpy scipy shell: bash + - name: Run clang-format + run: | + set -e + sh tools/clang-format.sh --Werror -n + shell: bash - name: Run Lint run: | set -e - pycodestyle --ignore=E402,W503,W504 --max-line-length=100 qiskit_aer + black --check qiskit_aer test tools setup.py pylint -j 2 -rn qiskit_aer sdist: runs-on: ${{ matrix.platform.os }} diff --git a/.pylintrc b/.pylintrc index c8443f43e8..5ae93e220b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -21,7 +21,7 @@ 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 - pylint.extensions.docstyle # basic docstring stle checks + pylint.extensions.docstyle, # basic docstring style checks # Use multiple processes to speed up Pylint. jobs=1 @@ -33,8 +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= - +extension-pkg-allow-list= [MESSAGES CONTROL] @@ -43,10 +42,10 @@ extension-pkg-whitelist= confidence= # Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifiers separated by comma (,) or put this option +# either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. -#enable= +enable=use-symbolic-message-instead # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this @@ -57,7 +56,8 @@ 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=no-self-use, # disabled as it is too verbose +disable=spelling, # way too noisy + 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 @@ -70,8 +70,13 @@ disable=no-self-use, # disabled as it is too verbose 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 - unsubscriptable-object, # pylint can't determine this for numpy types - bad-continuation, bad-whitespace # differences of opinion with black + import-outside-toplevel, + bad-continuation, bad-whitespace, # differences of opinion with black + import-error, # overzealous with our optionals/dynamic packages + consider-using-f-string, # pass re-lint old commits + broad-exception-raised, # pass re-lint old commits + unsubscriptable-object # pass re-lint old commits + @@ -83,12 +88,6 @@ disable=no-self-use, # disabled as it is too verbose # mypackage.mymodule.MyReporterClass. output-format=text -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". This option is deprecated -# and it will be removed in Pylint 2.0. -files-output=no - # Tells whether to display a full report or only the messages reports=yes @@ -117,8 +116,8 @@ evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / stateme # pi = the PI constant # 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,dt, - __unittest +good-names=a,b,i,j,k,d,n,m,ex,v,w,x,y,z,Run,_,logger,q,c,r,qr,cr,qc,nd,pi,op,b,ar,br,p,cp,dt, + __unittest,iSwapGate,mu # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,toto,tutu,tata @@ -171,16 +170,16 @@ attr-rgx=[a-z_][a-z0-9_]{2,30}$ attr-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ +argument-rgx=[a-z_][a-z0-9_]{2,30}|ax|dt$ # Naming hint for argument names argument-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ +variable-rgx=[a-z_][a-z0-9_]{1,30}$ # Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ +variable-name-hint=[a-z_][a-z0-9_]{1,30}$ # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ @@ -212,7 +211,7 @@ max-nested-blocks=5 [FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=105 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ @@ -279,7 +278,7 @@ spelling-dict= spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= +spelling-private-dict-file=.local-spellings # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. @@ -296,7 +295,7 @@ 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,numpy.random,qiskit.providers +ignored-modules=numpy # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of @@ -333,7 +332,7 @@ callbacks=cb_,_cb # List of qualified module names which can have objects that can redefine # builtins. -redefining-builtins-modules=six.moves,future.builtins,tools.compiler +redefining-builtins-modules=six.moves,future.builtins [CLASSES] diff --git a/CMakeLists.txt b/CMakeLists.txt index e6ed3d51df..1db8b19893 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,7 +262,7 @@ if(AER_THRUST_SUPPORTED) set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} ${AER_CUDA_ARCH_FLAGS_EXPAND} -DAER_THRUST_CUDA -I${AER_SIMULATOR_CPP_SRC_DIR} -isystem ${AER_SIMULATOR_CPP_SRC_DIR}/third-party/headers -use_fast_math --expt-extended-lambda") set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_CUDA) - set(THRUST_DEPENDENT_LIBS "") + set(THRUST_DEPENDANT_LIBS "-L${CUDA_TOOLKIT_ROOT_DIR}/lib64") if(NOT DEFINED CUQUANTUM_ROOT) if(DEFINED ENV{CUQUANTUM_ROOT}) set(CUQUANTUM_ROOT $ENV{CUQUANTUM_ROOT}) @@ -283,14 +283,14 @@ if(AER_THRUST_SUPPORTED) set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_CUSTATEVEC AER_CUTENSORNET) if(DEFINED CUQUANTUM_ROOT) set(AER_COMPILER_FLAGS "${AER_COMPILER_FLAGS} -I${CUQUANTUM_ROOT}/include") - set(THRUST_DEPENDANT_LIBS "-L${CUQUANTUM_ROOT}/lib") + set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -L${CUQUANTUM_ROOT}/lib/${CUDA_VERSION_MAJOR}") endif() if(DEFINED CUTENSOR_ROOT) set(AER_COMPILER_FLAGS "${AER_COMPILER_FLAGS} -I${CUTENSOR_ROOT}/include") - set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -L${CUTENSOR_ROOT}/lib/11") + set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -L${CUTENSOR_ROOT}/lib/${CUDA_VERSION_MAJOR}") endif() if(CUQUANTUM_STATIC) - set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -lcustatevec_static -lcutensornet_static -lcutensor -lcublas") + set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -lcustatevec_static -lcutensornet_static -lcutensor_static -lmetis_static -lcusolver_static -lcusparse_static -lcusolver_lapack_static -lcublas_static -lcublasLt_static -lculibos") else() set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -lcustatevec -lcutensornet -lcutensor") endif() @@ -299,9 +299,9 @@ if(AER_THRUST_SUPPORTED) set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_CUSTATEVEC) set(AER_COMPILER_FLAGS "${AER_COMPILER_FLAGS} -I${CUSTATEVEC_ROOT}/include") if(CUSTATEVEC_STATIC) - set(THRUST_DEPENDANT_LIBS "-L${CUSTATEVEC_ROOT}/lib -L${CUSTATEVEC_ROOT}/lib64 -lcustatevec_static -L${CUDA_TOOLKIT_ROOT_DIR}/lib64 -lcublas") + set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -L${CUSTATEVEC_ROOT}/lib -L${CUSTATEVEC_ROOT}/lib/${CUDA_VERSION_MAJOR} -lcustatevec_static -lcusolver_static -lcusparse_static -lcusolver_lapack_static -lcublas_static -lcublasLt_static -lculibos") else() - set(THRUST_DEPENDANT_LIBS "-L${CUSTATEVEC_ROOT}/lib -L${CUSTATEVEC_ROOT}/lib64 -lcustatevec") + set(THRUST_DEPENDANT_LIBS "${THRUST_DEPENDANT_LIBS} -L${CUSTATEVEC_ROOT}/lib -L${CUSTATEVEC_ROOT}/lib/${CUDA_VERSION_MAJOR} -lcustatevec") endif() endif() elseif(AER_THRUST_BACKEND STREQUAL "TBB") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 307136e3c5..59d1bdb829 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,11 @@ please ensure that: 1. The code follows the code style of the project and successfully passes the tests. For convenience, you can execute `tox` locally, which will run these checks and report any issues. + + If your code fails the local style checks, you can use `tox -eblack` + and `tox -eclang` to automatically fix and update the code formatting + in python and C++, respectively. + 2. The documentation has been updated accordingly. In particular, if a function or class has been modified during the PR, please update the *docstring* accordingly. @@ -34,7 +39,7 @@ automation. This works through a combination of the git log and the pull request. When a release is tagged and pushed to GitHub, the release automation bot looks at all commit messages from the git log for the release. It takes the PR numbers from the git log (assuming a squash merge) and checks if that PR had -a `Changelog:` label on it. If there is a label it will add the git commit +a `Changelog:` label on it. If there is a label, it will add the git commit message summary line from the git log for the release to the changelog. If there are multiple `Changelog:` tags on a PR, the git commit message summary @@ -64,7 +69,7 @@ understand if they need to update their program which uses Qiskit, and how they would go about doing that. It ideally should explain why they need to make this change too, to provide the necessary context. -To make sure we don't forget a release note or if the details of user-facing +To make sure we don't forget a release note if the details of user-facing changes over a release cycle, we require that all user facing changes include documentation at the same time as the code. To accomplish this, we use the [reno](https://docs.openstack.org/reno/latest/) tool which enables a git-based @@ -181,10 +186,29 @@ https://github.com/Qiskit/qiskit/blob/master/docs/release_notes.rst) Building The release notes are part of the standard qiskit-aer documentation builds. To check what the rendered HTML output of the release notes will look -like for the current state of the repo, you can run: `tox -edocs` which will +like for the current state of the repo, you need to install +[pandoc](https://pandoc.org/installing.html), then you can run: `tox -edocs` which will build all the documentation into `docs/_build/html` and the release notes in particular will be located at `docs/_build/html/release_notes.html` +## Style and lint + +Qiskit Aer uses 3 tools for verifying code formatting and lint checking. The +first tool is [black](https://github.com/psf/black) which is a Python code formatting +tool that will automatically update the code formatting to a consistent style. +The second tool is [pylint](https://www.pylint.org/) which is a code linter +which does a deeper analysis of the Python code to find both style issues and +potential bugs and other common issues in Python. The third tool is +[clang-format](https://clang.llvm.org/docs/ClangFormat.html) which is a +C++ code formatting tool that will automatically update codes with a consistent style. + +You can check that your local modifications conform to the style rules +by running `tox -elint` which will run `black`, `pylint` and `clang-format` +to check the local code formatting and lint. If black returns a code +formatting error you can run `tox -eblack` to automatically update the +code formatting to conform to the style. However, if `pylint` returns +any error you will have to fix these issues by manually updating your code. + ### Development Cycle The development cycle for qiskit-aer is all handled in the open using @@ -216,7 +240,7 @@ When it is time to release a new minor version of qiskit-aer, we will: 1. Create a new tag with the version number and push it to github 2. Change the `main` version to the next release version. -The release automation processes will be triggered by the new tag and perform +The release automation processes will be triggered by the new tag and will perform the following steps: 1. Create a stable branch for the new minor version from the release tag @@ -439,7 +463,7 @@ As any other Python package, we can install from source code by just running: qiskit-aer$ pip install . This will build and install `Aer` with the default options which is probably suitable for most of the users. -There's another Pythonic approach to build and install software: build the wheels distributable file. +There's another Pythonic approach to building and installing software: build the wheels distributable file. qiskit-aer$ python ./setup.py bdist_wheel @@ -542,7 +566,7 @@ As any other Python package, we can install from source code by just running: (QiskitDevEnv) qiskit-aer > pip install . This will build and install `Aer` with the default options which is probably suitable for most of the users. -There's another Pythonic approach to build and install software: build the wheels distributable file. +There's another Pythonic approach to building and installing software: build the wheels distributable file. (QiskitDevEnv) qiskit-aer > python ./setup.py bdist_wheel @@ -1079,3 +1103,28 @@ Now we can run the Python interpreter and pass the arguments (the python file to Target 0: (python) stopped. After this, you can step through the code and continue with your debug session as always. + + +## Dealing with the git blame ignore list + +In the qiskit-aer repository we maintain a list of commits for git blame to +ignore. This is mostly commits that are code style changes that don't change +the functionality but just change the code formatting (for example, when we +migrated to use black for code formatting). This file, `.git-blame-ignore-revs` +just contains a list of commit SHA1s you can tell git to ignore when using the +`git blame` command. This can be done one time with something like + +``` +git blame --ignore-revs-file .git-blame-ignore-revs qiskit/version.py + +``` + +from the root of the repository. If you'd like to enable this by default you +can update your local repository's configuration with: + +``` +git config blame.ignoreRevsFile .git-blame-ignore-revs +``` + +which will update your local repositories configuration to use the ignore list +by default. diff --git a/README.md b/README.md index 199654cfce..d261484abc 100755 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **Qiskit** is an open-source framework for working with noisy quantum computers at the level of pulses, circuits, and algorithms. -Qiskit is made up of elements that each work together to enable quantum computing. This element is **Aer**, which provides high-performance quantum computing simulators with realistic noise models. +Qiskit is made up of elements that work together to enable quantum computing. This element is **Aer**, which provides high-performance quantum computing simulators with realistic noise models. ## Installation @@ -14,7 +14,7 @@ We encourage installing Qiskit via the pip tool (a python package manager). The pip install qiskit qiskit-aer ``` -Pip will handle all dependencies automatically for us and you will always install the latest (and well-tested) version. +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](CONTRIBUTING.md). @@ -34,7 +34,7 @@ the same functionality found in the canonical `qiskit-aer` package, plus the ability to run the GPU supported simulators: statevector, density matrix, and unitary. **Note**: This package is only available on x86_64 Linux. For other platforms -that have CUDA support you will have to build from source. You can refer to +that have CUDA support, you will have to build from source. You can refer to the [contributing guide](CONTRIBUTING.md#building-with-gpu-support) for instructions on doing this. @@ -84,9 +84,9 @@ print('Counts(noise):', counts_noise) ## Contribution Guidelines If you'd like to contribute to Qiskit, please take a look at our -[contribution guidelines](CONTRIBUTING.md). This project adheres to Qiskit's [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expect to uphold to this code. +[contribution guidelines](CONTRIBUTING.md). This project adheres to Qiskit's [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold 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://qiskit.slack.com/join/shared_invite/zt-fybmq791-hYRopcSH6YetxycNPXgv~A#/). 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). +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://qiskit.slack.com/join/shared_invite/zt-fybmq791-hYRopcSH6YetxycNPXgv~A#/). 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). ## Next Steps diff --git a/cmake/FindPybind11.cmake b/cmake/FindPybind11.cmake index be1ced78b4..ed26dd1e41 100644 --- a/cmake/FindPybind11.cmake +++ b/cmake/FindPybind11.cmake @@ -1,4 +1,4 @@ -find_package(PythonLibs REQUIRED) +find_package(PythonLibs) message(STATUS ${PYTHON_INCLUDE_DIRS}) message(STATUS "PYTHON EXECUTABLE: ${PYTHON_EXECUTABLE}") diff --git a/contrib/runtime/aer_runtime.cpp b/contrib/runtime/aer_runtime.cpp index 3686714005..037b60da7c 100644 --- a/contrib/runtime/aer_runtime.cpp +++ b/contrib/runtime/aer_runtime.cpp @@ -11,218 +11,224 @@ * copyright notice, and modified files need to carry a notice indicating * that they have been altered from the originals. */ -#include #include "controllers/state_controller.hpp" +#include // initialize and return state extern "C" { -void* aer_state() { - AER::AerState* handler = new AER::AerState(); +void *aer_state() { + AER::AerState *handler = new AER::AerState(); return handler; }; -void* aer_state_initialize(void* handler) { - AER::AerState* state = reinterpret_cast(handler); +void *aer_state_initialize(void *handler) { + AER::AerState *state = reinterpret_cast(handler); state->initialize(); return handler; }; // finalize state -void aer_state_finalize(void* handler) { - AER::AerState* state = reinterpret_cast(handler); - delete(state); +void aer_state_finalize(void *handler) { + AER::AerState *state = reinterpret_cast(handler); + delete (state); }; // configure state -void aer_state_configure(void* handler, char* key, char* value) { - AER::AerState* state = reinterpret_cast(handler); +void aer_state_configure(void *handler, char *key, char *value) { + AER::AerState *state = reinterpret_cast(handler); state->configure(key, value); }; // allocate qubits and return the first qubit index. // following qubits are indexed with incremented indices. -uint_t aer_allocate_qubits(void* handler, uint_t num_qubits) { - AER::AerState* state = reinterpret_cast(handler); +uint_t aer_allocate_qubits(void *handler, uint_t num_qubits) { + AER::AerState *state = reinterpret_cast(handler); auto qubit_ids = state->allocate_qubits(num_qubits); return qubit_ids[0]; }; // measure qubits -uint_t aer_apply_measure(void* handler, uint_t* qubits_, size_t num_qubits) { - AER::AerState* state = reinterpret_cast(handler); +uint_t aer_apply_measure(void *handler, uint_t *qubits_, size_t num_qubits) { + AER::AerState *state = reinterpret_cast(handler); std::vector qubits; qubits.insert(qubits.end(), &(qubits_[0]), &(qubits[num_qubits - 1])); return state->apply_measure(qubits); }; // return probability of a specific bitstring -double aer_probability(void* handler, uint_t outcome) { - AER::AerState* state = reinterpret_cast(handler); +double aer_probability(void *handler, uint_t outcome) { + AER::AerState *state = reinterpret_cast(handler); return state->probability(outcome); }; // return probability amplitude of a specific bitstring -complex_t aer_amplitude(void* handler, uint_t outcome) { - AER::AerState* state = reinterpret_cast(handler); +complex_t aer_amplitude(void *handler, uint_t outcome) { + AER::AerState *state = reinterpret_cast(handler); return state->amplitude(outcome); }; // return probability amplitudes // returned pointer must be freed in the caller -complex_t* aer_release_statevector(void* handler) { - AER::AerState* state = reinterpret_cast(handler); +complex_t *aer_release_statevector(void *handler) { + AER::AerState *state = reinterpret_cast(handler); AER::Vector sv = state->move_to_vector(); return sv.move_to_buffer(); }; // phase gate -void aer_apply_p(void* handler, uint_t qubit, double lambda) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_p(void *handler, uint_t qubit, double lambda) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcphase({qubit}, lambda); }; // Pauli gate: bit-flip or NOT gate -void aer_apply_x(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_x(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcx({qubit}); }; // Pauli gate: bit and phase flip -void aer_apply_y(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_y(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcy({qubit}); }; // Pauli gate: phase flip -void aer_apply_z(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_z(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcz({qubit}); }; // Clifford gate: Hadamard -void aer_apply_h(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_h(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_h(qubit); }; // Clifford gate: sqrt(Z) or S gate -void aer_apply_s(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_s(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_u(qubit, 0, 0, M_PI / 2.0); }; // Clifford gate: inverse of sqrt(Z) -void aer_apply_sdg(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); - state->apply_u(qubit, 0, 0, - M_PI / 2.0); +void aer_apply_sdg(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); + state->apply_u(qubit, 0, 0, -M_PI / 2.0); }; // // sqrt(S) or T gate -void aer_apply_t(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_t(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_u(qubit, 0, 0, M_PI / 4.0); }; // inverse of sqrt(S) -void aer_apply_tdg(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); - state->apply_u({qubit}, 0, 0, - M_PI / 4.0); +void aer_apply_tdg(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); + state->apply_u({qubit}, 0, 0, -M_PI / 4.0); }; // sqrt(NOT) gate -void aer_apply_sx(void* handler, uint_t qubit) { - AER::AerState* state = reinterpret_cast(handler); - state->apply_mcrx({qubit}, - M_PI / 4.0); +void aer_apply_sx(void *handler, uint_t qubit) { + AER::AerState *state = reinterpret_cast(handler); + state->apply_mcrx({qubit}, -M_PI / 4.0); }; // Rotation around X-axis -void aer_apply_rx(void* handler, uint_t qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_rx(void *handler, uint_t qubit, double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcrx({qubit}, theta); }; // rotation around Y-axis -void aer_apply_ry(void* handler, uint_t qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_ry(void *handler, uint_t qubit, double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcry({qubit}, theta); }; // rotation around Z axis -void aer_apply_rz(void* handler, uint_t qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_rz(void *handler, uint_t qubit, double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcrz({qubit}, theta); }; // controlled-NOT -void aer_apply_cx(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cx(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcx({ctrl_qubit, tgt_qubit}); }; // controlled-Y -void aer_apply_cy(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cy(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcy({ctrl_qubit, tgt_qubit}); }; // controlled-Z -void aer_apply_cz(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cz(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcz({ctrl_qubit, tgt_qubit}); }; // controlled-phase -void aer_apply_cp(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double lambda) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cp(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit, + double lambda) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcphase({ctrl_qubit, tgt_qubit}, lambda); }; // controlled-rx -void aer_apply_crx(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_crx(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit, + double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcrx({ctrl_qubit, tgt_qubit}, theta); }; // controlled-ry -void aer_apply_cry(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cry(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit, + double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcry({ctrl_qubit, tgt_qubit}, theta); }; // controlled-rz -void aer_apply_crz(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double theta) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_crz(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit, + double theta) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcrz({ctrl_qubit, tgt_qubit}, theta); }; // controlled-H -void aer_apply_ch(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_ch(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcu({ctrl_qubit, tgt_qubit}, M_PI / 2.0, 0, M_PI, 0); }; // swap -void aer_apply_swap(void* handler, uint_t qubit0, uint_t qubit1) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_swap(void *handler, uint_t qubit0, uint_t qubit1) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcswap({qubit0, qubit1}); }; // Toffoli -void aer_apply_ccx(void* handler, uint_t qubit0, uint_t qubit1, uint_t qubit2) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_ccx(void *handler, uint_t qubit0, uint_t qubit1, uint_t qubit2) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcx({qubit0, qubit1, qubit2}); }; // // controlled-swap -void aer_apply_cswap(void* handler, uint_t ctrl_qubit, uint_t qubit0, uint_t qubit1) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cswap(void *handler, uint_t ctrl_qubit, uint_t qubit0, + uint_t qubit1) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcswap({ctrl_qubit, qubit0, qubit1}); }; // four parameter controlled-U gate with relative phase γ -void aer_apply_cu(void* handler, uint_t ctrl_qubit, uint_t tgt_qubit, double theta, double phi, double lambda, double gamma) { - AER::AerState* state = reinterpret_cast(handler); +void aer_apply_cu(void *handler, uint_t ctrl_qubit, uint_t tgt_qubit, + double theta, double phi, double lambda, double gamma) { + AER::AerState *state = reinterpret_cast(handler); state->apply_mcu({ctrl_qubit, tgt_qubit}, theta, phi, lambda, gamma); }; } diff --git a/contrib/standalone/qasm_simulator.cpp b/contrib/standalone/qasm_simulator.cpp old mode 100755 new mode 100644 index d0b8fa9c0a..708c8128ac --- a/contrib/standalone/qasm_simulator.cpp +++ b/contrib/standalone/qasm_simulator.cpp @@ -12,7 +12,7 @@ * that they have been altered from the originals. */ -//#define DEBUG // Uncomment for verbose debugging output +// #define DEBUG // Uncomment for verbose debugging output #include #include #include @@ -27,32 +27,28 @@ /******************************************************************************* * * EXIT CODES: - * + * * 0: The Qobj was succesfully executed. * Returns full result JSON. - * + * * 1: Command line invalid or Qobj JSON cannot be loaded. * Returns JSON: * {"success": false, "status": "ERROR: Invalid input (error msg)"} - * + * * 2: Qobj failed to load or execute. * Returns JSON: * {"success": false, "status": "ERROR: Failed to execute qobj (error msg)"} - * + * * 3: At least one experiment in Qobj failed to execute successfully. * Returns parial result JSON with failed experiments returning: * "{"success": false, "status": "ERROR: error msg"} * ******************************************************************************/ -enum class CmdArguments { - SHOW_VERSION, - INPUT_CONFIG, - INPUT_DATA -}; +enum class CmdArguments { SHOW_VERSION, INPUT_CONFIG, INPUT_DATA }; -inline CmdArguments parse_cmd_options(const std::string& argv){ - if(argv == "-v" || argv == "--version") +inline CmdArguments parse_cmd_options(const std::string &argv) { + if (argv == "-v" || argv == "--version") return CmdArguments::SHOW_VERSION; if (argv == "-c" || argv == "--config") @@ -61,22 +57,20 @@ inline CmdArguments parse_cmd_options(const std::string& argv){ return CmdArguments::INPUT_DATA; } -inline void show_version(){ - std::cout << "Qiskit Aer: " - << AER_MAJOR_VERSION << "." - << AER_MINOR_VERSION << "." - << AER_PATCH_VERSION << "\n"; +inline void show_version() { + std::cout << "Qiskit Aer: " << AER_MAJOR_VERSION << "." << AER_MINOR_VERSION + << "." << AER_PATCH_VERSION << "\n"; } inline void failed(const std::string &msg, std::ostream &o = std::cout, - int indent = -1){ + int indent = -1) { json_t ret; ret["success"] = false; ret["status"] = std::string("ERROR: ") + msg; o << ret.dump(indent) << std::endl; } -inline void usage(const std::string& command, std::ostream &out){ +inline void usage(const std::string &command, std::ostream &out) { failed("Invalid command line", out); // Print usage message std::cerr << "\n\n"; @@ -85,7 +79,8 @@ inline void usage(const std::string& command, std::ostream &out){ std::cerr << "Usage: \n"; std::cerr << command << " [-v] [-c ] \n"; std::cerr << " -v : Show version\n"; - std::cerr << " -c : Configuration file\n";; + std::cerr << " -c : Configuration file\n"; + ; std::cerr << " file : qobj file\n"; } @@ -95,51 +90,51 @@ int main(int argc, char **argv) { int indent = 4; json_t qobj; json_t config; - int myrank=0; + int myrank = 0; #ifdef AER_MPI int prov; - int nprocs=1; - MPI_Init_thread(&argc,&argv,MPI_THREAD_MULTIPLE,&prov); - MPI_Comm_size(MPI_COMM_WORLD,&nprocs); - MPI_Comm_rank(MPI_COMM_WORLD,&myrank); + int nprocs = 1; + MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &prov); + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank); #endif - if(argc == 1){ // NOLINT + if (argc == 1) { // NOLINT usage(std::string(argv[0]), out); // NOLINT return 1; } // Parse command line options - for(auto pos = 1UL; pos < static_cast(argc); ++pos){ // NOLINT - auto option = parse_cmd_options(std::string(argv[pos])); // NOLINT - switch(option){ - case CmdArguments::SHOW_VERSION: - show_version(); - return 0; - case CmdArguments::INPUT_CONFIG: - if (++pos == static_cast(argc)) { - failed("Invalid config (no file is specified.)", out, indent); - return 1; - } - try { - config = JSON::load(std::string(argv[pos])); - }catch(std::exception &e){ - std::string msg = "Invalid config (" + std::string(e.what()) + ")"; - failed(msg, out, indent); - return 1; - } - break; - case CmdArguments::INPUT_DATA: - try { - qobj = JSON::load(std::string(argv[pos])); // NOLINT - pos = argc; //Exit from the loop - }catch(std::exception &e){ - std::string msg = "Invalid input (" + std::string(e.what()) + ")"; - failed(msg, out, indent); - return 1; - } - break; + for (auto pos = 1UL; pos < static_cast(argc); ++pos) { // NOLINT + auto option = parse_cmd_options(std::string(argv[pos])); // NOLINT + switch (option) { + case CmdArguments::SHOW_VERSION: + show_version(); + return 0; + case CmdArguments::INPUT_CONFIG: + if (++pos == static_cast(argc)) { + failed("Invalid config (no file is specified.)", out, indent); + return 1; + } + try { + config = JSON::load(std::string(argv[pos])); + } catch (std::exception &e) { + std::string msg = "Invalid config (" + std::string(e.what()) + ")"; + failed(msg, out, indent); + return 1; + } + break; + case CmdArguments::INPUT_DATA: + try { + qobj = JSON::load(std::string(argv[pos])); // NOLINT + pos = argc; // Exit from the loop + } catch (std::exception &e) { + std::string msg = "Invalid input (" + std::string(e.what()) + ")"; + failed(msg, out, indent); + return 1; + } + break; } } @@ -148,7 +143,7 @@ int main(int argc, char **argv) { // Check for command line config // and if present add to qobj config - json_t& config_all = qobj["config"]; + json_t &config_all = qobj["config"]; if (!config.empty()) // NOLINT config_all.update(config.begin(), config.end()); @@ -166,7 +161,7 @@ int main(int argc, char **argv) { // Initialize simulator AER::Controller sim; auto result = sim.execute(qobj).to_json(); - if(myrank == 0){ + if (myrank == 0) { out << result.dump(4) << std::endl; } @@ -179,9 +174,9 @@ int main(int argc, char **argv) { #ifdef AER_MPI MPI_Finalize(); #endif - if(status == "COMPLETED") + if (status == "COMPLETED") return 3; // The simulation was was completed unsuccesfully. - return 2; // Failed to execute the Qobj + return 2; // Failed to execute the Qobj } } catch (std::exception &e) { std::stringstream msg; diff --git a/docs/apidocs/aer.rst b/docs/apidocs/aer.rst index 1cc50e510a..09eb417563 100644 --- a/docs/apidocs/aer.rst +++ b/docs/apidocs/aer.rst @@ -15,4 +15,4 @@ Qiskit Aer API Reference aer_pulse aer_utils aer_quantum_info - parallel + circuit \ No newline at end of file diff --git a/docs/apidocs/circuit.rst b/docs/apidocs/circuit.rst new file mode 100644 index 0000000000..b0d35e6e2f --- /dev/null +++ b/docs/apidocs/circuit.rst @@ -0,0 +1,50 @@ +.. _circuit: + +Additional circuit methods +========================== + +.. currentmodule:: qiskit.circuit + +On import, Aer adds several simulation-specific methods to :class:`~qiskit.circuit.QuantumCircuit` for convenience. +These methods are not available until Aer is imported (``import qiskit_aer``). + +Setting a custom simulator state +-------------------------------- + +The set instructions can also be added to circuits by using the +following ``QuantumCircuit`` methods which are patched when importing Aer. + +.. automethod:: QuantumCircuit.set_density_matrix +.. automethod:: QuantumCircuit.set_stabilizer +.. automethod:: QuantumCircuit.set_unitary +.. automethod:: QuantumCircuit.set_superop +.. automethod:: QuantumCircuit.set_matrix_product_state + +Saving Simulator Data +-------------------------------- + +The save instructions can also be added to circuits by using the +following ``QuantumCircuit`` methods which are patched when importing Aer. + +.. note :: + Each save method has a default label for accessing from the + circuit result data, however duplicate labels in results will result + in an exception being raised. If you use more than 1 instance of a + specific save instruction you must set a custom label for the + additional instructions. + +.. automethod:: QuantumCircuit.save_amplitudes +.. automethod:: QuantumCircuit.save_amplitudes_squared +.. automethod:: QuantumCircuit.save_clifford +.. automethod:: QuantumCircuit.save_density_matrix +.. automethod:: QuantumCircuit.save_expectation_value +.. automethod:: QuantumCircuit.save_expectation_value_variance +.. automethod:: QuantumCircuit.save_matrix_product_state +.. automethod:: QuantumCircuit.save_probabilities +.. automethod:: QuantumCircuit.save_probabilities_dict +.. automethod:: QuantumCircuit.save_stabilizer +.. automethod:: QuantumCircuit.save_state +.. automethod:: QuantumCircuit.save_statevector +.. automethod:: QuantumCircuit.save_statevector_dict +.. automethod:: QuantumCircuit.save_superop +.. automethod:: QuantumCircuit.save_unitary diff --git a/docs/conf.py b/docs/conf.py index 210369c49d..c82f7439e8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,15 +44,14 @@ copyright = f"2017-{datetime.date.today().year}, Qiskit Development Team" # pylint: disable=redefined-builtin author = 'Qiskit Development Team' -import qiskit_sphinx_theme - # The short X.Y version -version = '0.12.0' +version = '0.12.1' # The full version, including alpha/beta/rc tags -release = '0.12.0' +release = '0.12.1' templates_path = ['_templates'] + # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. @@ -72,7 +71,10 @@ 'jupyter_sphinx', 'reno.sphinxext', 'matplotlib.sphinxext.plot_directive', - 'sphinx.ext.intersphinx' + 'sphinx.ext.intersphinx', + 'nbsphinx', + "sphinx_design", + 'qiskit_sphinx_theme' ] # ----------------------------------------------------------------------------- diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000000..725260c52e --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,211 @@ +:orphan: + +############### +Getting started +############### + +Installation +============ +Qiskit Aer depends on the main Qiskit package which has its own +`Qiskit Getting Started `__ detailing the +installation options for Qiskit and its supported environments/platforms. You should refer to +that first. Then the information here can be followed which focuses on the additional installation +specific to Qiskit Aer. + + +.. tab-set:: + + .. tab-item:: Start locally + + The simplest way to get started is to follow the getting started 'Start locally' for Qiskit + here `Qiskit Getting Started `__ + + In your virtual environment where you installed Qiskit simply add ``aer`` to the + extra list in a similar manner to how the extra ``visualization`` support is installed for + Qiskit, i.e: + + .. code:: sh + + pip install qiskit-aer + + **Installing GPU support** + + In order to install and run the GPU supported simulators on Linux, you need CUDA® 10.1 or newer + previously installed. CUDA® itself would require a set of specific GPU drivers. + Please follow CUDA® installation procedure in the NVIDIA® `web `_. + + If you want to install our GPU supported simulators, you have to install this other package: + + .. code:: sh + + pip install qiskit-aer-gpu + + This will overwrite your current qiskit-aer package installation giving you the same functionality found + in the canonical qiskit-aer package, plus the ability to run the GPU supported + simulators: statevector, density matrix, and unitary. + + *Note: This package is only available on x86_64 Linux. + For other platforms that have CUDA support you will have to build from source.* + + + .. tab-item:: Install from source + + + Installing Qiskit Aer from source allows you to access the most recently + updated version under development instead of using the version in the Python Package + Index (PyPI) repository. This will give you the ability to inspect and extend + the latest version of the Qiskit Aer code more efficiently. + + Since Qiskit Aer depends on Qiskit, and its latest changes may require new or changed + features of Qiskit, you should first follow Qiskit's `"Install from source"` instructions + here `Qiskit Getting Started `__ + + .. raw:: html + +

Installing Qiskit Aer from Source

+ + + Clone the ``Qiskit Aer`` repo via *git*. + + .. code:: sh + + git clone https://github.com/Qiskit/qiskit-aer + + The common dependencies can then be installed via *pip*, using the ``requirements-dev.txt`` file, e.g.: + + .. code:: sh + + cd qiskit-aer + pip install -r requirements-dev.txt + + As any other Python package, we can install from source code by just running: + + .. code:: sh + + qiskit-aer$ pip install . + + This will build and install ``Aer`` with the default options which is + probably suitable for most of the users. There’s another Pythonic + approach to build and install software: build the wheels distributable + file. + + .. code:: sh + + qiskit-aer$ pip install build + qiskit-aer$ python -I -m build --wheel + + + See `here `__ + for detailed installation information. + + .. raw:: html + +

Building with GPU support

+ + Qiskit Aer can exploit GPU’s horsepower to accelerate some simulations, + specially the larger ones. GPU access is supported via CUDA® (NVIDIA® + chipset), so to build with GPU support, you need to have CUDA® >= 10.1 + preinstalled. See install instructions + `here `__ Please note + that we only support GPU acceleration on Linux platforms at the moment. + + Once CUDA® is properly installed, you only need to set a flag so the + build system knows what to do: + + .. code:: sh + + AER_THRUST_BACKEND=CUDA + + For example, + + .. code:: sh + + qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_THRUST_BACKEND=CUDA + + See `here `__ + for detailed GPU support information. + + .. raw:: html + +

Building with MPI support

+ + Qiskit Aer can parallelize its simulation on the cluster systems by + using MPI. This can extend available memory space to simulate quantum + circuits with larger number of qubits and also can accelerate the + simulation by parallel computing. To use MPI support, any MPI library + (i.e. OpenMPI) should be installed and configured on the system. + + Qiskit Aer supports MPI both with and without GPU support. Currently + following simulation methods are supported to be parallelized by MPI. + + - statevector + - density_matrix + - unitary + + To enable MPI support, the following flag is needed for build system + based on CMake. + + .. code:: sh + + AER_MPI=True + + For example, + + .. code:: sh + + qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_MPI=True + + See `here `__ + for detailed MPI support information. + + +Simulating your first quantum program with Qiskit Aer +===================================================== +Now that you have Qiskit Aer installed, you can start simulating a quantum circuit. +Here is a basic example: + +.. code:: python + + import qiskit + from qiskit_aer import AerSimulator + + # Generate 3-qubit GHZ state + circ = qiskit.QuantumCircuit(3) + circ.h(0) + circ.cx(0, 1) + circ.cx(1, 2) + circ.measure_all() + + # Construct an ideal simulator + aersim = AerSimulator() + + # Perform an ideal simulation + result_ideal = qiskit.execute(circ, aersim).result() + counts_ideal = result_ideal.get_counts(0) + print('Counts(ideal):', counts_ideal) + # Counts(ideal): {'000': 493, '111': 531} + +Ready to get going?... +====================== + +.. raw:: html + +
+
+ +.. qiskit-call-to-action-item:: + :description: Find out about Qiskit Aer + :header: Dive into the tutorials + :button_link: ./tutorials/index.html + :button_text: Qiskit Aer tutorials + +.. raw:: html + +
+
+ + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` \ No newline at end of file diff --git a/docs/howtos/index.rst b/docs/howtos/index.rst new file mode 100644 index 0000000000..3176842da7 --- /dev/null +++ b/docs/howtos/index.rst @@ -0,0 +1,18 @@ +########################### +Qiskit Aer How-To Guides +########################### + +This section of the documentation provides concrete step-by-step instructions for how to +do specific useful actions in Qiskit Aer. + +.. toctree:: + :caption: How to... + :maxdepth: 1 + :glob: + + * + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/docs/apidocs/parallel.rst b/docs/howtos/parallel.rst similarity index 100% rename from docs/apidocs/parallel.rst rename to docs/howtos/parallel.rst diff --git a/docs/howtos/running_gpu.rst b/docs/howtos/running_gpu.rst new file mode 100644 index 0000000000..31c3c72bb2 --- /dev/null +++ b/docs/howtos/running_gpu.rst @@ -0,0 +1,92 @@ +.. _running_gpu: + +Running with multiple-GPUs and/or multiple nodes +================================================ + +Qiskit Aer parallelizes simulations by distributing quantum states into +distributed memory space. To decrease data transfer between spaces the +distributed states are managed as chunks that is a sub-state for smaller +qubits than the input circuits. + +For example, 30-qubits circuit is distributed into 2^10 chunks with +20-qubits. + +To decrease data exchange between chunks and also to simplify the +implementation, we are applying cache blocking technique. This technique +allows applying quantum gates to each chunk independently without data +exchange, and serial simulation codes can be reused without special +implementation. Before the actual simulation, we apply transpilation to +remap the input circuits to the equivalent circuits that has all the +quantum gates on the lower qubits than the chunk’s number of qubits. And +the (noiseless) swap gates are inserted to exchange data. + +Please refer to this paper (https://arxiv.org/abs/2102.02957) for more +detailed algorithm and implementation of parallel simulation. + +So to simulate by using multiple GPUs or multiple nodes on the cluster, +following configurations should be set to backend options. (If there is +not enough memory to simulate the input circuit, Qiskit Aer +automatically set following options, but it is recommended to explicitly +set them) + +- blocking_enable + +should be set to True for distributed parallelization. (Default = False) + +- blocking_qubits + +this flag sets the qubit number for chunk, should be smaller than the +smallest memory space on the system (i.e. GPU). Set this parameter to +satisfy +``sizeof(complex)*2^(blocking_qubits+4) < size of the smallest memory space`` +in byte. + +Here is an example how we parallelize simulation with multiple GPUs. + +.. code:: python + + sim = AerSimulator(method='statevector', device='GPU') + circ = transpile(QuantumVolume(qubit, 10, seed = 0)) + circ.measure_all() + result = execute(circ, sim, shots=100, blocking_enable=True, blocking_qubits=23).result() + +To run Qiskit Aer with Python script with MPI parallelization, MPI +executer such as mpirun should be used to submit a job on the cluster. +Following example shows how to run Python script using 4 processes by +using mpirun. + +.. code:: sh + + mpirun -np 4 python example.py + +MPI_Init function is called inside Qiskit Aer, so you do not have to +manage MPI processes in Python script. Following metadatas are useful to +find on which process is this script running. + +- num_mpi_processes : shows number of processes using for this + simulation +- mpi_rank : shows zero based rank (process ID) + +Here is an example how to get my rank. + +.. code:: python + + sim = AerSimulator(method='statevector', device='GPU') + result = execute(circuit, sim, blocking_enable=True, blocking_qubits=23).result() + dict = result.to_dict() + meta = dict['metadata'] + myrank = meta['mpi_rank'] + +Multiple shots are also distributed to multiple nodes when setting +``device=GPU`` and ``batched_shots_gpu=True``. The results are +distributed to each processes. + +Note : In the script, make sure that the same random seed should be used +for all processes so that the consistent circuits and parameters are +passed to Qiskit Aer. To do so add following option to the script. + +.. code:: python + + from qiskit.utils import algorithm_globals + algorithm_globals.random_seed = consistent_seed_to_all_processes + diff --git a/docs/index.rst b/docs/index.rst index bd05a9c1bf..4c45d3da72 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,7 +15,10 @@ https://github.com/Qiskit/qiskit-aer .. toctree:: :hidden: + Getting Started API Documentation + Tutorials + How-Tos Release Notes .. Hiding - Indices and tables diff --git a/docs/tutorials/1_aer_provider.ipynb b/docs/tutorials/1_aer_provider.ipynb new file mode 100755 index 0000000000..53d573c801 --- /dev/null +++ b/docs/tutorials/1_aer_provider.ipynb @@ -0,0 +1,856 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Simulators\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This notebook shows how to import the *Qiskit Aer* simulator backend and use it to run ideal (noise free) Qiskit Terra circuits." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:50:28.054060Z", + "start_time": "2019-08-19T16:50:22.255565Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "# Import Qiskit\n", + "from qiskit import QuantumCircuit\n", + "from qiskit import Aer, transpile\n", + "from qiskit.tools.visualization import plot_histogram, plot_state_city\n", + "import qiskit.quantum_info as qi" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Aer Provider\n", + " \n", + "The `Aer` provider contains a variety of high performance simulator backends for a variety of simulation methods. The available backends on the current system can be viewed using `Aer.backends`" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[AerSimulator('aer_simulator'),\n", + " AerSimulator('aer_simulator_statevector'),\n", + " AerSimulator('aer_simulator_density_matrix'),\n", + " AerSimulator('aer_simulator_stabilizer'),\n", + " AerSimulator('aer_simulator_matrix_product_state'),\n", + " AerSimulator('aer_simulator_extended_stabilizer'),\n", + " AerSimulator('aer_simulator_unitary'),\n", + " AerSimulator('aer_simulator_superop'),\n", + " QasmSimulator('qasm_simulator'),\n", + " StatevectorSimulator('statevector_simulator'),\n", + " UnitarySimulator('unitary_simulator'),\n", + " PulseSimulator('pulse_simulator')]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Aer.backends()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The Aer Simulator\n", + " \n", + "The main simulator backend of the Aer provider is the `AerSimulator` backend. A new simulator backend can be created using `Aer.get_backend('aer_simulator')`." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "simulator = Aer.get_backend('aer_simulator')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The default behavior of the `AerSimulator` backend is to mimic the execution of an actual device. If a `QuantumCircuit` containing measurements is run it will return a count dictionary containing the final values of any classical registers in the circuit. The circuit may contain gates, measurements, resets, conditionals, and other custom simulator instructions that will be discussed in another notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simulating a quantum circuit\n", + "\n", + "The basic operation runs a quantum circuit and returns a counts dictionary of measurement outcomes. Here we run a simple circuit that prepares a 2-qubit Bell-state $\\left|\\psi\\right\\rangle = \\frac{1}{\\sqrt{2}}\\left(\\left|0,0\\right\\rangle + \\left|1,1 \\right\\rangle\\right)$ and measures both qubits." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create circuit\n", + "circ = QuantumCircuit(2)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "circ.measure_all()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get counts\n", + "result = simulator.run(circ).result()\n", + "counts = result.get_counts(circ)\n", + "plot_histogram(counts, title='Bell-State counts')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Returning measurement outcomes for each shot\n", + "\n", + "The `QasmSimulator` also supports returning a list of measurement outcomes for each individual shot. This is enabled by setting the keyword argument `memory=True` in the `run`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:50:57.035995Z", + "start_time": "2019-08-19T16:50:57.016437Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['11', '00', '00', '00', '00', '11', '11', '11', '00', '11']\n" + ] + } + ], + "source": [ + "# Run and get memory\n", + "result = simulator.run(circ, shots=10, memory=True).result()\n", + "memory = result.get_memory(circ)\n", + "print(memory)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aer Simulator Options\n", + "\n", + "The `AerSimulator` backend supports a variety of configurable options which can be updated using the `set_options` method. See the `AerSimulator` API documentation for additional details." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simulation Method\n", + "\n", + "The `AerSimulator` supports a variety of simulation methods, each of which supports a different set of instructions. The method can be set manually using `simulator.set_option(method=value)` option, or a simulator backend with a preconfigured method can be obtained directly from the `Aer` provider using `Aer.get_backend`.\n", + "\n", + "When simulating ideal circuits, changing the method between the exact simulation methods `stabilizer`, `statevector`, `density_matrix` and `matrix_product_state` should not change the simulation result (other than usual variations from sampling probabilities for measurement outcomes)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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5FdX9YeGhobFx8dr6qr++jJ8+PCgadOm+Vo6jvrChaCJiIhu0oujL3auy/rnLHdvELeaq8rx48cPG3+eOnWq34kTJxw3btx4ypIxmfvwww+9li9f3mz//v1JVyu3YsUKq7qlHFsAiYiIyKqVlZVZOoR6xwSQiIioEdm1a5dzx44dO7i6ukYPGDCgtVarNf2uX7lypXv79u07ajSaqOjo6PZ79uxxNh7z9/ePePXVV33atm3bUaPRRA0YMKB1YWGhAEB6errdnXfeGarRaKLc3d2jOnfu3K68vNx03oYNGzRr165t8tFHH7XYvHmzh4uLS3S7du06fvbZZx5hYWEdzOObOXOmT+/evUOudg1DhgwJHjFiRFBcXFwbFxeX6JiYmPZnzpyxe+yxxwKbNGkS1apVq7Bdu3aZYn/ppZdaBAYGhru6ukaHhISEffnll00B4MCBA07Tpk1rmZCQ4Obi4hKt0WiijPU//PDDQT169Ah1dnaOjo+P15h3lc+YMaNFp06d2peWlgIA3n777eahoaFhxtejMWACSERE1EgUFxfL0KFDQx988MHzubm5Cffff3/ev//976aAPjF8+umngxcuXHg6Ly8v4bHHHsu+7777QouKikxJzfr16z1/+umn5OPHjx86evSo84IFC5oBwKxZs3x8fX1LcnJyErOyshJnz559TqRiLnT//ffnT5w4MWPAgAF5hYWFB5OSko489NBDF1JTUx0PHDjgZCy3evVqr5EjR56/1rVs3rzZY/bs2edycnISHBwcdF27du0QExNTmJubmzBo0KC8qVOnBhrLhoaGan/77bek/Pz8g9OnT08bO3Zsq9OnT9vHxMQUz5s373RUVNSlwsLCgwUFBQnGczZt2uQ5Y8aM9EuXLh3s27fvJfPnfuONNzIcHBzUCy+84Hfo0CHH2bNn+y9fvvyki4uLqvGb0kAxASQiImokfvnlF9eysjJ55ZVXshwdHdWjjz6aFxERUQgACxcubD5y5MjsXr16Xbazs8PEiRPP29vbq23btrkazx8/fnxmcHBwqY+PT3nfvn0vJiQkOAOAvb29yszMtE9OTnZwdHRU/fv3v2Rjc+0UwtnZWQ0cODD3888/9wKAffv2OZ07d87hwQcfvHitc/v163fh73//e6GLi4saNGjQBUdHR92ECRPO29nZYcSIEXlHjhxxMZZ97LHH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fQDuVUqlVXsyERERETUYNZkF/CGAcgChSqlApVQggDaGfR/WRXBEREREVPtq0gXcB0BPpdQp4w6l1EkRmQTg51qPjIiIiIjqRE3XAazqXsDV3h+YiIiIiBqemiSAPwP4SEQCjTtEJAjA+2ALIBEREdEtoyYJ4CQArgBOishpETkN4IRh36S6CI6IiIiIal9N1gE8KyIxAO4C0N6w+6hSamudREZEREREdeKaLYAicreIpIhIE6W3RSn1kVLqIwC/G471qYdYiYiIiKgWXE8X8AQA85RS+ZUPKKUuAngbwJRajouIiIiI6sj1JICdAFytm3cbgMjaCYeIiIiI6tr1JIDNAeiuclwB8KqdcIiIiIiorl1PApgKfStgdToBOFc74RARERFRXbueBHAzgDdFxLnyARFxAfCGoQwRERER3QKuZxmY2QDuB3BMRBYA+MuwvwP0E0QEwFt1Ex4RERER1bZrJoBKqSwRuQPAIugTPTEeAvAjgKeVUpl1FyIRERER1abrWghaKXUawD9ExANAKPRJYLJSKq8ugyMiIiKi2nfddwIBAEPC93sdxUJERERE9aAm9wImIiIiokaACSARERGRlWECSERERGRlmAASERERWRkmgERERERWhgkgERERkZVhAkhERERkZZgAEhEREVkZJoBEREREVoYJIBEREZGVYQJIREREZGWYABIRERFZGSaARERERFaGCSARERGRlWECSERERGRlmAASERERWRkmgERERERWhgkgERERkZVhAkhERERkZZgAEhEREVkZJoBEREREVoYJIBEREZGVYQJIREREZGWYABIRERFZGSaARERERFaGCSARERGRlWECSERERGRlmAASERERWRkmgERERERWhgkgERERkZVhAkhERERkZZgAEhEREVkZJoBEREREVoYJIBEREZGVYQJIREREZGWYABIRERFZmXpNAEUkTkQ2icg5EVEi8kil418Y9ps//lupjKOIfCQiOSJy2VBfQKUyQSLyneF4joh8KCIO9XCJRERERA1efbcAugH4E8BkAEXVlNkKwNfs8Y9Kx98HMATAQwD+DqAJgHgRsQUAw7+bAWgMxx8CcD+A92rxOoiIiIhuWXb1+WRKqe8BfA/oW/uqKaZVSmVUdUBE3AE8DuBRpdQWw76RAE4DuAvAjwD6AggD0FIpddZQ5nkAS0VkhlIq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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Increase shots to reduce sampling variance\n", + "shots = 10000\n", + "\n", + "# Stabilizer simulation method\n", + "sim_stabilizer = Aer.get_backend('aer_simulator_stabilizer')\n", + "job_stabilizer = sim_stabilizer.run(circ, shots=shots)\n", + "counts_stabilizer = job_stabilizer.result().get_counts(0)\n", + "\n", + "# Statevector simulation method\n", + "sim_statevector = Aer.get_backend('aer_simulator_statevector')\n", + "job_statevector = sim_statevector.run(circ, shots=shots)\n", + "counts_statevector = job_statevector.result().get_counts(0)\n", + "\n", + "# Density Matrix simulation method\n", + "sim_density = Aer.get_backend('aer_simulator_density_matrix')\n", + "job_density = sim_density.run(circ, shots=shots)\n", + "counts_density = job_density.result().get_counts(0)\n", + "\n", + "# Matrix Product State simulation method\n", + "sim_mps = Aer.get_backend('aer_simulator_matrix_product_state')\n", + "job_mps = sim_mps.run(circ, shots=shots)\n", + "counts_mps = job_mps.result().get_counts(0)\n", + "\n", + "plot_histogram([counts_stabilizer, counts_statevector, counts_density, counts_mps],\n", + " title='Counts for different simulation methods',\n", + " legend=['stabilizer', 'statevector',\n", + " 'density_matrix', 'matrix_product_state'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Automatic Simulation Method\n", + "The default simulation method is `automatic` which will automatically select a one of the other simulation methods for each circuit based on the instructions in those circuits. A fixed simulation method can be specified by by adding the method name when getting the backend, or by setting the `method` option on the backend." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### GPU Simulation\n", + "\n", + "The `statevector`, `density_matrix` and `unitary` simulators support running on a NVidia GPUs. For these methods the simulation device can also be manually set to CPU or GPU using `simulator.set_options(device='GPU')` backend option. If a GPU device is not available setting this option will raise an exception." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit_aer import AerError\n", + "\n", + "# Initialize a GPU backend\n", + "# Note that the cloud instance for tutorials does not have a GPU\n", + "# so this will raise an exception.\n", + "try:\n", + " simulator_gpu = Aer.get_backend('aer_simulator')\n", + " simulator_gpu.set_options(device='GPU')\n", + "except AerError as e:\n", + " print(e)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Aer` provider will also contain preconfigured GPU simulator backends if Qiskit Aer was installed with GPU support on a compatible system:\n", + "\n", + "* `aer_simulator_statevector_gpu`\n", + "* `aer_simulator_density_matrix_gpu`\n", + "* `aer_simulator_unitary_gpu`\n", + "\n", + "*Note: The GPU version of Aer can be installed using* `pip install qiskit-aer-gpu`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Simulation Precision\n", + "\n", + "One of the available simulator options allows setting the float precision for the `statevector`, `density_matrix`, `unitary` and `superop` methods. This is done using the `set_precision=\"single\"` or `precision=\"double\"` (default) option:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'11': 491, '00': 533}\n" + ] + } + ], + "source": [ + "# Configure a single-precision statevector simulator backend\n", + "simulator = Aer.get_backend('aer_simulator_statevector')\n", + "simulator.set_options(precision='single')\n", + "\n", + "# Run and get counts\n", + "result = simulator.run(circ).result()\n", + "counts = result.get_counts(circ)\n", + "print(counts)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Setting the simulation precision applies to both CPU and GPU simulation devices. Single precision will halve the required memory and may provide performance improvements on certain systems." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Custom Simulator Instructions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Saving the simulator state\n", + "\n", + "The state of the simulator can be saved in a variety of formats using custom simulator instructions.\n", + "\n", + "\n", + "| Circuit method | Description |Supported Methods | \n", + "|----------------|-------------|------------------|\n", + "| `save_state` | Save the simulator state in the native format for the simulation method | All | \n", + "| `save_statevector` | Save the simulator state as a statevector | `\"automatic\"`, `\"statevector\"`, `\"matrix_product_state\"`, `\"extended_stabilizer\"`|\n", + "| `save_stabilizer` | Save the simulator state as a Clifford stabilizer | `\"automatic\"`, `\"stabilizer\"`| \n", + "| `save_density_matrix` | Save the simulator state as a density matrix | `\"automatic\"`, `\"statevector\"`, `\"matrix_product_state\"`, `\"density_matrix\"` |\n", + "| `save_matrix_product_state` | Save the simulator state as a a matrix product state tensor | `\"automatic\"`, `\"matrix_product_state\"`|\n", + "| `save_unitary` | Save the simulator state as unitary matrix of the run circuit | `\"automatic\"`, `\"unitary\"`|\n", + "| `save_superop` | Save the simulator state as superoperator matrix of the run circuit | `\"automatic\"`, `\"superop\"`|\n", + "\n", + "Note that these instructions are only supported by the Aer simulator and will result in an error if a circuit containing them is run on a non-simulator backend such as an IBM Quantum device." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving the final statevector\n", + "\n", + "To save the final statevector of the simulation we can append the circuit with the `save_statevector` instruction. Note that this instruction should be applied *before* any measurements if we do not want to save the collapsed post-measurement state" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Construct quantum circuit without measure\n", + "circ = QuantumCircuit(2)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "circ.save_statevector()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get statevector\n", + "result = simulator.run(circ).result()\n", + "statevector = result.get_statevector(circ)\n", + "plot_state_city(statevector, title='Bell state')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving the circuit unitary\n", + "\n", + "To save the unitary matrix for a `QuantumCircuit` we can append the circuit with the `save_unitary` instruction. Note that this circuit cannot contain any measurements or resets since these instructions are not supported on for the `\"unitary\"` simulation method" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Circuit unitary:\n", + " [[ 0.70711+0.j 0.70711-0.j 0. +0.j 0. +0.j]\n", + " [ 0. +0.j 0. +0.j 0.70711+0.j -0.70711+0.j]\n", + " [ 0. +0.j 0. +0.j 0.70711+0.j 0.70711-0.j]\n", + " [ 0.70711+0.j -0.70711+0.j 0. +0.j 0. +0.j]]\n" + ] + } + ], + "source": [ + "# Construct quantum circuit without measure\n", + "circ = QuantumCircuit(2)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "circ.save_unitary()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get unitary\n", + "result = simulator.run(circ).result()\n", + "unitary = result.get_unitary(circ)\n", + "print(\"Circuit unitary:\\n\", np.asarray(unitary).round(5))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Saving multiple states\n", + "\n", + "We can also apply save instructions at multiple locations in a circuit. Note that when doing this we must provide a unique label for each instruction to retrieve them from the results" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'psi_3': Statevector([0.58778525+0.j , 0. -0.80901699j],\n", + " dims=(2,)),\n", + " 'psi_2': Statevector([0.95105652+0.j , 0. -0.30901699j],\n", + " dims=(2,)),\n", + " 'psi_5': Statevector([-1.+0.00000000e+00j, 0.-2.77555756e-16j],\n", + " dims=(2,)),\n", + " 'psi_1': Statevector([1.+0.j, 0.+0.j],\n", + " dims=(2,)),\n", + " 'psi_4': Statevector([-0.30901699+0.j , 0. -0.95105652j],\n", + " dims=(2,)),\n", + " 'psi_0': Statevector([1.+0.j, 0.+0.j],\n", + " dims=(2,))}" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Construct quantum circuit without measure\n", + "steps = 5\n", + "circ = QuantumCircuit(1)\n", + "for i in range(steps):\n", + " circ.save_statevector(label=f'psi_{i}')\n", + " circ.rx(i * np.pi / steps, 0)\n", + "circ.save_statevector(label=f'psi_{steps}')\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get saved data\n", + "result = simulator.run(circ).result()\n", + "data = result.data(0)\n", + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Setting the simulator to a custom state\n", + "\n", + "The `AerSimulator` allows setting a custom simulator state for several of its simulation methods using custom simulator instructions\n", + "\n", + "| Circuit method | Description |Supported Methods | \n", + "|----------------|-------------|------------------|\n", + "| `set_statevector` | Set the simulator state to the specified statevector | `\"automatic\"`, `\"statevector\"`, `\"density_matrix\"`|\n", + "| `set_stabilizer` | Set the simulator state to the specified Clifford stabilizer | `\"automatic\"`, `\"stabilizer\"`| \n", + "| `set_density_matrix` | Set the simulator state to the specified density matrix | `\"automatic\"`, `\"density_matrix\"` |\n", + "| `set_unitary` | Set the simulator state to the specified unitary matrix | `\"automatic\"`, `\"unitary\"`, `\"superop\"`|\n", + "| `set_superop` | Set the simulator state to the specified superoperator matrix | `\"automatic\"`, `\"superop\"`|\n", + "\n", + "\n", + "**Notes:**\n", + "* These instructions must be applied to all qubits in a circuit, otherwise an exception will be raised.\n", + "* The input state must also be a valid state (statevector, density matrix, unitary etc) otherwise an exception will be raised.\n", + "* These instructions can be applied at any location in a circuit and will override the current state with the specified one. Any classical register values (e.g. from preceding measurements) will be unaffected\n", + "* Set state instructions are only supported by the Aer simulator and will result in an error if a circuit containing them is run on a non-simulator backend such as an IBM Quantum device." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting a custom statevector\n", + "\n", + "The `set_statevector` instruction can be used to set a custom `Statevector` state. The input statevector must be valid ($|\\langle\\psi|\\psi\\rangle|=1$)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'statevector': Statevector([ 0.18572453-0.03102771j, -0.26191269-0.18155865j,\n", + " 0.12367038-0.47837907j, 0.66510011-0.4200986j ],\n", + " dims=(2, 2))}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Generate a random statevector\n", + "num_qubits = 2\n", + "psi = qi.random_statevector(2 ** num_qubits, seed=100)\n", + "\n", + "# Set initial state to generated statevector\n", + "circ = QuantumCircuit(num_qubits)\n", + "circ.set_statevector(psi)\n", + "circ.save_state()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get saved data\n", + "result = simulator.run(circ).result()\n", + "result.data(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Using the initialize instruction\n", + "\n", + "It is also possible to initialize the simulator to a custom statevector using the `initialize` instruction. Unlike the `set_statevector` instruction this instruction is also supported on real device backends by unrolling to reset and standard gate instructions." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'statevector': Statevector([ 0.18572453-0.03102771j, -0.26191269-0.18155865j,\n", + " 0.12367038-0.47837907j, 0.66510011-0.4200986j ],\n", + " dims=(2, 2))}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Use initilize instruction to set initial state\n", + "circ = QuantumCircuit(num_qubits)\n", + "circ.initialize(psi, range(num_qubits))\n", + "circ.save_state()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get result data\n", + "result = simulator.run(circ).result()\n", + "result.data(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting a custom density matrix\n", + "\n", + "The `set_density_matrix` instruction can be used to set a custom `DensityMatrix` state. The input density matrix must be valid ($Tr[\\rho]=1, \\rho \\ge 0$)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'density_matrix': DensityMatrix([[ 0.2075308 +0.j , 0.13161422-0.01760848j,\n", + " 0.0442826 +0.07742704j, 0.04852053-0.01303171j],\n", + " [ 0.13161422+0.01760848j, 0.20106116+0.j ,\n", + " 0.02568549-0.03689812j, 0.0482903 -0.04367912j],\n", + " [ 0.0442826 -0.07742704j, 0.02568549+0.03689812j,\n", + " 0.39731492+0.j , -0.01114025-0.13426423j],\n", + " [ 0.04852053+0.01303171j, 0.0482903 +0.04367912j,\n", + " -0.01114025+0.13426423j, 0.19409312+0.j ]],\n", + " dims=(2, 2))}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num_qubits = 2\n", + "rho = qi.random_density_matrix(2 ** num_qubits, seed=100)\n", + "circ = QuantumCircuit(num_qubits)\n", + "circ.set_density_matrix(rho)\n", + "circ.save_state()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get saved data\n", + "result = simulator.run(circ).result()\n", + "result.data(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting a custom stabilizer state\n", + "\n", + "The `set_stabilizer` instruction can be used to set a custom `Clifford` stabilizer state. The input stabilizer must be a valid `Clifford`." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'stabilizer': StabilizerState(StabilizerTable: ['+ZZ', '-IZ'])}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Generate a random Clifford C\n", + "num_qubits = 2\n", + "stab = qi.random_clifford(num_qubits, seed=100)\n", + "\n", + "# Set initial state to stabilizer state C|0>\n", + "circ = QuantumCircuit(num_qubits)\n", + "circ.set_stabilizer(stab)\n", + "circ.save_state()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get saved data\n", + "result = simulator.run(circ).result()\n", + "result.data(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Setting a custom unitary\n", + "\n", + "The `set_unitary` instruction can be used to set a custom unitary `Operator` state. The input unitary matrix must be valid ($U^\\dagger U=\\mathbb{1}$)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'unitary': Operator([[-0.44885724-0.26721573j, 0.10468034-0.00288681j,\n", + " 0.4631425 +0.15474915j, -0.11151309-0.68210936j],\n", + " [-0.37279054-0.38484834j, 0.3820592 -0.49653433j,\n", + " 0.14132327-0.17428515j, 0.19643043+0.48111423j],\n", + " [ 0.2889092 +0.58750499j, 0.39509694-0.22036424j,\n", + " 0.49498355+0.2388685j , 0.25404989-0.00995706j],\n", + " [ 0.01830684+0.10524311j, 0.62584001+0.01343146j,\n", + " -0.52174025-0.37003296j, 0.12232823-0.41548904j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Generate a random unitary\n", + "num_qubits = 2\n", + "unitary = qi.random_unitary(2 ** num_qubits, seed=100)\n", + "\n", + "# Set initial state to unitary\n", + "circ = QuantumCircuit(num_qubits)\n", + "circ.set_unitary(unitary)\n", + "circ.save_state()\n", + "\n", + "# Transpile for simulator\n", + "simulator = Aer.get_backend('aer_simulator')\n", + "circ = transpile(circ, simulator)\n", + "\n", + "# Run and get saved data\n", + "result = simulator.run(circ).result()\n", + "result.data(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:54:58.630868Z", + "start_time": "2019-08-19T16:54:58.624544Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
qiskit-terra0.24.0.dev0+dba2eff
qiskit-aer0.11.2
qiskit-ignis0.7.1
qiskit-ibmq-provider0.20.0
qiskit0.41.0
System information
Python version3.8.11
Python compilerClang 12.0.5 (clang-1205.0.22.11)
Python builddefault, Jul 27 2021 10:46:38
OSDarwin
CPUs8
Memory (Gb)64.0
Wed Feb 15 14:35:41 2023 JST
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/2_device_noise_simulation.ipynb b/docs/tutorials/2_device_noise_simulation.ipynb new file mode 100755 index 0000000000..aef129393c --- /dev/null +++ b/docs/tutorials/2_device_noise_simulation.ipynb @@ -0,0 +1,305 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Device backend noise model simulations\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This notebook shows how to use the Qiskit Aer `noise` module to automatically generate a basic noise model for an IBMQ hardware device, and use this model to do noisy simulations of `QuantumCircuits` to study the effects of errors which occur on real devices.\n", + "\n", + "Note, that these automatic models are only an *approximation* of the real errors that occur on actual devices, due to the fact that they must be build from a limited set of input parameters related to *average error rates* on gates. The study of quantum errors on real devices is an active area of research and we discuss the Qiskit Aer tools for configuring more detailed noise models in another notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:57:28.062507Z", + "start_time": "2019-08-19T16:57:24.202560Z" + } + }, + "outputs": [], + "source": [ + "from qiskit import IBMQ, transpile\n", + "from qiskit import QuantumCircuit\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit.tools.visualization import plot_histogram" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Device Backend Noise Model\n", + "\n", + "The *Qiskit Aer* device noise model automatically generates a simplified noise model for a real device. This model is generated using the calibration information reported in the `BackendProperties` of a device and takes into account\n", + "\n", + "* The *gate_error* probability of each basis gate on each qubit.\n", + "* The *gate_length* of each basis gate on each qubit.\n", + "* The $T_1$, $T_2$ relaxation time constants of each qubit.\n", + "* The readout error probability of each qubit.\n", + "\n", + "### Fake Provider Backends\n", + "\n", + "We will use real noise data for an IBM Quantum device using the data stored in Qiskit Terra. Specifically, in this tutorial, the device is `ibmq_vigo`." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:57:32.162207Z", + "start_time": "2019-08-19T16:57:32.159466Z" + } + }, + "outputs": [], + "source": [ + "from qiskit.providers.fake_provider import FakeVigo\n", + "device_backend = FakeVigo()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Test circuit for device and simulation comparison\n", + "\n", + "Now we construct a test circuit to compare the output of the real device with the noisy output simulated on the Qiskit Aer `AerSimulator`. We will prepare a 3-qubit GHZ state $\\frac{1}{\\sqrt2}(|0,0,0\\rangle + |1,1,1\\rangle)$ on qubits 0, 1 and 2. Before running with noise or on the device we show the ideal expected output with no noise." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:57:32.330124Z", + "start_time": "2019-08-19T16:57:32.175615Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAc0AAAFTCAYAAABbKVcuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8qNh9FAAAACXBIWXMAAAsTAAALEwEAmpwYAAArMElEQVR4nO3de7xVdZ3/8dcbjh4xILkIyEUBuYxAongMUQQzycZGposjmqaOqZnlJTO1UtOmnPxZJuqYRU2gNmrqZHRRMQ0Z9IQdKBIwxAAF5CIXBQQOcvj8/ljr4GZzLmvD5tx4Px+P82Cf7/qutT+Lvff57O/6XpYiAjMzM6tfq8YOwMzMrLlw0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTmhRJvSWFpJIiHGuxpFOKEVdjk9RG0m8kvSPp0caOp1Dpa9qvlm3nSJrS0DGZ7Q4nTWtwLSmZFULSREnf2c3dzwC6Ap0i4t+KEMsgSRWS1qU/f5A0aE+Puzsi4hcR8bGc2GpNsDl1DpE0QdKbkjZKWpj+//5Tur3GL1+5r4Gk+9J9c382pfuNKuQcJE2VdFEB9W+W9GAhz2FNg5OmWfNwGPBqRGwrdMdaWu1vkiTijkBnYDLw8B5F2EAkdQJeBA4ETgTaAcOA54ExWY8TEZdGRNvcH+B/gT8CLxQ/cmsJnDStUUlqLen7klZLWgh8Im/7ByX9TNJyScskfUdS63Tb4ZKek7Qm3f8Xkg7K+LxtJP1A0uvpJc/pktqk28ZKmivp7bQFcUTOfju1gvJaLidJWirpq5JWpTH/e7rtEuAc4Nq0RfObtPy69Lw2SJov6aM1xHoLcBMwLt3385JaSbohjX+VpPslfTCtX93K+rykN4Dn8o8ZEW9HxOJIlgQTUAXU17r7WnpOb0q6MPf/Ir+lJekCSdPzDnFa2iJcLel2Sa3y60qaltadnZ7ruBpC+QqwHvhcRPwjEm9HxM8j4u66zqGe8/si8BHg7IioqmH7AZIeTN9vb0v6s6Sukr5LkrzvSWO+J60/XtISSeslzZR0Ylr+ceAbvP96zk7La32vW9Oxx/1GZnvoYuBfgKOBd4HH87ZPBFaR/EH/APBbYAnwY5I/9v8JTAPap/veDFyV4Xm/DwwGjgdWAMOB7ZIGAA8BnwSmkvyB/o2kQRGxNcNxuwEfBHqQtHoek/RERPxE0vHA0oi4AUDSQODLwLER8aak3sAufyQj4luSAugXEeem+14IXEDyR34VcD9wD/C5nF1HA0cA22sLVtLbQFuSL9A31VHv48A1wEeBRcCE+v4javApoCx9vj8A84Gf5laIiFHpuQ6NiNdqOc4pwK8iotbzKpSkY4HbgY9HxMpaqp1P8tr2AiqBo4DNEfFNSScAD0ZE7vn8Gfg28A5wJfCopN4R8ZSkW8l5PVMTqf29bk2EW5rW2M4E7oyIJRGxliQJAiCpK3AacFVEvBsRq4AfAmcBRMRrEfFMRFRGxFvAHSSJok5pC+dC4MqIWBYRVRHxYkRUAuOA36XHfY8kubYhSa5ZvAd8OyLei4jfAxuBgbXUrQJKgUGS9ktbfv/I+DznAHdExMKI2Ah8HTgr71Lszen/2+baDhIRB5Ekgi8Df6nj+c4Efh4RcyLiXZIvJ4W6LSLWRsQbwJ3A2btxDEguJ6+o/iW9MvB22lrPH1C0Ot32dvoF4bP5B5PUEXgUuCki8lvHud4DOpEku6qImBkR62urHBEPRsSaiNgWET8gea1rfC/U9163psMtTWts3Um+TVd7PefxYcB+wHJJ1WWtquunf2jG836/VitgXYbn7AwcANSUoLrnxhAR2yUtIWk5ZrEmr99xE0nLahcR8Zqkq0gS0GBJTwNXR8SbGZ5npzjTxyUkg4WqLSGDiHhX0n3AW+ml6AOAeTnb26bPNzPv+QqV/zp3341jAKwBDqn+JSImAwell4fPzavbOff1kDQxd6OSN9aDQEVE3FHP8z5A0sp8WEk3wIPAN9MvV7uQdA3weZLzDJKrIZ1rOXad73VrOtzStMa2nOQPUbVDcx4vIbkM1jkiDkp/2kfE4HT7rSR/jD4UEe1J/mCK+q0GtgCH17DtTZI/YMCOP6q9gGVp0SaSASjVumV4vmq73FIoIv4nIkamzxnAbRmPtVOcJP9v24DcS4uF3MKoFcl59YiIN/IGx0DdrxMkl9br+3/J3z/Ll4OaPAt8srpPdA/dQHI59ML6KqZXD26JiEEkVx7+BTivenNu3bT/8lqSFnqHtEX/Du+/P/Nfm/re69ZEOGlaY/slcIWknpI6ANdXb4iI5cAU4AeS2qeDXw6XVH0Jth3J5c93JPUAvpblCdO+sP8G7pDUXclgpBGSStN4PiHpo5L2A75K8sfsxXT3vwKfTff5OBkuB+dYCfSt/kXSQEknp8+7BdhMHf2PeR4CviKpj6S2JF8gHsk6ulbSGElHp+fRnuTS9jrglVp2+SVwgZKpKgcC38rb/lfg05IOTAcHfb6GY3xNUgdJvUj6+B6p5bl2+n+qwR1AB+CB9P0gSe1I+hgzUzLt6VrgM3VdZs2p/xFJH0oH56wnuVxb/Xrlx9yO5EvMW0CJpJtIWprk1O9dnfgzvNetiXDStMY2AXgamA3MIhnyn+s8YH+Sy4XrgMd4/9LcLSRTDd4BflfDvnW5BniZZLDGWpIWXquImE/SYr2bpEV6OnB6ziCgK9Oyt0n6FZ8o4Dl/RtJ/+bakJ0j6uL6XPs8KoAtJ32QW/01yuXAaycCcLcDlBcRyEEnifYfkMvXhJINgttRUOSKeJOmHfA54jV1H5P4Q2EqSDCYBv6jhML8mucT7V5LX62e1xHYzMCn9fzqzhlhWA8eRnPN0YEN6zHbAF2s5Zk2+QdJfXa5d52ueU0P9biTvv/UkXy6eJ3kNIOkmOEPJnNe7SN7TTwGvklyK3sLOl1qrF6hYI2lW+riu97o1EfJNqM1sd6SjXPvXMcrVrMVxS9PMzCwjJ00zM7OMfHnWzMwsI7c0zczMMnLSNDMzy6jBVwSSdBnJfLpDgLkky0b9Xx319yeZgPw5kpU1VgLfj4i7cup8BvgPkmHz/yBZpeNX9cXSuXPn6N279+6fjJmZtTgzZ85cHREH17StQZOmkjsWjAcuI5lfdRnwZLoY9hu17PYw0BO4BFhAskxYm5xjjiCZJP0tknl6nyZZGPmEiJhRVzy9e/emoqJiz07KzMxaFEm1LhPZoAOBJM0A/hYRF+eULQAei4hdJnVL+hjJJODD0wnNNR3zEaBjRIzJKfsD8FZE1LkgdFlZWThpmplZLkkzI6Kspm0N1qeZXmY9hmSpqFxTqP0OEp8kWbHlaiX3KVwg6a502bBqI2o45tN1HNPMzGy3NOTl2c4k9wrMv1fdSpL749WkLzCSZO3Pz5As/XU3Sd/mGWmdbrUcs5CFtM3MzOrV1G8N1orkbgCfjYh3ACR9GXhaUtc6bhZbK0mXkPSP0r17d6ZOnQpA3759adeuHbNnzwagU6dODB48mGnTkhvJl5SUMHLkSGbNmsX69cnazmVlZaxcuZIlS5IlJfv3709paSlz5swBoEuXLgwYMIDp05Nb9JWWljJixAgqKirYuHEjAMOHD2fp0qUsW5bcRGPgwIG0bt2aefOSOzN169aNPn36UF5eDkCbNm0YPnw4M2bMYPPm5DaJI0aMYNGiRaxYkdxicNCgQVRVVTF//nwAevToQc+ePZkxI+nibdu2LWVlZZSXl1NZWQnAyJEjefXVV1m1ahUAQ4YMobKykgULFgDQq1cvunbtuqMPuH379gwbNozp06ezbVuyRvioUaOYO3cua9asAWDo0KFs2LCBhQsXAkkfcseOHZk1K1lqs0OHDgwdOpTnn3+eiEASo0ePZvbs2axbl9zha9iwYaxdu5bFixf7dfLr5NfJr1ODvE51abA+zfTy7Cbg7Ih4NKf8v4AhEbHLav6SJgEnRES/nLJewBvAhyPiz5LeAO6OiNtz6nwN+HJEHJZ/zFzu0zQzs3xNok8zvUvETGBM3qYxvH/bpXwvAN3z+jAHpP9Wj24qL/CYZmZmu6WhFze4g+SefBdJOkLSeJL+yfsAJN0v6f6c+v9Dcpf2n0saLOkEkikrj0XEqrTOeOBkSddL+idJXwc+QnIbIzMzs6Jp0D7NiHhEUieSxQoOAeYAp0VEdavx0Lz6G9Mbxd5NMop2Hcn9C3NvVPyipLOA7wDfJlncYFx9czTNzMwKtU8v2O4+TTMzy9ck+jTNzMyaOydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNM7Nm4qmnnmLgwIH069eP733ve7tsnzhxIgcffDBHHXUURx11FD/96U93bJs0aRL9+/enf//+TJo0aUf5I488wpFHHsngwYO57rrrGuQ8mrMGvZ+mmZntnqqqKr70pS/xzDPP0LNnT4499ljGjh3LoEGDdqo3btw47rnnnp3K1q5dyy233EJFRQWSOOaYYxg7dizbt2/na1/7GjNnzuTggw/m/PPP59lnn+WjH/1oQ55as+KWpplZM/DSSy/Rr18/+vbty/77789ZZ53Fr3/960z7Pv3004wZM4aOHTvSoUMHxowZw1NPPcXChQvp378/Bx98MACnnHIKjz/++N48jWbPSdPMrBlYtmwZvXr12vF7z549WbZs2S71Hn/8cY488kjOOOMMlixZUue+/fr1Y/78+SxevJht27bxxBNP7NjHauakaWbWQpx++uksXryYv/3tb4wZM4bzzz+/zvodOnTgRz/6EePGjePEE0+kd+/etG7duoGibZ6cNM3MmoEePXrs1ApcunQpPXr02KlOp06dKC0tBeCiiy5i5syZ9e57+umnM2PGDMrLyxk4cCADBgzY26fSrDlpmpk1A8ceeywLFixg0aJFbN26lYcffpixY8fuVGf58uU7Hk+ePJkjjjgCgFNPPZUpU6awbt061q1bx5QpUzj11FMBWLVqFQDr1q3j3nvv5aKLLmqgM2qePHrWzKwZKCkp4Z577uHUU0+lqqqKCy+8kMGDB3PTTTdRVlbG2LFjueuuu5g8eTIlJSV07NiRiRMnAtCxY0duvPFGjj32WABuuukmOnbsCMCVV17J7Nmzd5S7pVk3RURjx9BoysrKoqKiorHDaFRPPfUUV155JVVVVVx00UVcf/31NdZ7/PHHOeOMM/jzn/9MWVkZW7du5Qtf+AIVFRW0atWK8ePHc9JJJwHwzW9+k/vvv59169axcePGBjwbM7M9J2lmRJTVtM2XZ/dh1fO+nnzySebNm8dDDz3EvHnzdqm3YcMGxo8fz/Dhw3eUTZgwAYCXX36ZZ555hq9+9ats374dSPpIXnrppYY5CTOzBuSkuQ/LOu/rxhtv5LrrruOAAw7YUTZv3jxOPvlkALp06cJBBx1Edav9uOOO45BDDmmYkzAza0BOmvuwLPO+Zs2axZIlS/jEJz6xU/nQoUOZPHky27ZtY9GiRcycOdPzu8ysxfNAIKvV9u3bufrqq3cMJsh14YUX8sorr1BWVsZhhx3G8ccf7/ldZtbiOWnuw+qb97VhwwbmzJmzY4DPihUrGDt2LJMnT6asrIwf/vCHO+oef/zxHnVnZi2eL8/uw+qb9/XBD36Q1atXs3jxYhYvXsxxxx23I2Fu2rSJd999F4BnnnmGkpKSXRaONjNraZw092G5876OOOIIzjzzzB3zviZPnlznvqtWrWLYsGEcccQR3HbbbTzwwAM7tl177bX07NmTTZs20bNnT26++ea9fCZmZg3D8zT38XmaZma2M8/TNDMzKwInTTMzs4ycNM3MzDJy0jQzM8vI8zTNbJ9z8Z2NHYHtDROu2vvP4ZammZlZRk6aZmZmGTlpmpmZZeSkaWZmlpGTppmZWUZOmmZmZhk5aZqZmWXkeZpF4DlfLVNDzPkys+bFLU0zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMGjxpSrpM0iJJWyTNlHRixv1GStomaU5e+QWSooafA/bOGZiZ2b6qQZOmpHHAeOBW4GjgReBJSYfWs18H4H7g2VqqbAIOyf2JiC3FitvMzAwavqV5NTAxIiZExCsRcTmwHPhiPfv9DJgElNeyPSJiRe5PEWM2MzMDGjBpStofOAaYkrdpCnB8HftdBnQFvlPH4dtIel3SUkm/lXT0HgdsZmaWpyFbmp2B1sDKvPKVQLeadpD0IeBbwLkRUVXLcecDFwL/CpwNbAFekNS/GEGbmZlVK2nsAGojqRR4BLgmIhbVVi8iysm5bCvpReCvwOXAFTUc9xLgEoDu3bszdepUAPr27Uu7du2YPXs2AJ06dWLw4MFMmzYNgJKSEkaOHMmsWbNYv349AGVlZaxcuRI4fE9P15qgiooKNm7cCMDw4cNZunQpy5YtA2DgwIG0bt2aefPmAdCtWzf69OlDeXnyVmzTpg3Dhw9nxowZbN68GYARI0awaNEiVqxIeg8GDRpEVVUV8+fPB6BHjx707NmTGTNmANC2bVvKysooLy+nsrISgJEjR/Lqq6+yatUqAIYMGUJlZSULFiwAoFevXnTt2pWKigoA2rdvz7Bhw5g+fTrbtm0DYNSoUcydO5c1a9YAMHToUDZs2MDChQsB6N27Nx07dmTWrFkAdOjQgaFDh/L8888TEUhi9OjRzJ49m3Xr1gEwbNgw1q5dy+LFi4E9+zwtWbIEgP79+1NaWsqcOcnYvy5dujBgwACmT58OQGlpKSNGjNit1wlKC3w3WHOwfPnyonye6qKI2IunkPNEyeXZTcDZEfFoTvl/AUMiYnRe/d7AIiC3hdkKUFp2WkTkX+qt3vfnQLeI+Oe6YiorK4vqPy574uI79/gQ1gRNuKqxI7C9xZ/ZlqlYn1lJMyOirKZtDXZ5NiK2AjOBMXmbxpCMos23DPgQcFTOz33Aa+njmvZBkoAjSQYYmZmZFU1DX569A3hA0kvAC8ClQHeSZIik+wEi4ryIeA/In5O5CqiMiDk5Zd8C/gQsANqTXJI9kvpH5JqZmRWkQZNmRDwiqRNwA8l8yjkkl1lfT6vUOV+zFgcBPyEZTPQO8BdgVES8tOcRm5mZva/BBwJFxL3AvbVsO6mefW8Gbs4r+wrwleJEZ2ZmVjuvPWtmZpaRk6aZmVlGTppmZmYZOWmamZll5KRpZmaWkZOmmZlZRk6aZmZmGTlpmpmZZeSkaWZmlpGTppmZWUZOmmZmZhk5aZqZmWXkpGlmZpaRk6aZmVlGTppmZmYZOWmamZll5KRpZmaWkZOmmZlZRgUlTUmtJLXK+b2bpIsknVD80MzMzJqWQluavwMuB5DUFqgAbgemSjqvyLGZmZk1KYUmzTLgufTxp4H1QBfgYuCaIsZlZmbW5BSaNNsCb6ePPwb8KiLeI0mkhxcxLjMzsyan0KT5BnCCpA8ApwLPpOUdgU3FDMzMzKypKSmw/h3AA8BG4HVgWlo+Cni5iHGZmZk1OQUlzYj4saSZQC/gmYjYnm76B3BjsYMzMzNrSgptaRIRFSSjZnPLfle0iMzMzJqoghc3kHSZpLmSNknqm5ZdJ+nM4odnZmbWdBS6uMFVwA3ATwDlbHoT+HLxwjIzM2t6Cm1pXgpcHBHjgW055bOAwUWLyszMrAkqNGkeBsypofw9oM2eh2NmZtZ0FZo0FwLDaig/DZi35+GYmZk1XYWOnv0+cI+kA0n6NEdI+hxwLXBhsYMzMzNrSgqdp/lzSSXArcCBJAsdvAlcERGP7IX4zMzMmozdmac5AZggqTPQKiJWFT8sMzOzpqfgpFktIlYXMxAzM7Omrt6kKelvwOiIWCfpZSBqqxsRRxYzODMzs6YkS0vzcaAy53GtSdPMzKwlqzdpRsQtOY9v3qvRmJmZNWGFLqP3nKSDaihvL+m5okVlZmbWBBW6uMFJwP41lB8AnLjH0ZiZmTVhmUbPSspdBehISWtzfm8NnAosK2ZgZmZmTU3WKScVJAOAAphSw/bNwOXFCsrMzKwpypo0+5Asm7cQ+DDwVs62rcCqiKgqcmxmZmZNSqakGRGvpw8Lvmm1mZlZS5FlcYNPA7+JiPfSx7WKiP8tWmRmZmZNTJaW5mNAN2BV+rg2QTIoyMzMrEXKsrhBq5oem5mZ7WucBM3MzDLK2qeZifs0zcysJcvap5mF+zTNzKxFK6hP08zMbF/mhGhmZpaR52mamZll5HmaZmZmGXmeppmZWUZOgmZmZhkVnDQlDZN0v6SK9OeBvPtt1rf/ZZIWSdoiaaakWm9eLWm0pBclrZG0WdLfJV1TQ73PSJonqTL991OFnpeZmVl9Ckqaks4B/gwcAvw+/ekKvCTp3Az7jwPGA7cCRwMvAk9KOrSWXTYCdwGjgEHAd4BbJF2Wc8wRwCPAL4Cj0n8flTS8kHMzMzOrT9b7aVb7LnBjRNyaWyjp6yQJ7cF69r8amBgRE9LfL5f0ceCLwNfzK0fETGBmTtGidATvicC9adlVwB8j4rvVMUr6SFp+dsbzMjMzq1ehl2cPBn5ZQ/mjQJe6dpS0P3AMMCVv0xTg+CxPLunotO7zOcUjajjm01mPaWZmllWhLc0/AicBr+WVn8TOiawmnUmmpKzMK18JnFLXjpKWkiTsEuCWiLgvZ3O3Wo7ZrZZjXQJcAtC9e3emTp0KQN++fWnXrh2zZ88GoFOnTgwePJhp06YBUFJSwsiRI5k1axbr168HoKysjJUrVwKH1xW+NVMVFRVs3LgRgOHDh7N06VKWLVsGwMCBA2ndujXz5s0DoFu3bvTp04fy8nIA2rRpw/Dhw5kxYwabN28GYMSIESxatIgVK1YAMGjQIKqqqpg/fz4APXr0oGfPnsyYMQOAtm3bUlZWRnl5OZWVlQCMHDmSV199lVWrVgEwZMgQKisrWbBgAQC9evWia9euVFRUANC+fXuGDRvG9OnT2bZtGwCjRo1i7ty5rFmzBoChQ4eyYcMGFi5cCEDv3r3p2LEjs2bNAqBDhw4MHTqU559/nohAEqNHj2b27NmsW7cOgGHDhrF27VoWL14M7NnnacmSJQD079+f0tJS5syZA0CXLl0YMGAA06dPB6C0tJQRI0bs1usEpQW+G6w5WL58eVE+T3VRRNRdYecFDQ4BbgYeB/6Ulh0HfBq4OSLupRaSugPLgNERMS2n/CbgnIgYWMe+fYC26XPdBlwZEQ+k27YCF0XE/Tn1zwMmRESdn4yysrKo/uOyJy6+c48PYU3QhKsaOwLbW/yZbZmK9ZmVNDMiymratrsLtu9oreW4m/f7GWuyGqgiGTiUqyuwoq4AImJR+vBlSV1JEvcDadmK3TmmmZlZoert04yIVhl/6lwNKCK2kgzqGZO3aQzJKNpCYs5tQZYX4ZhmZmb1KrRPc0/dATwg6SXgBeBSoDtwH4Ck+wEi4rz098uBRcD8dP9RwDXs3KIdD0yTdD3wBPAp4CPAyL18LmZmto8pOGlK6gD8M3AosH/utoj4dl37RsQjkjoBN5D0j84BTouI19Mq+fM1W5P0YfYGtgH/AK4nTbLpMV+UdBbJlJdvp3XGRcSMQs/NzMysLgUlTUnHAb8DKklGsy4jSX6VwGKSpFWndLBQjX2fEXFS3u93AndmOOZjZL9ZtpmZ2W4pdJ7m7SQr7vQAtgAnk7QOK0hahGZmZi1WoUnzSOCeSOapVAGlEbESuI5kRKuZmVmLVWjS3JrzeCVwWPp4I8mAHjMzsxar0IFAs4BjgVeBqcB30nmT5wJ/K25oZmZmTUuhLc1vAm+mj28A3iJZ1KADuy52YGZm1qIU1NKMiIqcx2+RTD0xMzPbJ+zW4gaSDgeOSH+dFxELixeSmZlZ01ToPM1OwM+AscD294v1W+DCiFhT5PjMzMyajEL7NH8K9CO5CfQB6c8ooA8woY79zMzMmr1CL8+eCnw0Ispzyl6Q9AXgD8ULy8zMrOkptKX5FvBuDeWbAF+aNTOzFq3QpPlt4E5JPaoL0sc/IMO6s2ZmZs1ZvZdnJb0MRE5RH2CxpGXp79Xr0HYh6fM0MzNrkbL0afruIWZmZmRImhFxS0MEYmZm1tTt7uIGJwODSC7bzo2IqcUMyszMrCkqdHGDHsCvgGN4fw3a7pIqgE9FxJu17mxmZtbMFTp69i6S+2j2i4heEdEL6J+W3VXs4MzMzJqSQi/PjgFOiohF1QURsVDSFcCzRY3MzMysiSm0pQk7Tz+pq8zMzKxFKTRpPgvcLalXdYGkQ4E7cUvTzMxauEKT5hXAB4CFkl6X9Drwj7TsimIHZ2Zm1pQU2qe5BvgwcBLwT2nZKxHhxdrNzKzFy5w0JbUG3gGGRsQzwDN7LSozM7MmKPPl2YioAl4H9t974ZiZmTVdhfZp/gfwPUmd90YwZmZmTVmhfZrXkNzlZJmkpeTdWzMijixWYGZmZk1NoUnzMZI5mdoLsZiZmTVpmZKmpAOB24FPAvuRzMm8PCJW773QzMzMmpasfZq3ABcAvwMeAk4BfrSXYjIzM2uSsl6e/TTw+Yh4GEDSL4AXJLVOR9WamZm1eFlbmr2A/6v+JSJeArYB3fdGUGZmZk1R1qTZGtiaV7aN3byJtZmZWXOUNekJeFBSZU7ZAcAESZuqCyJibDGDMzMza0qyJs1JNZQ9WMxAzMzMmrpMSTMi/n1vB2JmZtbU7c5NqM3MzPZJTppmZmYZOWmamZll5KRpZmaWkZOmmZlZRk6aZmZmGTlpmpmZZeSkaWZmlpGTppmZWUZOmmZmZhk5aZqZmWXkpGlmZpaRk6aZmVlGTppmZmYZOWmamZll5KRpZmaWkZOmmZlZRk6aZmZmGTlpmpmZZeSkaWZmllGDJ01Jl0laJGmLpJmSTqyj7iGS/kfS3yVVSZpYQ50LJEUNPwfs1RMxM7N9ToMmTUnjgPHArcDRwIvAk5IOrWWXUmA18D1gRh2H3gQckvsTEVuKFbeZmRk0fEvzamBiREyIiFci4nJgOfDFmipHxOKIuCIiJgJr6zhuRMSK3J/ih25mZvu6BkuakvYHjgGm5G2aAhy/h4dvI+l1SUsl/VbS0Xt4PDMzs12UNOBzdQZaAyvzylcCp+zBcecDFwKzgXbAlcALkoZGxIL8ypIuAS4B6N69O1OnTgWgb9++tGvXjtmzZwPQqVMnBg8ezLRp0wAoKSlh5MiRzJo1i/Xr1wNQVlbGypUrgcP3IHxrqioqKti4cSMAw4cPZ+nSpSxbtgyAgQMH0rp1a+bNmwdAt27d6NOnD+Xl5QC0adOG4cOHM2PGDDZv3gzAiBEjWLRoEStWJBdCBg0aRFVVFfPnzwegR48e9OzZkxkzkp6Itm3bUlZWRnl5OZWVlQCMHDmSV199lVWrVgEwZMgQKisrWbAgeav36tWLrl27UlFRAUD79u0ZNmwY06dPZ9u2bQCMGjWKuXPnsmbNGgCGDh3Khg0bWLhwIQC9e/emY8eOzJo1C4AOHTowdOhQnn/+eSICSYwePZrZs2ezbt06AIYNG8batWtZvHgxsGefpyVLlgDQv39/SktLmTNnDgBdunRhwIABTJ8+HYDS0lJGjBixW69T0vNjLc3y5cuL8nmqiyJiL55CzhNJ3YFlwOiImJZTfhNwTkQMrGf/3wKrI+KCeuq1Bv4K/DEirqirbllZWVT/cdkTF9+5x4ewJmjCVY0dge0t/sy2TMX6zEqaGRFlNW1ryD7N1UAV0DWvvCtQtD7IiKgCKoD+xTqmmZkZNGDSjIitwExgTN6mMSSjaItCkoAjSQYYmZmZFU1D9mkC3AE8IOkl4AXgUqA7cB+ApPsBIuK86h0kHZU+bA9sT3/fGhHz0u3fAv4ELEjrXEGSNGsckWtmZra7GjRpRsQjkjoBN5DMp5wDnBYRr6dVapqv+Ze8308HXgd6p78fBPwE6Aa8k9YfFREvFTV4MzPb5zV0S5OIuBe4t5ZtJ9VQpnqO9xXgK0UJzszMrA5ee9bMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vISdPMzCwjJ00zM7OMnDTNzMwyctI0MzPLyEnTzMwsIydNMzOzjJw0zczMMmrwpCnpMkmLJG2RNFPSifXUH53W2yJpoaRL9/SYZmZmu6NBk6akccB44FbgaOBF4ElJh9ZSvw/w+7Te0cB/AndL+szuHtPMzGx3NXRL82pgYkRMiIhXIuJyYDnwxVrqXwq8GRGXp/UnAJOAa/bgmGZmZrulwZKmpP2BY4ApeZumAMfXstuIGuo/DZRJ2m83j2lmZrZbGrKl2RloDazMK18JdKtln2611C9Jj7c7xzQzM9stJY0dQEOTdAlwSfrrRknzGzOeZqgzsLqxg2gIP/1KY0dgVhT+zBbusNo2NGTSXA1UAV3zyrsCK2rZZ0Ut9belx1Ohx4yInwA/yRy17URSRUSUNXYcZpaNP7PF1WCXZyNiKzATGJO3aQzJiNealNdSvyIi3tvNY5qZme2Whr48ewfwgKSXgBdIRsd2B+4DkHQ/QEScl9a/D/iypDuBHwMnABcAZ2c9ppmZWbE0aNKMiEckdQJuAA4B5gCnRcTraZVD8+ovknQa8EOSKSRvAldExOMFHNOKy5e2zZoXf2aLSBHR2DGYmZk1C1571szMLCMnTTMzs4ycNK0gktTYMZiZNRb3aZqZmWW0z60IZIWTVAocCXwKeAeYC7wGLImIdyUp/O3LzPYBbmlavSTdBXya5O4xHYDeJNN/ngDujIiFjRacmVkDcp+m1UnSIJIFJS4EPh4R/Ujmw94LnA7MlXSJ+zrNbF/glqbVSdI3SJLlqPT3kojYlrP9VpJW6MkR8WYjhWlmOSR1JVlO9PcRsbaOevtFxHsNF1nz55am1ecV4BBJ/QAiYpukEkkHpNsnAJuAMxorQDPbxQ3A/cBrkh6VdFo6NmEHSYcCV+aXW92cNK0+00juKvNbSWdKKo2IbRGxBZKlDknuaVrZmEGa2U7KgNuAr5KMQ/gVsEjS3ZKGpXUuBi6NCH92C+DLs1YvSd1J1v/9ELAUeAl4Ln38JeCzQO+IeLfRgjQzYMfndTzwTET8RFIJ0A/4V5KbXXwImAf0Bb4REeMbLdhmyEnTMkkXxf8Xkn6SvsBAkm+wzwM/joiHGzE8M0tJ+gBwMrAqImbkbTsQGAJcQzIWoV1EbG74KJsvJ02rlaSeJN9QAd4l+Xa6mSRptiXpy1xd10ADM2tcNc2jljQRODwiTmycqJovL25gNZL0RZJpJkNJkuNCksuxfwQei4jXGjE8M6uFpFZAVCfKGhJmG5J7Dt/RCOE1e25p2i7SS7GvAT8AfgQcDJwCnAQM4v37ms7zakBmzYuk/YCyiChv7FiaIydN24Wky4FzI2J4DdtGAv8J9AA+HBGrGzo+M9tVmgz7AK97ROze4yknVpOtQDtJQyBZe1bS/gARMR04B9gCfKzxQjSzPF8C/gLcJ+l0Sd0ktc6tIKm9pE9Uf56tcE6aVpPHgO3AVZLaRURlRGxN+0qIiDeAt4GejRijme1sHMl0sH4k60KXA7dLGinpg2mdzwI3RsTWxgmx+XPStJ2ka8iuJVlRZAzwpqSfSTom3X6opHNJ5nr9svEiNbNqkg4G3gMmpCNiDwN+RjJNbBrwnKTrgKuAGbUdx+rnPk2rkaSDgEOB40luCXZCumkFIOCBiLi5UYIzs51IOgQ4C5gXEU/nbTsauCjd3gHoFRHLGj7KlsFJ03aQ1AX4HMnSW6tJ5mS+DUwH/gTsR3Lp56mIeLWRwjSzGqRTSSIituTedah6dLuk7wKnRcTRjRVjS+CkaTukE54HA78huUTbkeQy7ABgFXBD/gojZtZ01DYFLF0JaBbw84i4reEjazmcNA3Y0Ze5geSb6LScskOB4SSXd/oCZ0bErEYL1Mx2Iqk9sKGu+dLpXYnGAQ95ENCe8UAgqzYIWEQy3QRIlxSJeD0ifklyw+m3gX9rnPDMrBa3AxdK+lCaQGtyQERMcsLcc06aVm0hySXYH0rqXz29pFo6WXoS8M+NEZyZ7UrS2SS3+PoB8GuSKSafknR42sdZ3dc5qXrete0ZX561HSQdB9xHMgDox8CzwLqI2Jj2idwPbImIcxsxTDNLSZoAVAH/j+SuJecDhwPzgd+TfIYHAuMjwgsaFIGTpu0k/TZ6IzCW5M4m5cBbJGvPLgcuioiXGy9CMwNI75N5LdA+Iq7PKR9M0vo8AzgAOAiYFBGfb4w4WxonTatROv3kE8AnSZbMmwM8GhF/b8y4zOx9kjoAXSPi7+nSeO/lDgiSNA54CBgWEX9tpDBbFCdNq5ekVhGxvbHjMLP6peMRFBFVki4muTR7YGPH1VL4fppWLydMs+Yj7/PaDvhWY8XSErmlaWbWQqW3C6vyF9/icdI0MzPLyPM0zczMMnLSNDMzy8hJ08zMLCMnTTMzs4ycNM3MzDJy0jQzM8vo/wNPTIQ+y8kdRgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Construct quantum circuit\n", + "circ = QuantumCircuit(3, 3)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "circ.cx(1, 2)\n", + "circ.measure([0, 1, 2], [0, 1, 2])\n", + "\n", + "sim_ideal = AerSimulator()\n", + "\n", + "# Execute and get counts\n", + "result = sim_ideal.run(transpile(circ, sim_ideal)).result()\n", + "counts = result.get_counts(0)\n", + "plot_histogram(counts, title='Ideal counts for 3-qubit GHZ state')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generating a simulator that mimics a device\n", + "\n", + "We call `from_backend` to create a simulator for `ibmq_vigo`:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "sim_vigo = AerSimulator.from_backend(device_backend)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By storing the device properties in `vigo_simulator`, we ensure that the appropriate basis gates and coupling map are used when compiling circuits for simulation, thereby most closely mimicking the gates that will be executed on a real device. In addition `vigo_simulator` contains an approximate noise model consisting of:\n", + "\n", + "* **Single-qubit gate errors** consisting of a single qubit depolarizing error followed by a single qubit thermal relaxation error.\n", + "* **Two-qubit gate errors** consisting of a two-qubit depolarizing error followed by single-qubit thermal relaxation errors on both qubits in the gate.\n", + "* **Single-qubit readout errors** on the classical bit value obtained from measurements on individual qubits.\n", + "\n", + "For the gate errors the error parameter of the thermal relaxation errors is derived using the `thermal_relaxation_error` function from `aer.noise.errors` module, along with the individual qubit $T_1$ and $T_2$ parameters, and the `gate_time` parameter from the device backend properties. The probability of the depolarizing error is then set so that the combined average gate infidelity from the depolarizing error followed by the thermal relaxation is equal to the `gate_error` value from the backend properties.\n", + "\n", + "For the readout errors the probability that the recorded classical bit value will be flipped from the true outcome after a measurement is given by the qubit `readout_errors`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Running a noise simulation\n", + "\n", + "Once we have created a noisy simulator backend based on a real device we can use it to run noisy simulations.\n", + "\n", + "**Important:** When running noisy simulations it is *critical* to `transpile` the circuit for the backend so that the circuit is transpiled to the correct noisy basis gate set for the backend." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T16:57:36.864987Z", + "start_time": "2019-08-19T16:57:36.672055Z" + }, + "tags": [ + "nbsphinx-thumbnail" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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kZiUNEVERGJS0hQREYlJSVNERCQmJU0REZGYlDRFRERiUtIUERGJSUlTREQkJiVNERGRmJQ0RUREYlLSFBERiUlJU0REJCYlTRERkZiUNEVERGJS0hQREYlJSVNERCQmJU0REZGYlDRFRERiUtIUERGJSUlTREQkJiXN/dy0adPo27cvpaWl3HbbbXvMr6io4IwzzqC0tJShQ4eyYsUKAObMmcNRRx3FUUcdxaBBg3jyySdrlvnlL3/JgAEDGDhwIGeddRY7d+5sqtUREWlUSpr7saqqKi677DKmTp3KokWLePjhh1m0aNFuZe677z7atWvH22+/zTXXXMN1110HwMCBAykvL+f1119n2rRpfOc736GyspI1a9Zw9913U15ezoIFC6iqqmLy5MnZWD0RkYxT0tyPzZkzh9LSUnr37k3z5s0588wzmTJlym5lpkyZwje/+U0ATjvtNJ5//nncnVatWlFYWAjAzp07MbOaZSorK9mxYweVlZVs376drl27Nt1KiYg0IiXN/diaNWvo1q1bzeuSkhLWrFmTtkxhYSEHHXQQmzdvBmD27NkMGDCAI444ggkTJlBYWEhxcTHXXnst3bt359BDD+Wggw7ipJNOarqVEhFpREqasteGDh3KwoULee2117j11lvZuXMnH3zwAVOmTGH58uWsXbuWbdu28eCDD2Y7VBGRjFDS3I8VFxezatWqmterV6+muLg4bZnKyko++ugjOnTosFuZfv360bp1axYsWMBzzz1Hr169OOSQQzjggAM49dRTeeWVVxp/ZUREmoCS5n7smGOOYenSpSxfvpxdu3YxefJkxo4du1uZsWPH8oc//AGAxx9/nM9//vOYGcuXL6eyshKAlStX8tZbb9GzZ0+6d+/Oq6++yvbt23F3nn/+efr169fk6yayL9rb3u7PPvssQ4YM4YgjjmDIkCG88MILAGzZsqWmF/xRRx1Fx44dufrqq5twjfJPYbYDkOwpLCzknnvuYfTo0VRVVfHtb3+bAQMGcOONN1JWVsbYsWM5//zz+cY3vkFpaSnt27ev6Qk7c+ZMbrvtNg444ACaNWvGvffeS8eOHenYsSOnnXYagwcPprCwkKOPPpqLLrooy2sqkv+qe7s/++yzlJSUcMwxxzB27Fj69+9fUyaxt/vkyZO57rrreOSRR+jYsSN//vOf6dq1KwsWLGD06NGsWbOGNm3a8Prrr9csP2TIEE499dQsrF3+MHfPdgxZU1ZW5uXl5Q2u58I7Gx5LbSZe3bj1i0jumzVrFuPHj+fvf/87ALfeeisA119/fU2Z0aNHM378eIYPH05lZSVdunThvffe2613u7vToUMH1q1bR1FRUc30JUuW8IUvfIF33313t/KZlC/bSjOb6+5lqeapeVZEJA80tLd7tSeeeILBgwfvljABJk+ezBlnnNFoCXNfoeZZEZH9xMKFC7nuuut45pln9pg3efJkHnjggSxElV+UNPdz+dJcIpIp06ZN46qrrqKqqooLLriA733ve7vNr6io4Nxzz2Xu3Ll06NCBRx55hJ49e7J582ZOO+00XnvtNc477zzuueceIHSmOe6442qWX716Neeccw533nlnRuOuT2/3kpKSPXq7r169mlNOOYX777+fPn367Lbc/PnzqaysZMiQIRmNeV+k5lkR2W80ZOjIFi1a8JOf/ITbb799t/LVnWmqHz169GiUzjQN6e3+4Ycf8qUvfYnbbruNY489do+6H374Yc4666yMx7wvUtIUkf1GQ4aOPPDAAxkxYgQtWrRIW/+SJUvYuHHjbkeemZLY271fv36cfvrpNb3dn376aQDOP/98Nm/eTGlpKXfccUfNZSn33HMPb7/9NjfddFPN5SUbN26sqfvRRx9V0oxJzbMist9I1Zlm9uzZacskdqbp2LFjnfU3dmeaMWPGMGbMmN2m3XTTTTXPW7RowWOPPbbHcjfccAM33HBD2nqXLVuWuSD3cTrSFBHJkMmTJ+uIbR+npCl5a29HR4FwjVtpaSl9+/atue5t8eLFu42O0rZt24x35pDsytTQkamoM83+Qc2zkpcaMjrKokWLmDx5MgsXLmTt2rWccMIJLFmyhL59+9aMjlJVVUVxcTGnnHJKo8S/tz04IST8++67j4KCAu6++25Gjx4NwIcffsgFF1zAggULMDN+97vfMXz48LyJv2fPnrRp04aCggIKCwvJxMAjyRI70xQXFzN58mQeeuih3cpUd6YZPnz4bp1p6tJUnWnU4z27dKQpeakhHTqmTJnCmWeeSVFREb169aK0tJQ5c+bstuzzzz9Pnz596NGjR8Zjb0gPzsSEP23aNC699FKqqqoAuOqqqzj55JN56623mD9/fqON+dtY8QO8+OKLvP76642SMKFhnWkg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", + "text/plain": [ + "
" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Transpile the circuit for the noisy basis gates\n", + "tcirc = transpile(circ, sim_vigo)\n", + "\n", + "# Execute noisy simulation and get counts\n", + "result_noise = sim_vigo.run(tcirc).result()\n", + "counts_noise = result_noise.get_counts(0)\n", + "plot_histogram(counts_noise,\n", + " title=\"Counts for 3-qubit GHZ state with device noise model\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If transpilation is skipped noise from the device noise model will not be applied to gates in the circuit that are supported by the simulator, but not supported by the mimicked backend." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
qiskit-terra0.22.0.dev0+397a639
qiskit-aer0.10.4
System information
Python version3.10.4
Python compilerClang 12.0.0
Python buildmain, Mar 31 2022 03:38:35
OSDarwin
CPUs2
Memory (Gb)8.0
Thu Jul 14 10:20:15 2022 +08
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "celltoolbar": "Tags", + "kernelspec": { + "display_name": "Python 3.10.4 ('fixissueenv')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.4" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "f53bee1cc8ff042b6860c611f7dc293c44dcfc3f45ad90e95286e8cf5f1fb6ad" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/3_building_noise_models.ipynb b/docs/tutorials/3_building_noise_models.ipynb new file mode 100644 index 0000000000..771fbff7fe --- /dev/null +++ b/docs/tutorials/3_building_noise_models.ipynb @@ -0,0 +1,932 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Building Noise Models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This notebook introduces how to use the Qiskit Aer `noise` module to build custom noise models for noisy simulations." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.403378Z", + "start_time": "2019-08-19T17:00:41.139269Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from qiskit import QuantumCircuit, transpile\n", + "from qiskit.quantum_info import Kraus, SuperOp\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit.tools.visualization import plot_histogram\n", + "\n", + "# Import from Qiskit Aer noise module\n", + "from qiskit_aer.noise import (NoiseModel, QuantumError, ReadoutError, \n", + " pauli_error, depolarizing_error, thermal_relaxation_error)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Qiskit Aer Noise Module\n", + "\n", + "The Qiskit Aer `noise` module contains Python classes to build customized noise models for simulation. There are three key classes:\n", + "\n", + "1. The `NoiseModel` class which stores a noise model used for noisy simulation.\n", + "2. The `QuantumError` class which describes CPTP gate errors. These can be applied:\n", + " * After *gate* or *reset* instructions\n", + " * Before *measure* instructions.\n", + "\n", + "3. The `ReadoutError` class which describes classical readout errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quantum Errors\n", + "\n", + "Rather than deal with the `QuantumError` object directly, many helper functions exist to automatically generate a specific type of parameterized quantum error. These are contained in the `noise` module and include functions for many common errors types used in quantum computing research. The function names and the type of error they return are:\n", + "\n", + "| Standard error function | Details |\n", + "| --- | --- |\n", + "| `kraus_error` | a general n-qubit CPTP error channel given as a list of Kraus matrices $[K_0, ...]$. |\n", + "| `mixed_unitary_error` | an n-qubit mixed unitary error given as a list of unitary matrices and probabilities $[(U_0, p_0),...]$. |\n", + "| `coherent_unitary_error` | an n-qubit coherent unitary error given as a single unitary matrix $U$. |\n", + "| `pauli_error` | an n-qubit Pauli error channel (mixed unitary) given as a list of Pauli's and probabilities $[(P_0, p_0),...]$ |\n", + "| `depolarizing_error` | an n-qubit depolarizing error channel parameterized by a depolarization probability $p$. |\n", + "| `reset_error` | a single-qubit reset error parameterized by a probabilities $p_0, p_1$ of resetting to the $|0\\rangle$, $|1\\rangle$ state.|\n", + "| `thermal_relaxation_error` | a single qubit thermal relaxation channel parameterized by relaxation time constants $T_1$, $T_2$, gate time $t$, and excited state thermal population $p_1$. |\n", + "| `phase_amplitude_damping_error` | A single-qubit generalized combined phase and amplitude damping error channel given by an amplitude damping parameter $\\lambda$, a phase damping parameter $\\gamma$, and an excited state thermal population $p_1$. |\n", + "| `amplitude_damping_error` | A single-qubit generalized amplitude damping error channel given by an amplitude damping parameter $\\lambda$, and an excited state thermal population $p_1$. |\n", + "| `phase_damping_error` | A single-qubit phase damping error channel given by a phase damping parameter $\\gamma$ |\n", + "\n", + "### Combining quantum errors\n", + "\n", + "`QuantumError` instances can be combined by using composition, tensor product, and tensor expansion (reversed order tensor product) to produce new `QuantumErrors` as:\n", + "\n", + " * Composition: $\\cal{E}(\\rho)=\\cal{E_2}(\\cal{E_1}(\\rho))$ as `error = error1.compose(error2)`\n", + " * Tensor product: $\\cal{E}(\\rho) =(\\cal{E_1}\\otimes\\cal{E_2})(\\rho)$ as `error error1.tensor(error2)`\n", + " * Expand product: $\\cal{E}(\\rho) =(\\cal{E_2}\\otimes\\cal{E_1})(\\rho)$ as `error error1.expand(error2)`" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Example\n", + "\n", + "For example to construct a 5% single-qubit Bit-flip error:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.420358Z", + "start_time": "2019-08-19T17:00:43.416062Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.05, Circuit = \n", + " ┌───┐\n", + "q: ┤ X ├\n", + " └───┘\n", + " P(1) = 0.95, Circuit = \n", + " ┌───┐\n", + "q: ┤ I ├\n", + " └───┘\n", + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.05, Circuit = \n", + " ┌───┐\n", + "q: ┤ Z ├\n", + " └───┘\n", + " P(1) = 0.95, Circuit = \n", + " ┌───┐\n", + "q: ┤ I ├\n", + " └───┘\n" + ] + } + ], + "source": [ + "# Construct a 1-qubit bit-flip and phase-flip errors\n", + "p_error = 0.05\n", + "bit_flip = pauli_error([('X', p_error), ('I', 1 - p_error)])\n", + "phase_flip = pauli_error([('Z', p_error), ('I', 1 - p_error)])\n", + "print(bit_flip)\n", + "print(phase_flip)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.435843Z", + "start_time": "2019-08-19T17:00:43.432211Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.0025000000000000005, Circuit = \n", + " ┌───┐┌───┐\n", + "q: ┤ X ├┤ Z ├\n", + " └───┘└───┘\n", + " P(1) = 0.0475, Circuit = \n", + " ┌───┐┌───┐\n", + "q: ┤ X ├┤ I ├\n", + " └───┘└───┘\n", + " P(2) = 0.0475, Circuit = \n", + " ┌───┐┌───┐\n", + "q: ┤ I ├┤ Z ├\n", + " └───┘└───┘\n", + " P(3) = 0.9025, Circuit = \n", + " ┌───┐┌───┐\n", + "q: ┤ I ├┤ I ├\n", + " └───┘└───┘\n" + ] + } + ], + "source": [ + "# Compose two bit-flip and phase-flip errors\n", + "bitphase_flip = bit_flip.compose(phase_flip)\n", + "print(bitphase_flip)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.460191Z", + "start_time": "2019-08-19T17:00:43.456782Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 2 qubits. Noise circuits:\n", + " P(0) = 0.0025000000000000005, Circuit = \n", + " ┌───┐\n", + "q_0: ┤ X ├\n", + " ├───┤\n", + "q_1: ┤ Z ├\n", + " └───┘\n", + " P(1) = 0.0475, Circuit = \n", + " ┌───┐\n", + "q_0: ┤ I ├\n", + " ├───┤\n", + "q_1: ┤ Z ├\n", + " └───┘\n", + " P(2) = 0.0475, Circuit = \n", + " ┌───┐\n", + "q_0: ┤ X ├\n", + " ├───┤\n", + "q_1: ┤ I ├\n", + " └───┘\n", + " P(3) = 0.9025, Circuit = \n", + " ┌───┐\n", + "q_0: ┤ I ├\n", + " ├───┤\n", + "q_1: ┤ I ├\n", + " └───┘\n" + ] + } + ], + "source": [ + "# Tensor product two bit-flip and phase-flip errors with\n", + "# bit-flip on qubit-0, phase-flip on qubit-1\n", + "error2 = phase_flip.tensor(bit_flip)\n", + "print(error2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Converting to and from QuantumChannel operators\n", + " \n", + "We can also convert back and forth between `QuantumError` objects in Qiskit Aer and `QuantumChannel` objects in Qiskit Terra." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.482424Z", + "start_time": "2019-08-19T17:00:43.473779Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Kraus([[[ 9.74679434e-01+0.j, 0.00000000e+00+0.j],\n", + " [-1.20234617e-16+0.j, 9.74679434e-01+0.j]],\n", + "\n", + " [[ 2.62045272e-16+0.j, 2.23606798e-01+0.j],\n", + " [ 2.23606798e-01+0.j, -2.84112242e-16+0.j]]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "# Convert to Kraus operator\n", + "bit_flip_kraus = Kraus(bit_flip)\n", + "print(bit_flip_kraus)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.509521Z", + "start_time": "2019-08-19T17:00:43.503976Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n", + " [0. +0.j, 0.9+0.j, 0. +0.j, 0. +0.j],\n", + " [0. +0.j, 0. +0.j, 0.9+0.j, 0. +0.j],\n", + " [0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]],\n", + " input_dims=(2,), output_dims=(2,))\n" + ] + } + ], + "source": [ + "# Convert to Superoperator\n", + "phase_flip_sop = SuperOp(phase_flip)\n", + "print(phase_flip_sop)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:43.794037Z", + "start_time": "2019-08-19T17:00:43.778223Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 1.0, Circuit = \n", + " ┌───────┐\n", + "q: ┤ kraus ├\n", + " └───────┘\n" + ] + }, + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Convert back to a quantum error\n", + "print(QuantumError(bit_flip_kraus))\n", + "\n", + "# Check conversion is equivalent to original error\n", + "QuantumError(bit_flip_kraus) == bit_flip" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Readout Error\n", + "\n", + "Classical readout errors are specified by a list of assignment probabilities vectors $P(A|B)$:\n", + "\n", + " * $A$ is the *recorded* classical bit value\n", + " * $B$ is the *true* bit value returned from the measurement \n", + " \n", + "E.g. for 1 qubits: $ P(A|B) = [P(A|0), P(A|1)]$." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:44.659598Z", + "start_time": "2019-08-19T17:00:44.654818Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "ReadoutError([[0.95 0.05]\n", + " [0.1 0.9 ]])" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Measurement miss-assignement probabilities\n", + "p0given1 = 0.1\n", + "p1given0 = 0.05\n", + "\n", + "ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Readout errors may also be combined using `compose`, `tensor` and `expand` like with quantum errors." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Adding errors to a Noise Model\n", + "\n", + "When adding a quantum error to a noise model we must specify the type of *instruction* that it acts on, and what qubits to apply it to. There are two cases for Quantum Errors:\n", + " \n", + " 1. All-qubit quantum error\n", + " 2. Specific qubit quantum error\n", + " \n", + "### 1. All-qubit quantum error\n", + "\n", + "This applies the same error to any occurrence of an instruction, regardless of which qubits it acts on.\n", + "\n", + "It is added as `noise_model.add_all_qubit_quantum_error(error, instructions)`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:45.882254Z", + "start_time": "2019-08-19T17:00:45.877630Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NoiseModel:\n", + " Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n", + " Instructions with noise: ['u3', 'u2', 'u1']\n", + " All-qubits errors: ['u1', 'u2', 'u3']\n" + ] + } + ], + "source": [ + "# Create an empty noise model\n", + "noise_model = NoiseModel()\n", + "\n", + "# Add depolarizing error to all single qubit u1, u2, u3 gates\n", + "error = depolarizing_error(0.05, 1)\n", + "noise_model.add_all_qubit_quantum_error(error, ['u1', 'u2', 'u3'])\n", + "\n", + "# Print noise model info\n", + "print(noise_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Specific qubit quantum error\n", + "\n", + "This applies the error to any occurrence of an instruction acting on a specified list of qubits. Note that the order of the qubit matters: For a 2-qubit gate an error applied to qubits [0, 1] is different to one applied to qubits [1, 0] for example.\n", + "\n", + "It is added as `noise_model.add_quantum_error(error, instructions, qubits)`:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:46.615959Z", + "start_time": "2019-08-19T17:00:46.612055Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NoiseModel:\n", + " Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n", + " Instructions with noise: ['u3', 'u2', 'u1']\n", + " Qubits with noise: [0]\n", + " Specific qubit errors: [('u1', (0,)), ('u2', (0,)), ('u3', (0,))]\n" + ] + } + ], + "source": [ + "# Create an empty noise model\n", + "noise_model = NoiseModel()\n", + "\n", + "# Add depolarizing error to all single qubit u1, u2, u3 gates on qubit 0 only\n", + "error = depolarizing_error(0.05, 1)\n", + "noise_model.add_quantum_error(error, ['u1', 'u2', 'u3'], [0])\n", + "\n", + "# Print noise model info\n", + "print(noise_model)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Note on non-local qubit quantum error\n", + "\n", + "`NoiseModel` does not support addition of non-local qubit quantum errors. They should be handled outside of `NoiseModel`. That suggests you should write your own transpiler pass (`TransformationPass`) and run the pass just before running the simulator if you need to insert your quantum errors into your circuit under your own conditions. See `noise.passes.LocalNoisePass` for example of how to implement such a transpiler pass." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Executing a noisy simulation with a noise model\n", + "\n", + "The command `AerSimulator(noise_model=noise_model)` returns a simulator configured to the given noise model. In addition to setting the simulator's noise model, it also overrides the simulator's basis gates, according to the gates of the noise model." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Noise Model Examples\n", + "\n", + "We will now give some examples of noise models. For our demonstrations we will use a simple test circuit generating a n-qubit GHZ state:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:48.817405Z", + "start_time": "2019-08-19T17:00:48.806966Z" + }, + "slideshow": { + "slide_type": "fragment" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌───┐ ░ ┌─┐ \n", + " q_0: ┤ H ├──■─────────────░─┤M├─────────\n", + " └───┘┌─┴─┐ ░ └╥┘┌─┐ \n", + " q_1: ─────┤ X ├──■────────░──╫─┤M├──────\n", + " └───┘┌─┴─┐ ░ ║ └╥┘┌─┐ \n", + " q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───\n", + " └───┘┌─┴─┐ ░ ║ ║ └╥┘┌─┐\n", + " q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├\n", + " └───┘ ░ ║ ║ ║ └╥┘\n", + "meas: 4/════════════════════════╩══╩══╩══╩═\n", + " 0 1 2 3 \n" + ] + } + ], + "source": [ + "# System Specification\n", + "n_qubits = 4\n", + "circ = QuantumCircuit(n_qubits)\n", + "\n", + "# Test Circuit\n", + "circ.h(0)\n", + "for qubit in range(n_qubits - 1):\n", + " circ.cx(qubit, qubit + 1)\n", + "circ.measure_all()\n", + "print(circ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ideal Simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:50.560988Z", + "start_time": "2019-08-19T17:00:50.415545Z" + }, + "scrolled": true + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcgAAAFLCAYAAABShRycAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAAsTAAALEwEAmpwYAAAdIElEQVR4nO3de5xdZX3v8c/PDA7BJJoEk5ALDkguhkA0jI1Dx6BiKGqrzbFHLV7QVrBW8ND22L7aamu1FERrqfS0CoWC1rZalcvBFhEl5IymwUlKIAGT0CQ0wVzMBZPQOJj4O3+sPek4PLlMMjN7Lp/367Vf7FnPWnv/5kVWvlnPep5nRWYiSZJ+1nPqXYAkSQORASlJUoEBKUlSgQEpSVKBASlJUoEBKUlSQUO9C+hPp556ajY1NdW7DEnSALF8+fIdmfnCUtuwCsimpiba29vrXYYkaYCIiCcO12YXqyRJBQakJA0iTU1NnHPOObz0pS+lubkZgA996EPMmjWLc889l0WLFvHUU08BsHPnTl796lczatQorrjiijpWPTgZkJI0yNx///089NBDh24ZLVy4kFWrVvHwww8zY8YMrrnmGgBOPvlkPv7xj/OpT32qnuUOWgakJA1yF110EQ0N1ZCSV7ziFWzevBmA5z3vebS2tnLyySfXs7xBy4CUpEEkIrjooos477zzuPHGG5/Vfsstt/C6172uDpUNPcNqFKskDXZtbW1MmTKF7du3s3DhQmbNmsWCBQsAuPrqq2loaODtb397nascGryClKRBZMqUKQBMmDCBRYsW8eCDDwJw6623cvfdd/PFL36RiKhniUOGASlJg8TTTz/N3r17D72/9957mTNnDvfccw/XXXcdd911F6ecckqdqxw67GKVpEFi27ZtLFq0CIADBw5wySWXcPHFF3PWWWfR0dHBwoULgWqgzmc/+1mgmhayZ88ennnmGe644w7uvfdeZs+eXbffYTCJzKx3Df2mubk5XUlHktQpIpZnZnOpzS7WYa406XjXrl0sXLiQ6dOns3DhQnbv3g3AnXfeybnnnnto37a2tnqWLkl9yoDUsyYdX3vttVx44YWsW7eOCy+8kGuvvRaACy+8kJUrV/LQQw9xyy238N73vreeZUtSnzIg9Sx33nknl156KQCXXnopd9xxBwCjRo06NDru6aefdqScpCHNgBzmSpOO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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Ideal simulator and execution\n", + "sim_ideal = AerSimulator()\n", + "result_ideal = sim_ideal.run(circ).result()\n", + "plot_histogram(result_ideal.get_counts(0))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "source": [ + "## Noise Example 1: Basic bit-flip error noise model\n", + "\n", + "Lets consider a simple toy noise model example common in quantum information theory research:\n", + "\n", + "* When applying a single qubit gate, flip the state of the qubit with probability `p_gate1`.\n", + "* When applying a 2-qubit gate apply single-qubit errors to each qubit.\n", + "* When resetting a qubit reset to 1 instead of 0 with probability `p_reset`.\n", + "* When measuring a qubit, flip the state of the qubit with probability `p_meas`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:51.543615Z", + "start_time": "2019-08-19T17:00:51.536564Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NoiseModel:\n", + " Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n", + " Instructions with noise: ['reset', 'measure', 'u3', 'u1', 'cx', 'u2']\n", + " All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']\n" + ] + } + ], + "source": [ + "# Example error probabilities\n", + "p_reset = 0.03\n", + "p_meas = 0.1\n", + "p_gate1 = 0.05\n", + "\n", + "# QuantumError objects\n", + "error_reset = pauli_error([('X', p_reset), ('I', 1 - p_reset)])\n", + "error_meas = pauli_error([('X',p_meas), ('I', 1 - p_meas)])\n", + "error_gate1 = pauli_error([('X',p_gate1), ('I', 1 - p_gate1)])\n", + "error_gate2 = error_gate1.tensor(error_gate1)\n", + "\n", + "# Add errors to noise model\n", + "noise_bit_flip = NoiseModel()\n", + "noise_bit_flip.add_all_qubit_quantum_error(error_reset, \"reset\")\n", + "noise_bit_flip.add_all_qubit_quantum_error(error_meas, \"measure\")\n", + "noise_bit_flip.add_all_qubit_quantum_error(error_gate1, [\"u1\", \"u2\", \"u3\"])\n", + "noise_bit_flip.add_all_qubit_quantum_error(error_gate2, [\"cx\"])\n", + "\n", + "print(noise_bit_flip)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Executing the noisy simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:52.951874Z", + "start_time": "2019-08-19T17:00:52.687440Z" + }, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcgAAAFLCAYAAABShRycAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAAsTAAALEwEAmpwYAAA7N0lEQVR4nO3deXxU5dn/8c9FggENKIuEvZHKHkkMqRGLAbdqXVpB69oWhcetrRatVZ/SRf09lboiorWKtVpt1dq6tEjdyq4IBARF2RSwgOxQtmIk4f79cZ/EYThgZstMku/79ZoX5JyZ61wzZ+Zc59znPvcx5xwiIiKyrybpTkBERCQTqUCKiIiEUIEUEREJoQIpIiISQgVSREQkhAqkiIhIiOx0J1CX2rZt6/Lz89OdhoiIZIi5c+ducs4dGTavURXI/Px8ysvL052GiIhkCDP75EDz1MQqIiISQgWyFlatWsVJJ51Enz596Nu3L2PHjgXg1ltvpVOnThQVFVFUVMTEiRMB2LNnD8OGDeOYY46hd+/ejB49Op3pi4hIHBpVE2u8srOzuffeeykuLmbHjh3079+f0047DYDrr7+eG2+8cZ/nP//881RUVPD+++/z3//+lz59+nDxxRej858iIvWHCmQtdOjQgQ4dOgDQokULevfuzZo1aw74fDNj165dVFZWsnv3bg455BBatmxZV+mKiEgSqIk1RitXruTdd9+ltLQUgAcffJB+/foxfPhwtm7dCsD555/PYYcdRocOHejatSs33ngjrVu3TmfaIiISIxXIGOzcuZPzzjuP+++/n5YtW3LNNdfw8ccfM3/+fDp06MBPfvITAGbPnk1WVhaffvopK1as4N5772X58uVpzl5ERGKhAllLe/bs4bzzzuPSSy9l6NChAOTl5ZGVlUWTJk244oormD17NgB//vOfOeOMM2jatCnt2rXj61//ui4vERGpZ1Qga8E5x4gRI+jduzc33HBDzfS1a9fW/P/FF1+koKAAgK5duzJp0iQAdu3axTvvvEOvXr3qNmkREUmIOunUwltvvcVTTz3FMcccQ1FREQB33HEHzzzzDPPnz8fMyM/P55FHHgHghz/8IZdffjl9+/bFOcfll19Ov3790vgOREQkVuacS3cOdaakpMSpqVNERKqZ2VznXEnYPDWxioiIhFCBFBERCaECKSIiEkIFUkREJIQKpIiISAgVSBERkRAqkCIikjYHup1gtXvvvRczY9OmTQC8/PLL9OvXj6KiIkpKSpgxY0bKctNAASIikjYHup1gnz59WLVqFa+//jpdu3atef4pp5zCt771LcyM9957jwsuuIDFixenJDcdQYqISNp06NCB4uJiYP/bCV5//fXcddddmFnN83Nzc2v+3rVr1z7zkk0FUkREMkLk7QRffvllOnXqRGFh4X7Pe/HFF+nVqxdnnXUWjz/+eMryUYEUEZG0i7ydYHZ2NnfccQe333576HOHDBnC4sWLeemll/jFL36RspxUIEVEJK2ibyf48ccfs2LFCgoLC8nPz2f16tUUFxezbt26fV5XVlbG8uXLazrwJJs66YiISNqE3U7wmGOOYcOGDTXPyc/Pp7y8nLZt2/LRRx/x1a9+FTNj3rx5VFRU0KZNm5TkpgIZoyvuj+3540emIgsRkYbhQLcTPPPMM0Of/7e//Y0//vGPNG3alObNm/Pcc8+lrKOObncVIxVIEZGGQ7e7EhERiZEKpIiISAgVSBERkRAqkCIiIiFUIEVEREKoQIqIiIRQgRQREQmhAikiIhJCBVJERCSECqSIiEgIFUgREZEQdVYgzex/zWyOmW03s41m9g8zKzjI8x8xM2dmN0ZNzzGzcWa2ycx2mdnfzaxz6t+BiIg0JnV5N4/BwG+BOYABtwNvmlkf59yWyCea2fnAccCnIXHuB74NXAxsBu4DJphZf+dcVcqyFxGRlMq0m0HUWYF0zp0e+beZfQ/YBnwd+EfE9K8AY4FTgX9GveZwYARwuXPujYg4nwTPfy2Fb0FERBqRdJ6DbBEsf2v1BDPLBp4B/s85tyjkNf2BpsDr1ROcc6uARcAJKc1WREQalXTeMHksMB+YGTHtNmCTc+7hA7ymPVAFbIqavj6Ytx8zuxK4EqBjx45MmTIFgG7dutGiRQsWLFgAQJs2bejbty/Tpk0DIDs7m4EDBzJv3jy2b98OQElJCZAb05sEKC8vZ+fOnQCUlpayevVq1qxZA0DPnj3Jysriww8/9G+wfXuOOuooZs70H0vz5s0pLS1l1qxZ7N69G4ABAwawYsUK1q1bB0CfPn2oqqpiyZIlAHTq1InOnTsza9YsAHJzcykpKWHmzJlUVFQAMHDgQJYuXVpz1+6CggIqKipYtmwZAF26dCEvL4/q+2e2bNmS4uJiZsyYQWVlJQBlZWV88MEHbN68GYDCwkJ27NjB8uXLAX8X8NatWzNv3jwAWrVqRWFhIVOnTsU5h5kxaNAgFixYwNatfj+puLiYLVu2sHLlyoTW0/r161m1ahUA3bt3Jycnh4ULFwLQrl07evTowYwZMwDIyclhwIABWk9aT1pPaV5PkEMs1q5dm/B6Opi03DDZzO4DLgIGOueWB9MGA38CipxzG4NpK4EHnXP3BH9fAvwRaOoiEjezScAy59xVB1uubpgsIpK50rF9zagbJpvZGHwHm5Ori2NgMNABWGtmlWZWCXwFuNPMVgfPWQdkAW2jwuYF80RERJKiTgukmY3li+K4OGr2b4F+QFHE41NgDHBK8Jy5wB7gtIiYnYHewNupy1xERBqbOjsHaWYPAd8DzgW2mln1OcOdzrmdzrkNwIao1+wB1jnnlgA457aZ2e+Bu8xsA19c5vEe8GbdvBMREWkM6rKTzg+Cf/8VNf024NYY4owEKoHngOZBvO/rGkgREUmmurwO0uJ4TX7ItArg2uAhIiKSEhqLVUREJIQKpIiISAgVSBERkRAqkCIiIiFUIEVEREKoQIqIiIRQgRQREQmhAikiIhJCBVJERCSECqSIiEgIFUgREZEQKpAiIiIhVCBFRERCqECKiIiEUIEUEREJoQIpIiISQgVSREQkhAqkiIhICBVIERGRECqQIiIiIVQgRUREQqhAioiIhFCBFBERCaECKSIiEkIFUkREJIQKpIiISAgVSBERkRAqkCIiIiFUIEVEREKoQIqIiIRQgRQREQmhAikiIhJCBVJERCSECqSIiEgIFUgREZEQKpAiIiIhVCBFRERCqECKiIiEUIEUEREJoQIpIiISQgVSREQkhAqkiIhICBVIERGRECqQIiIiIVQgRUREQqhAioiIhKjTAmlmZWb2dzNbY2bOzC6Lmp9rZuPMbLWZ7TazJWZ2fdRzcoLnbDKzXUG8znX5PkREpOGr6yPIXGAh8GNgd8j8+4CzgO8BvYFfA78xs+9FPOd+4DzgYuBEoCUwwcyyUpe2iIg0Ntl1uTDn3ERgIoCZPRHylBOAp5xzk4O/V5rZCKAUeMrMDgdGAJc7594I4nwP+AQ4FXgtte9AREQai0w7BzkDOMfMugCY2QlAEfBqML8/0BR4vfoFzrlVwCJ8cRUREUmKOj2CrIXrgEeAf5tZZTDtWufchOD/7YEqYFPU69YH8/ZjZlcCVwJ07NiRKVOmANCtWzdatGjBggULAGjTpg19+/Zl2rRpAGRnZzNw4EDmzZvH9u3bASgpKcG3EsemvLycnTt3AlBaWsrq1atZs2YNAD179iQrK4sPP/zQv8H27TnqqKOYOXMmAM2bN6e0tJRZs2axe7dvlR4wYAArVqxg3bp1APTp04eqqiqWLFkCQKdOnejcuTOzZs0CIDc3l5KSEmbOnElFRQUAAwcOZOnSpWzYsAGAgoICKioqWLZsGQBdunQhLy+P8vJyAFq2bElxcTEzZsygstKvmrKyMj744AM2b94MQGFhITt27GD58uUA5Ofn07p1a+bNmwdAq1atKCwsZOrUqTjnMDMGDRrEggUL2Lp1KwDFxcVs2bKFlStXJrSe1q9fz6pVqwDo3r07OTk5LFy4EIB27drRo0cPZsyYAUBOTg4DBgzQetJ60npK83qCHGKxdu3ahNfTwZhzLqaEksXMdgI/cs49ETHtJ/hidiO+2bQM+A1wvnPuVTO7BPgj0NRFJG5mk4BlzrmrDrbMkpISV/0FjdcV98f2/PEjE1qciEijkY7tq5nNdc6VhM3LmCNIM2sOjAa+45z7RzD5PTMrwhfMV4F1QBbQFtgY8fI8YHrdZSsiIg1dJp2DbBo8qqKmV/FFnnOBPcBp1TODSzx6A2/XQY4iItJI1OkRpJnlAkcHfzYBugZHiFucc/82s6n4yzp24ptYBwHfB24CcM5tM7PfA3eZ2QZgM/7SkPeAN+vyvYiISMNW10eQJcC7waM5cFvw/9uD+RcBc4A/AR8CtwC/AB6MiDESeBF4DngL2Amc45yLPvIUERGJW11fBzkFsIPMXwdc/iUxKoBrg4eIiEhKZNI5SBERkYyhAikiIhJCBVJERCSECqSIiEgIFUgREZEQKpAiIiIhVCBFRERCqECKiIiEUIEUEREJoQIpIiISQgVSREQkRK0LpJmVmdl+Y7eaWbaZlSU3LRERkfSK5QhyMtA6ZPrhwTwREZEGI5YCaYALmd4G2JWcdERERDLDl97uysz+HvzXAU+bWUXE7CygAHg7BbmJiIikTW3uB7k5+NeArcDuiHmfAzOA8UnOS0REJK2+tEA65y4HMLOVwD3OOTWniohIg1ebI0gAnHO3pTIRERGRTFLrAmlmrYFfA6cA7Yjq4OOca5nc1ERERNKn1gUS+D1wLPAo8CnhPVpFREQahFgK5CnAac65WalKRkREJFPEch3kBmBnqhIRERHJJLEUyFHA7WaWm6pkREREMkUsTaw/B/KBDWb2CbAncqZzrl8S8xIREUmrWArkX1OWhYiISIbRdZAiIiIhdD9IERGRELEMFLCDg1z7qIECRESkIYnlHOSPov5uih844Dz8CDsiIiINRiznIJ8Mm25m8/CDCIxLVlIiIiLploxzkJOBc5IQR0REJGMko0BeBGxKQhwREZGMEUsnnffZt5OOAXlAa+CaJOclIiKSVokMFLAX2AhMcc4tTl5KIiIi6aeBAkRERELEcgQJgJmdDPTBN7d+4JybkuykRERE0i2Wc5CdgBeB/vgbJgN0NLNyYIhz7tMDvlhERKSeiaUX6wNAFXC0c66Lc64L0D2Y9kAqkhMREUmXWJpYTwMGO+dWVE9wzi03s+uAfyU9MxERkTSK9TrIsLFYDzg+q4iISH0VS4H8FzDOzLpUTzCzrsD96AhSREQamFgK5HXAYcByM/vEzD4BPg6mXZeK5ERERNIllusgV5lZMXAq0CuYvMg592ZKMhMREUmjLz2CNLNvmtlKM2vpvDecc+Occ+OAOcG80+ogVxERkTpTmybWHwF3O+e2R89wzm0D7gRGJjkvERGRtKpNgewHHKwZdRJQmJx0REREMkNtCuSR+IHJD8QBbZKTjoiISGaoTYFcjT+KPJB+wJraLMzMyszs72a2xsycmV0WMa+pmd1pZu+Z2S4zW2tmfw4uJYmMkWNm48xsU/C8v5tZ59osX0REpLZqUyBfAf6fmTWPnmFmhwK3B8+pjVxgIfBjYHfUvEOBYuDXwb/fBroAr5pZZG/b+4HzgIuBE4GWwAQzy6plDiIiIl+qNpd5/Bo4H1hqZg8C1fd+7I3vwGPAHbVZmHNuIjARwMyeiJq3DT+cXQ0zuwr4IFjW+2Z2ODACuNw590bwnO8Bn+AvP3mtNnmIiIh8mS8tkM65DWZ2AvAwvhBa9Sx8Qfqhc259ivJrGfy7Nfi3P9AUeD0iv1Vmtgg4ARVIERFJkloNFOCc+wQ408xaAUfji+Qy59zWg78yfmZ2CHAv8A/n3Opgcnv83UM2RT19fTAvLM6VwJUAHTt2ZMqUKQB069aNFi1asGDBAgDatGlD3759mTZtGgDZ2dkMHDiQefPmsX27v8KlpKQE30ocm/Lycnbu3AlAaWkpq1evZs0af9q2Z8+eZGVl8eGHH/o32L49Rx11FDNnzgSgefPmlJaWMmvWLHbv9q3SAwYMYMWKFaxbtw6APn36UFVVxZIlSwDo1KkTnTt3ZtasWQDk5uZSUlLCzJkzqaioAGDgwIEsXbqUDRs2AFBQUEBFRQXLli0DoEuXLuTl5VFeXg5Ay5YtKS4uZsaMGVRWVgJQVlbGBx98wObNmwEoLCxkx44dLF++HID8/Hxat27NvHnzAGjVqhWFhYVMnToV5xxmxqBBg1iwYAFbt/qvUnFxMVu2bGHlypUJraf169ezatUqALp3705OTg4LFy4EoF27dvTo0YMZM2YAkJOTw4ABA7SetJ60ntK8niCHWKxduzbh9XQw5lx6xho3s53Aj5xzT4TMywb+DPQFypxzm4PplwB/BJq6iMTNbBK+YF91sGWWlJS46i9ovK64P7bnjx+Z0OJERBqNdGxfzWyuc64kbF6sd/NIuaA4PoPvHXtKdXEMrAOygLZRL8sL5omIiCRFRhVIM2sKPIcvjic556KL3lxgDxGdeYJLPHoDb9dVniIi0vDFcsPkhJlZLv4cJvji3NXMioAtwKfA88DXgHMAZ2bV5xW3Oed2O+e2mdnvgbvMbAOwGbgPeI+Dj/YjIiISk7o+giwB3g0ezYHbgv/fDnTGX/vYEX+kuDbicWFEjJHAi/gjzbeAncA5zrmqOnkHIiLSKNTpEaRzbgpfXCYS5mDzqmNUANcGDxERkZTIqHOQIiIimUIFUkREJIQKpIiISAgVSBERkRAqkCIiIiFUIEVEREKoQIqIiIRQgRQREQmhAikiIhJCBVJERCSECqSIiEgIFUgREZEQKpAiIiIhVCBFRERCqECKiIiEUIEUEREJoQIpIiISQgVSREQkhAqkiIhICBVIERGRECqQIiIiIVQgRUREQqhAioiIhFCBFBERCaECKSIiEkIFUkREJIQKpIiISAgVSBERkRAqkCIiIiFUIEVEREKoQIqIiIRQgRQREQmhAikiIhJCBVJERCSECqSIiEgIFUgREZEQKpAiIiIhVCBFRERCqECKiIiEUIEUEREJoQIpIiISQgVSREQkhAqkiEgDlJ+fzzHHHENRURElJSU108eNG0evXr3o27cvN910UxozzHzZ6U5A6k5+fj4tWrQgKyuL7OxsysvLufXWWxk/fjxHHnkkAHfccQdnnnlmmjMVkWSYPHkybdu23efvl19+mQULFpCTk8OGDRvSmF3mU4FsZKJ/MADXX389N954Y5oyEpG68vDDD3PLLbeQk5MDQLt27dKcUWZTE6uISANkZnzjG9+gf//+PProowAsXbqU6dOnU1payqBBg5gzZ06as8xsGVcgzayDmT1pZhvN7DMz+9DMBkXMNzO71cw+NbPdZjbFzPqmM+f6IuwHA/Dggw/Sr18/hg8fztatW9OYoYgky4wZM5g3bx7//Oc/eeihh5g2bRqVlZVs2bKFd955h7vvvpsLLrgA51y6U81YGVUgzewI4C3AgLOA3sC1QGRD+U3AT4LpXwvmvWFmLeo02Xoo7AdzzTXX8PHHHzN//nw6dOjAT37yk3SnKSJJ0KlTJ8A3ow4ZMoTZs2fTuXNnhg4diplx3HHH0aRJEzZt2hRz7LAOQM8//zx9+/alSZMmlJeXJ/W9pEtGFUh88VvrnPu+c262c26Fc+5fzrlF4I8egZHAb5xzf3POLQSGAS2AS9KWdT0R9oPJy8sjKyuLJk2acMUVVzB79uw0Zykiidq1axc7duyo+f/rr79OQUEB5557LpMnTwZ8c+vnn3++X5+E2po8eTLz58+vKYYFBQW88MILlJWVJedNZIBMK5DnArPM7Dkz22Bm883sR0FhBDgKaA+8Xv0C59xuYBpwQp1nW48c6Aezdu3amue8+OKLFBQUpCtFkYxVVVXFsccey9lnnw3ApEmTKC4upqCggGHDhlFZWZnmDPe1fv16Bg4cSGFhIccddxxnnXUWZ5xxBsOHD2f58uUUFBRw0UUX8eSTT/LF5jUxvXv3pmfPnkmJlSkyrRdrN+AHwBjgN0ARMC6Y9yC+OAKsj3rdeqBTWEAzuxK4EqBjx45MmTLFL6hbN1q0aMGCBQsAaNOmDX379mXatGkAZGdnM3DgQObNm8f27dsBgqaE3JjfVHl5OTt37gSgtLSU1atXs2bNGgB69uxJVlYWH374IQDt27fnqKOOYubMmQA0b96c0tJSZs2axe7duwEYMGAAK1asYN26dQD06dOHqqoqlixZAvgjxc6dOzNr1iwAcnNzad26Naeffjp79+6lqqqKESNG0LVrV4YNG8ZHH31E8+bN6dSpE1dddRVTpkyhS5cu5OXl1ewdtmzZkuLiYmbMmFGzMSgrK+ODDz5g8+bNABQWFrJjxw6WL18O+GaY1q1bM2/ePABatWpFYWEhU6dOxTmHmTFo0CAWLFhQc+6zuLiYLVu2sHLlyn3W07x587j66qvp2LEjkydPZsyYMTzyyCM458jLy+OnP/0phx9+eM16Wr9+PatWrQKge/fu5OTksHDhQsAfQffo0YMZM2YAkJOTw4ABAzJiPZWUlDBz5kwqKioAGDhwIEuXLq3pjl9QUEBFRQXLli0DyLj1FOvvqb6spxdeeIHWrVuzefNmZs+ezbBhw7jnnnvIy8vj8ccf5/HHH2fgwIEZtZ7Gjh27z3qaMmUK3bp1Y9y4cfusp71798a8npxznHCCPya55JJL+PnPf16znqpfF896At+7trbWrl2b8O/pYCyTTtCa2edAuXPuhIhpdwBDnHO9zewE/DnKrzjn/h3xnMeBTs650w8Wv6SkxCXaNn7F/bE9f/zI2JdRVVVFSUkJnTp1YsKECZx44ok1R38bNmzguOOO46WXXoo9cD123333UV5ezvbt25kwYQI9evTg5Zdfpnfv3vz2t79l9uzZPPHEE+lOUxqg1atXM2zYMEaNGsV9993HH/7wB44//ng+/vhjAKZPn87o0aOZOHFimjOtO2vWrKFTp05s2LCB0047jXHjxtU0rQ4ePJh77rlnn8EJaqsutq/RzGyucy402UxrYl0LfBg1bRHQNfj/uuDfvKjn5EXMq/fGjh1L7969a/6ePn068+fPZ/78+QwYMIChQ4emMbu6t3r1al555RX+53/+p2aamdXsqW7bto2OHTumK720iW72c84xatQoevToQe/evXnggQfSnGHDMHLkSO666y6aNPGby7Zt21JZWVlzNPjXv/615uiqsQjrz9AQZVqBfAuIbsTuAXwS/H8FvhCeVj3TzJoBJwJv10WCqRZWDKpt376dSZMmce6559Z9YmkUvYECeOyxxzjzzDPp3LkzTz31FLfccksaM0yP6B2pJ554glWrVrF48WIWLVrERRddlMbsGoYJEybQrl07+vfvXzPNzHj22We5/vrrOe6442pGp2osDtSfoSHKtHOQY4C3zWwU8BxwLHAd8DMA55wzs/uBn5nZYmAp8HNgJ/DntGScZNXFoPoLGOmll17ilFNOoWXLlmnILD0iN1DV548BxowZw8SJEyktLeXuu+/mhhtu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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create noisy simulator backend\n", + "sim_noise = AerSimulator(noise_model=noise_bit_flip)\n", + "\n", + "# Transpile circuit for noisy basis gates\n", + "circ_tnoise = transpile(circ, sim_noise)\n", + "\n", + "# Run and get counts\n", + "result_bit_flip = sim_noise.run(circ_tnoise).result()\n", + "counts_bit_flip = result_bit_flip.get_counts(0)\n", + "\n", + "# Plot noisy output\n", + "plot_histogram(counts_bit_flip)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example 2: T1/T2 thermal relaxation\n", + "\n", + "Now consider a more realistic error model based on thermal relaxation with the qubit environment:\n", + "* Each qubit is parameterized by a thermal relaxation time constant $T_1$ and a dephasing time constant $T_2$.\n", + "* Note that we must have $T_2 \\le 2 T_1$.\n", + "* Error rates on instructions are determined by gate times and qubit $T_1$, $T_2$ values." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:54.577456Z", + "start_time": "2019-08-19T17:00:54.491018Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NoiseModel:\n", + " Basis gates: ['cx', 'id', 'rz', 'sx', 'u2', 'u3']\n", + " Instructions with noise: ['reset', 'measure', 'u3', 'cx', 'u2']\n", + " Qubits with noise: [0, 1, 2, 3]\n", + " Specific qubit errors: [('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('u2', (0,)), ('u2', (1,)), ('u2', (2,)), ('u2', (3,)), ('u3', (0,)), ('u3', (1,)), ('u3', (2,)), ('u3', (3,)), ('cx', (0, 0)), ('cx', (0, 1)), ('cx', (0, 2)), ('cx', (0, 3)), ('cx', (1, 0)), ('cx', (1, 1)), ('cx', (1, 2)), ('cx', (1, 3)), ('cx', (2, 0)), ('cx', (2, 1)), ('cx', (2, 2)), ('cx', (2, 3)), ('cx', (3, 0)), ('cx', (3, 1)), ('cx', (3, 2)), ('cx', (3, 3))]\n" + ] + } + ], + "source": [ + "# T1 and T2 values for qubits 0-3\n", + "T1s = np.random.normal(50e3, 10e3, 4) # Sampled from normal distribution mean 50 microsec\n", + "T2s = np.random.normal(70e3, 10e3, 4) # Sampled from normal distribution mean 50 microsec\n", + "\n", + "# Truncate random T2s <= T1s\n", + "T2s = np.array([min(T2s[j], 2 * T1s[j]) for j in range(4)])\n", + "\n", + "# Instruction times (in nanoseconds)\n", + "time_u1 = 0 # virtual gate\n", + "time_u2 = 50 # (single X90 pulse)\n", + "time_u3 = 100 # (two X90 pulses)\n", + "time_cx = 300\n", + "time_reset = 1000 # 1 microsecond\n", + "time_measure = 1000 # 1 microsecond\n", + "\n", + "# QuantumError objects\n", + "errors_reset = [thermal_relaxation_error(t1, t2, time_reset)\n", + " for t1, t2 in zip(T1s, T2s)]\n", + "errors_measure = [thermal_relaxation_error(t1, t2, time_measure)\n", + " for t1, t2 in zip(T1s, T2s)]\n", + "errors_u1 = [thermal_relaxation_error(t1, t2, time_u1)\n", + " for t1, t2 in zip(T1s, T2s)]\n", + "errors_u2 = [thermal_relaxation_error(t1, t2, time_u2)\n", + " for t1, t2 in zip(T1s, T2s)]\n", + "errors_u3 = [thermal_relaxation_error(t1, t2, time_u3)\n", + " for t1, t2 in zip(T1s, T2s)]\n", + "errors_cx = [[thermal_relaxation_error(t1a, t2a, time_cx).expand(\n", + " thermal_relaxation_error(t1b, t2b, time_cx))\n", + " for t1a, t2a in zip(T1s, T2s)]\n", + " for t1b, t2b in zip(T1s, T2s)]\n", + "\n", + "# Add errors to noise model\n", + "noise_thermal = NoiseModel()\n", + "for j in range(4):\n", + " noise_thermal.add_quantum_error(errors_reset[j], \"reset\", [j])\n", + " noise_thermal.add_quantum_error(errors_measure[j], \"measure\", [j])\n", + " noise_thermal.add_quantum_error(errors_u1[j], \"u1\", [j])\n", + " noise_thermal.add_quantum_error(errors_u2[j], \"u2\", [j])\n", + " noise_thermal.add_quantum_error(errors_u3[j], \"u3\", [j])\n", + " for k in range(4):\n", + " noise_thermal.add_quantum_error(errors_cx[j][k], \"cx\", [j, k])\n", + "\n", + "print(noise_thermal)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Executing the noisy simulation" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:00:55.689241Z", + "start_time": "2019-08-19T17:00:55.515394Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAcgAAAFLCAYAAABShRycAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAAsTAAALEwEAmpwYAAAqxklEQVR4nO3de5xVdb3/8deHGR1RQEHljg2UIBdhHKZoPCNwLNSUTiGdTC3zkphpeenX6frrR53Uk6dSMysjPRaeI6V5O3hJRQGxERsRDJCLcYkhYOImg+Eo+Pn98V1Dm813YAY2e+0N7+fjsR/MrLX2nvfsNezP+n7Xd32XuTsiIiKyq3ZpBxARESlEKpAiIiIRKpAiIiIRKpAiIiIRKpAiIiIRKpAiIiIRpWkHyKfjjjvOy8vL044hIiIF4uWXX17v7sfH1h1SBbK8vJy6urq0Y4iISIEws5UtrVMXq4iISIQKpIiI5NWOHTs45ZRTGDt2LADTpk2jsrKSiooKampqeP311wG47rrrqKiooKKigv79+3PMMcfkNech1cUqIiLpu+222xg4cCBbtmwB4Morr+SRRx5h4MCB/PSnP+V73/se99xzD7fccsvO59x+++288sorec2pFqSIiORNfX09jz32GJ/73Od2LjOzncXyjTfeoGfPnrs977777uP888/PW05QC1JERPLo2muv5eabb6axsXHnsl/+8pecffbZtG/fnk6dOvHiiy/u8pyVK1eyfPlyTj/99LxmVQtyP2X3pZ922mk7+8x79uzJxz/+cQA2bdrEuHHjGDp0KB/4wAeYP39+iqlFRPJv6tSpdO3aleHDh++y/JZbbuHxxx+nvr6eSy65hOuvv36X9VOmTOETn/gEJSUl+YyrFuT+yu5Lf/7553euGz9+PB/72McAuPHGG6moqOChhx5i0aJFXHXVVUybNi2VzCIiaXjhhRd49NFHefzxx3nrrbfYsmUL55xzDosWLWLEiBEAnHfeeZx11lm7PG/KlCnccccdec+rFuR+iPWlN9uyZQvPPvvszhbkwoULd3YPnHTSSaxYsYJ169blM66ISKpuuukm6uvrWbFiBVOmTOH000/nkUce4Y033mDJkiUAPP300wwcOHDncxYtWsSmTZuorq7Oe161IPdDrC+92cMPP8yHPvQhOnXqBMCwYcN48MEHOe2003jppZdY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EZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUgREZEIFUiRfXTppZfStWtXhgwZsnPZxIkT6dWrFxUVFVRUVPD444+nmFBE9ocKpMg+uvjii3nyySd3W37dddcxd+5c5s6dy9lnn51CMhHJBRVIkX00cuRIunTpknaMg0asRf6Vr3yFk046iaFDhzJu3Dg2b96cXkDiGe+//34GDx5Mu3btqKurSzHdPxTDe1kMVCBFcuwnP/kJQ4cO5dJLL2XTpk1pxykasRb5mDFjmD9/Pq+++ir9+/fnpptuSildEMs4ZMgQHnzwQUaOHJlSqt0Vw3tZDFQgRXLoyiuv5M9//jNz586lR48efPnLX047UtGItcjPOOMMSktLAfjgBz9IfX19GtF2imUcOHAgAwYMSClRXDG8l8WgNO0AIntTXl5Ox44dKSkpobS0tGC6sWK6deu28+vLL7+csWPHppjm4HL33Xdz3nnnpR3joKD3snVUIKUoPPfccxx33HFpx9irNWvW0KNHDwAeeuihXc4Byb674YYbKC0t5cILL0w7StHTe9l6KpCHqFWrVnHRRRexbt06zIwJEyZwzTXXpB2rqJx//vlMnz6d9evX07t3b77zne8wffp05s6di5lRXl7OnXfemXbMXVx66aVMnTqVrl27Mn/+/LTjtMo999zD1KlTmTZtGmaWdpyipveybVQgD1GlpaX88Ic/pLKyksbGRoYPH86YMWMYNGhQ2tF2Y2acccYZmBlXXHEFEyZMSDsSAPfdd99uyy677LIUkrTexRdfzNVXX81FF12UdpRWefLJJ7n55puZMWMGRx55ZNpxiprey7bTIJ1DVI8ePaisrASgY8eODBw4kNWrV6ecKm7WrFnMmTOHJ554gjvuuIOZM2emHaloFfKlKeeffz7V1dUsXryY3r17c9ddd3H11VfT2NjImDFjqKio4POf/3zBZXzooYfo3bs3tbW1nHPOOZx55pmpZmwpZ6G9l8VALUhhxYoVvPLKK4wYMSLtKFG9evUCoGvXrowbN46XXnop70PqL78196856drcv2YxK4YWeSwjwLhx4/KcZM+K4b0sBmpBHuK2bt3K+PHjufXWW+nUqVPacXbz5ptv0tjYuPPrp556SgNfRCQv1II8hL3zzjuMHz+eCy+8kHPPPTftOFHr1q3beXS+fft2LrjgAs4666yUU8n+KJbWuHJK0RZIM/sC8BWgB7AAuNbdn083VfFwdy677DIGDhzI9ddfn3acFvXr14958+alHUNEDkFF2cVqZucBtwE3AqcAfwCeMLMTUg1WRF544QUmT57Ms88+qztPHEJigzdEJK5YW5DXA/e4+6Tk+y+a2VnAlcDX04tVPGpqanD3tGOoeyjPWhpkIiK7K7oWpJkdDgwHnspa9RRwav4TiYjIwagYW5DHASXAuqzl64AP5z9OYVLL7NCU6/2ufS6HMiuEbra2MLOewGpglLvPzFj+beBCdx+Qtf0EoHnqlQHA4nxlJRTz9Xn8efuqGHIWQ0ZQzlxTztwphoyQ/5zvcffjYyuKsQW5HtgBdMta3g1Ym72xu/8C+EUecu3GzOrcvSqNn90WxZCzGDKCcuaacuZOMWSEwspZdOcg3f1t4GVgTNaqMYTRrCIiIvutGFuQAD8CJpvZS8ALwOeBnsDPU00lIiIHjaIskO7+GzM7FvgWYaKA+cDZ7r4y3WS7SaVrdx8UQ85iyAjKmWvKmTvFkBEKKGfRDdIRERHJh6I7BykiIpIPKpAiIiIRKpAiIiIRKpAHkJlZxtd6r0VEiog+tA8gd/dk7ljc/d3m5ZmFM23FUrjNrCTtDK1hZiWFtH9FZN9pFOsBYmbdgU8AlYQp7l4EHnD32lSDtSD5ULfMQp65zgvkD6W5oBdJTnP3HWlniTGzjsBRQANwJPD32HuaNjMrKdT3MFMR5ewBdAS2Eea0Xuvub6WbaldmVuru29POASqQB4yZPQa8D3gN+CvhTiNDgNeB/wAmp/0fysyuItxsera7b8tY3o7QAC6IPw4zuwGYAzzl7o0Zy0uAdwso538RDoR+6+6bMpaXEnIWRAEyswuASwgHbwC1wBPAM+6+ONmmYA42YOe+9kJ5D1tSyDmTm8xfSvgceocwI9mLwLPADHdvKqT93txrlOrnpLvrkeMHcDrwN6B38v0RwNGEIjkJWAZcn3LGGuBdYCZwL/AlYFjWNmXA/wV6FEDOV4DngR8QJqrP3KY98GPghALIuQT4C3A/8LFIzruBQSnmHAmsAH4NfAT4LKE4NgGrgKvSypaVswqYSuiFOSxrXSnJwX3ajyLKOZpwk4fvAwOTfT8p+XtdDdwAlKac8Z+AecDngMMj72U7wIAu+Xpf1YI8AMxsIjDS3U+PrOsEfJFwY+eR7j4nz/Gac9wCjACeIbQkugFbCf9hXiAUzm6E1kUnd9+aUs6bgfcDvyEc+Q4BjiHc3mw68L+ELsIXgaM9o4WZ55zfS3L+FDiR8IE0BHgbeBqYTJhkf3bKOX8LbHb3CVnLjwT+jfC3eau7/3sa+TLy/Ao4n1DMS4DnCL0uMzK2OZVwYHehp9TKKKKc/wM0uvsVWcsPAy4Dvgv8r7tflka+JMuvgE8Tuv2PJ3w2/cTdp2Zs80+Eg/axnodu2KKcaq4IPANcZWZnufuTmSvcfYuZfR8YlTxSKZCEW8rUufu3k4FEpxMmfD+FUDA/CZwETEurOCaOA5a5+8+Trt9KoJpQjP4FGAf0BZ5Mq+gkjgLWAI+5+3Yz+x2hQFYTWpdTgF7AEynnPJxQqAEwszJC9+/fgYnJe/wpM5vs7itSygjQn9CqqQM+QGj53mtmm4BHCQccFwJD0io6iWLJ+TbQxcyOcPe3zOwIYLu7vwP83MzeAb5iZoPdfUFKGcsJ04dOJbyX/wrcn2S7n9BL9CmgZz6KI6Au1gPxIHSl/ZpwvvFrwHDgqIz1xwD1wLkpZhwCfCSyvCvhiPgeQpfh2Sm/l92B0ZHlRwP/TDjyLYScHYAPtvC3MIjQbVQIOS8ENgCnZi0vSf7tAiwHqlPMWE74kLwy+f4IwkC3TwK3AX8kdAe/C/yLcrYq65mEWwV+Mmt5afLvUYRTA6NSytcT+CVwRfJ9CdCZcCD8teS9bErey4/mK5e6WA8QM+tD6Eb9MLCJsIPXEo7eq4EBnnVz53xrHi2WtBrakTGQxMw+Ctzr7kenmTFb9gCiJOd97t4h3WS7yh7sYGb/Qhi8c0SamQgfhJOAswjnHn9H6CXYnLy35wG/cPeOaeUEMLNuhIPKZVnLOwL9gC8A57n7MSnEy8xT8DmT/V4G3EjoQq8DfgXc7+4bzOw4Qm/Mre7eKcWcxwJHuvuqrOWlhGL5ReBL+XwvVSAPMDMbSuhXP5XQpd2ZcN/KH7n7n9LMFtN8uQfwAOHc44dTjtSi5AP9p0AXd/9k2nlakryn3wG6edY5oJTydAQuBs4hnGd+F3iDcNTeDZji7hPTyhcTOeB4GGhy9/PSS7WrZNSleUb3X6HlNLOxwAWEUynHE87lbycU0F+6+w9SjLdHyXu5w93H5+1nqkDmTnI+ZyhwLtAIvEo4z7c2WT+AcDL/bU/pjc/IOI7wobiA0BW8yt3fNLN27v5uctR2tLtvSCNns70Nm0/Wd3T3zXkNtnuOPV7KkRTzozzd84+7SP4eqwnncHsTugh/Arzs4cbkBcnMjgEeBL7q7n9MOQ5m1jF7vyb7uxMFkjPzACO5FnIQcAJh3x9BGF291Av0Wk4z6wDcDtzm7nPz9nNVIHPHzH5MKI5rCOdyygndqo8A3/cCuF9lJON7CNdpPkzoYlnW8rPzx8yGu/vLWcsK6rpHADOrcfdZWct2KZbNBx2pBGS3D8dSgKxWTqr5MnJk59zTAceRHgYWpcLMBgLXE1pirxPO380Fns/sIjSz9p5xjXGaCukax31hZmXu3pTPn1kU04wVAzMbROi2uhQ4y93fSxhgcjvhmqNFZnZF0t1WSBl7ELopPwosMLMJlkgx54nAH81svpn9yMxOgXDBsLt7Eu8wM/tAMgI3rZwnATPNrNHMpiRD0HH37Ukr3JJ85yTnqtJynJmNysi23czamVlZch66OWva0/ll52zOVZr592hh1pq/p/U3ambvJRxQDiBcwlMGnAb8H+CHZnZG87buvi3FnN3M7DNm1iXJ0nzw0a45k4WpEcvSyBfLuIftjvBkIoN8ZQM0ijVXD+AbwMyM70uz1t8ILCIMUVbGPef8NrAUuIVwvnY14TrHfwP6JNv0Ipw7653y+/kKYTDW84RzOWsJkxm8N9mma5KzT4o570gyrE2+HpS1voTQ3favJKNZCzRnu4ycqV3UDvyMcP1tx4xl3QgHnzOBN4HL0sqXken25P3cSLhM4mygLGubEwiFvUwZd3+oBZk7rwE9zOx9EI6AkyPf5lGLk4C/E2bcSEsxZIRwZP44YdaPywmFaD5hsFOtmf0vcCfwmrvXp5YyFOkXkiwfI1xLejdh8MtSM3uVcP3ja541Mi/P3g/8nPDBXgPMN7PXzewbZtbFw3mnzxJOA6R5DmpvOd/NyJnmXJ3vAea4e2PSAitx93Xufo+7j0x+h8stTL6QpirC/6EvEwYHPgQsN7Pbzax5msHLgc97nrsuiyZj2kc5B8sDOJZQgBYRroPa7WiHMI3SFcq4x4ylhFF2X89a3oUwoORq4LeEo87UjtIJra6zgS9kLT+ccMQ7lnDA8S5wSYo53wP8ntC6aUco6mcSWmh/SfK9QDiCv045W5X1miTTSVn7/fDk60GEa0n/OcWMPQktsgnJ96WEiT++SjhXugP4E6G1e40yxh8apJNDZtaT0C14MmEigJcIEwHXA1cRPvjL3f1NZWwdMzvMw2wfmcvOJVyG0sFTHKiRKTbQxczOJFxrmFpOC1MbjgNW+K7Tn7UnfEANJ1yrV0PoMkxlQEmx5Ewy9SWMTj0G+Hd3vztr/RDCDFnHpLjfjyL0aDS4++ysdUcSJgr5P4QBe6m8n0WRUQUytyxc7DqWMG1bP0J3YWdgBnCnu09JMR5Q2BlbGlGZjGpsHqTzA6DK3UfnPeA/8rR4e7CMbSYSZqQ5M2/B9iDJXOJZ3ZNm9t9ArzTfz0zFkNPCtaQ3EWYmOgx4ijDF5BBCEZ/r7hell3BXsRGsZnYP4Vz5aemk2lUhZlSBzAEz6024tRWE7oCFhPut9SNMQfZ3YL27b0wnYXFkhF1yGqFbbbEn15Em641wvm+1F8A1cHtiZqOBv3l6c1tGNRcgQhdWe8LAkpvc/XepBstSiDmTTO3cfUdy7v5kwvyrpxPmCV5OuDvOg5l/tynk3OMt65KW+SPAz9z9obyG+0eGws+oArl/zOxKwmUTwwhFZhmhu/I5wg2S0xycARRHRtgt55uE68vqCSNYH/bkXoVpswK6tm1Pkg+gjxFmTDmSMBp4hrs3ZGxTBnzY3R9LJ2Xx5GyJZVyfa2ZHu/sbaWfaGwt38ajyAr2BOxRGRhXI/ZB0Vb4O/JAw8u54wtyrowkn6v9KmDtwYaz7QBlbnXMgoVBel+RM7e7tZtaZMJDpMUJL4Q/N71nm+2fhQvI1ntIMP0kX4F3APxNa4vWAA28RutInu/uiNLJlKqKchxFmnVnpkdGUaf7fycqxx5yFoBgy7pTGyKCD5UGYPHd2C+tqCNfGLQOOU8aDKmcTYXDTDuDPhDuKDMjYpg/h+sh+Keb8JqGQvz/5/iTCZTI/I0yc/yhwfJrvZZHlvJbQq/FfhEk1upN1zShharmPkHXj5ALNeQ5ZNyVWxkjWNH94sT+AKwjn8oYk35dl7lDCcP+FwAXKeNDknES4zq0rYU7bmwiTGuwgdAVPIEwcsDXlnM8D10eWlxDOmS0l3EMztYxFlrOWcEri+WRfLwd+RDh4OzrZ5vPAi8pZ/BmbH5ooYP88QOgWutbChMVN7v52ck4Fd/8LsJkwEbQy7lnB50zOgy0kTOze4O6vuvvXCRc7n5msm0i4ge73U8xZSphYYbyZHZ8sK0lGCO9w95mED6DeZjZMOfea83jgHWCSh9GU7yF0C48lDBx61sy+SmgZzW7pdZSzODLuIu0KXawPwihLAz4OrCTcveMuwvVazVNifTpZXq6MxZ8zyVoGdE++LiGMaMxcP5qUp8BLcnyQ0P37fcJttrLX9wG2Ei6bUM49Z+wBXAecGVl3CmEygw3JflfOIs+Y+dAgnf1k4dY7JxDu9zgO+Kdk1VrCh/5kT/neesWQEQo/Z/NADDPrB7zp7usi674NXOzu/VLM2XwD7EsI8+uWEm6M/BvCDDBDCUfsg9z9/cq5d8klB+7ubyWXegC7TAB+A3C2u5+SVsYkR8HnLIaMzVQg94GZdQU+Q5g/cD3hesLNwCzCeajDCNfyPenuS5SxZUWY83qggTAx+RrCVFkPeriXphHmjfyru09NK2um5KDjYsIMSRWEVvhbhAEwN3nWDCZpKYacLY1UTWZ9mQP8l7un1rWekafgcxZDRlCB3CfJ7A6DCTP6byTME3oy0J/w4fmttP9DF0NGKPqcpxBGXdYD/+nuT6UWMGFhyrbGzA+fpKV2BGFCiCGE1m/af59FmzOyzRHAecB9ntKNposhZzFk3C2PCmTbJK2ERkIXwMyMZScAI4DPEWan+aS7z1HGgzpnb8I5tMsJgw3OTzNnkutOwiUoLxGuM9sS2aazu29K+brXgynnMZ7S9a4ZGQo+ZzFk3E3aJ0GL7UFoRfwJ+GAL68uAOkK3kDIeGjkPL5Cc5xMGN2wmXDN6J2Gi5/cB7ZNtOhBu9nuycu5TznHAezNyNk+HNkQ5iztj7KEWZBslJ5inEqbEugj4s+9+F4cvEm7FVJH/hMWRMcmgnDlkZpMI15XdTCg4nyV8AC0m3F9zGmFi+tvc/XDlVE5l3DNdB9lGHubg/CbhaOfXwEVm1sfMOsDOk8yjCNd3KeMeKGfuJNcULgc2u/syd/+Bu59MuAnxDMIH0m8Jd3CfrJzKqYytkHYTtlgfhEEEvyGMulxPGLxxN2F4+mxS7BoqpozKmdN8nUlu4kvo9rWs9ecRurkqlFM5lXHvD3Wx7qdk+P85hIvc3yK0Iu73AphguVkxZATlPBCSkaHm4fZMlxO6sI5MO1c25cytYshZFBlVIHPHWrjZbyEphoygnAeCmV1PmBT6P9POsifKmVvFkLNQM6pAihwiLNxmaEehF3TlzK1iyFmoGVUgRUREIjSKVUREJEIFUkREJEIFUkREJEIFUkREJEIFUkREJEIFUkREJOL/AzEml04tXgEsAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Run the noisy simulation\n", + "sim_thermal = AerSimulator(noise_model=noise_thermal)\n", + "\n", + "# Transpile circuit for noisy basis gates\n", + "circ_tthermal = transpile(circ, sim_thermal)\n", + "\n", + "# Run and get counts\n", + "result_thermal = sim_thermal.run(circ_tthermal).result()\n", + "counts_thermal = result_thermal.get_counts(0)\n", + "\n", + "# Plot noisy output\n", + "plot_histogram(counts_thermal)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:01:16.716582Z", + "start_time": "2019-08-19T17:01:16.709462Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
qiskit-terra0.24.0.dev0+dba2eff
qiskit-aer0.11.2
qiskit-ibmq-provider0.19.2
qiskit0.40.0
System information
Python version3.8.11
Python compilerClang 12.0.5 (clang-1205.0.22.11)
Python builddefault, Jul 27 2021 10:46:38
OSDarwin
CPUs8
Memory (Gb)64.0
Tue Feb 07 10:30:00 2023 JST
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "f8729fd834348017bca17aea688b306f536a675180840f7307eb909fff39c285" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/4_custom_gate_noise.ipynb b/docs/tutorials/4_custom_gate_noise.ipynb new file mode 100755 index 0000000000..8df57d9b9e --- /dev/null +++ b/docs/tutorials/4_custom_gate_noise.ipynb @@ -0,0 +1,552 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Applying noise to custom unitary gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This notebook shows how to add custom unitary gates to a quantum circuit, and use them for noise simulations in Qiskit Aer." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:09:18.154548Z", + "start_time": "2019-08-19T17:09:15.909907Z" + } + }, + "outputs": [], + "source": [ + "from qiskit import transpile, QuantumCircuit\n", + "import qiskit.quantum_info as qi\n", + "\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit_aer.noise import NoiseModel, amplitude_damping_error\n", + "\n", + "from qiskit.tools.visualization import plot_histogram" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating matrix operators\n", + "\n", + "We can use the `Operator` class in `qiskit.quantum_info` to represent arbitrary matrix operators. If the operator is unitary it can then be added to a quantum circuit and used for simulation on Qiskit Aer.\n", + "\n", + "Lets create two operators below for a CNOT gate and an iSWAP gate:\n", + "\n", + "$$\\mbox{CNOT} = \\left(\\begin{array} \n", + "& 1 & 0 & 0 & 0 \\\\ \n", + "0 & 0 & 0 & 1 \\\\ \n", + "0 & 0 & 1 & 0 \\\\ \n", + "0 & 1 & 0 & 0\n", + "\\end{array}\\right), \\quad\n", + "\\mbox{iSWAP} = \\left(\\begin{array} \n", + "& 1 & 0 & 0 & 0 \\\\ \n", + "0 & 0 & i & 0 \\\\ \n", + "0 & i & 0 & 0 \\\\ \n", + "0 & 0 & 0 & 1\n", + "\\end{array}\\right)$$\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:09:18.170527Z", + "start_time": "2019-08-19T17:09:18.166252Z" + } + }, + "outputs": [], + "source": [ + "# CNOT matrix operator with qubit-0 as control and qubit-1 as target\n", + "cx_op = qi.Operator([[1, 0, 0, 0],\n", + " [0, 0, 0, 1],\n", + " [0, 0, 1, 0],\n", + " [0, 1, 0, 0]])\n", + "\n", + "# iSWAP matrix operator\n", + "iswap_op = qi.Operator([[1, 0, 0, 0],\n", + " [0, 0, 1j, 0],\n", + " [0, 1j, 0, 0],\n", + " [0, 0, 0, 1]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:** The matrix is specified with respect to the tensor product $U_{b}\\otimes U_{a}$ for qubits specified by list `[a, b]`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using operators in circuits\n", + "\n", + "Let us demonstrate how these can be used in a circuit. We will consider an example of implementing a CNOT gate decomposed in terms of single-qubit gates and the iSWAP gate as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:09:55.343221Z", + "start_time": "2019-08-19T17:09:55.332156Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌─────┐ ┌────────┐┌─────┐┌───┐┌─────┐┌────────┐ \n", + "q_0: ┤ SDG ├─────┤0 ├┤ SDG ├┤ H ├┤ SDG ├┤0 ├─────\n", + " ├─────┤┌───┐│ iswap │└─────┘└───┘└─────┘│ iswap │┌───┐\n", + "q_1: ┤ SDG ├┤ H ├┤1 ├───────────────────┤1 ├┤ S ├\n", + " └─────┘└───┘└────────┘ └────────┘└───┘\n" + ] + } + ], + "source": [ + "# CNOT in terms of iSWAP and single-qubit gates\n", + "cx_circ = QuantumCircuit(2, name='cx')\n", + "\n", + "# Add gates\n", + "cx_circ.sdg(1)\n", + "cx_circ.h(1)\n", + "cx_circ.sdg(0)\n", + "cx_circ.unitary(iswap_op, [0, 1], label='iswap')\n", + "cx_circ.sdg(0)\n", + "cx_circ.h(0)\n", + "cx_circ.sdg(0)\n", + "cx_circ.unitary(iswap_op, [0, 1], label='iswap')\n", + "cx_circ.s(1)\n", + "\n", + "print(cx_circ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we have assigned an optional *label* of `\"iswap\"` to the unitary when it is inserted. This allows us to identify this unitary in a Qiskit Aer `NoiseModel` so that we can add errors to these custom unitary gates in noisy circuit simulations." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can confirm this circuit returns the correct output using the `Operator` class as a simulator for the circuit:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:09:58.954826Z", + "start_time": "2019-08-19T17:09:58.948275Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n", + " [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n", + " [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n", + " input_dims=(2, 2), output_dims=(2, 2))\n" + ] + } + ], + "source": [ + "# Simulate the unitary for the circuit using Operator:\n", + "unitary = qi.Operator(cx_circ)\n", + "print(unitary)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And to confirm the output is correct we can compute the average gate fidelity:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:01.198369Z", + "start_time": "2019-08-19T17:10:01.184222Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Average Gate Fidelity: F = 1.000000\n" + ] + } + ], + "source": [ + "f_ave = qi.average_gate_fidelity(cx_op, unitary)\n", + "print(\"Average Gate Fidelity: F = {:f}\".format(f_ave))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a custom unitary in a noise model\n", + "\n", + "The Qiskit Aer `AerSimulator` supports simulation of arbitrary unitary operators directly as specified by the `\"unitary\"` in the basis gates." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:03.174651Z", + "start_time": "2019-08-19T17:10:03.168643Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "'unitary' in AerSimulator().configuration().basis_gates" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This allows us to add noise models to arbitrary unitaries in our simulation when we identify them using the optional `label` argument of `QuantumCircuit.unitary`.\n", + "\n", + "We will now do this by creating a `NoiseModel` that includes a quantum error channel on our custom iSWAP gate. For our example we will create a 2-qubit error consisting of two single-qubit amplitude damping channels with different damping parameters. For now we will assume all the other circuit instructions are ideal." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:05.585654Z", + "start_time": "2019-08-19T17:10:05.574669Z" + } + }, + "outputs": [], + "source": [ + "# Error parameters\n", + "param_q0 = 0.05 # damping parameter for qubit-0\n", + "param_q1 = 0.1 # damping parameter for qubit-1\n", + "\n", + "# Construct the error\n", + "qerror_q0 = amplitude_damping_error(param_q0)\n", + "qerror_q1 = amplitude_damping_error(param_q1)\n", + "iswap_error = qerror_q1.tensor(qerror_q0)\n", + "\n", + "# Build the noise model by adding the error to the \"iswap\" gate\n", + "noise_model = NoiseModel()\n", + "noise_model.add_all_qubit_quantum_error(iswap_error, 'iswap')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that when we add an error to a custom label such as `\"iswap\"` the `NoiseModel` does not know what gate this label is supposed to apply to, so we must manually add the desired gate string to the noise model `basis_gates`. This ensures that the compiler will unroll to the correct basis gates for the noise model simulation. This can done using the `NoiseModel.add_basis_gates` function:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:06.301854Z", + "start_time": "2019-08-19T17:10:06.298595Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['cx', 'id', 'u3', 'unitary']\n" + ] + } + ], + "source": [ + "noise_model.add_basis_gates(['unitary'])\n", + "print(noise_model.basis_gates)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By default the basis gates of a noise model are `['cx','id','u3']` plus any standard `AerSimulator` basis gates that are added to the noise model." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulating a custom unitary noise model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let us first take our previous CX circuit and add an initial Hadamard gate and final measurement to create a Bell-state preparation circuit that we may simulator on the `AerSimulator` both for the ideal and noisy case:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:26.234163Z", + "start_time": "2019-08-19T17:10:26.224218Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ┌───┐┌────────────┐┌─┐ \n", + "q_0: ┤ H ├┤0 ├┤M├───\n", + " └───┘│ cx │└╥┘┌─┐\n", + "q_1: ─────┤1 ├─╫─┤M├\n", + " └────────────┘ ║ └╥┘\n", + "c: 2/════════════════════╩══╩═\n", + " 0 1 \n" + ] + } + ], + "source": [ + "# Bell state circuit where iSWAPS should be inserted at barrier locations\n", + "bell_circ = QuantumCircuit(2, 2, name='bell')\n", + "bell_circ.h(0)\n", + "bell_circ.append(cx_circ, [0, 1])\n", + "bell_circ.measure([0,1], [0,1])\n", + "print(bell_circ)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Ideal output\n", + "\n", + "Let's first see the ideal output. Since this generates a Bell-state we expect two peaks for 00 and 11." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:28.605669Z", + "start_time": "2019-08-19T17:10:28.467516Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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pJ/Y+QjpSv5X+RxNMzMrtw597VlPBDIzs1KS5kREVV3LfO1ZMzOznJw0zczMcnLSNDMzy8lJ08zMLCcnTTMzs5ycNM3MzHJy0jQzM8vJSdPMzCwnJ00zM7OcnDTNzMxyctI0MzPLyUnTzMwsJydNMzOznJw0zczMcnLSNDMzy8lJ08zMLCcnTTMzs5ycNDu43/72twwdOpQjjzyS6667bpflU6dOZf/99+e4447juOOO40tf+lKj6957770MHz6cTp06UV1d3SL7YWbWEpw0O7Da2lo++tGP8vDDD7Nw4ULuueceFi5cuEu9k08+mSeffJInn3yS//mf/2l03REjRvDAAw8wbty4Ft0fs/ausS+5BX/+85+pqKjgvvvu21F2yy23MGLECIYPH87NN9+8o3zevHmMGTOGY445hjPPPJONGzc26z7s7Zw0O7DZs2dz5JFHMnjwYDp37sz555/PL3/5yz1e9+ijj2bo0KHNGbpZh5P3S25tbS3XXHMN73jHO3aUzZ8/n8mTJzN79mzmzZvHb37zGxYtWgTAJZdcwnXXXcfTTz/N2WefzQ033NBi+7Q3ctLswFauXMnAgQN33B8wYAArV67cpd7MmTMZOXIk73znO1mwYEGT1jWz8sj7JffWW2/l3HPPpXfv3jvKnnnmGd70pjfRrVs3KisrGT9+PA8++CAAzz777I5RoQkTJnD//fe3zA7tpZw0O7CI2KVM0k73R40axbJly5g3bx5XXnklZ511Vu51zax88nxRXblyJQ8++CCXX375TuUjRoxg+vTprF+/ni1btjBlyhSWL1++Y9mvfvUrIM1HKJRb3Zw0O7ABAwbs9AZZsWIF/fr126lOz5496d69OwCnn34627ZtY926dbnWNbPyyfNFdeLEiVx//fVUVFTsVH700UdzzTXXMGHCBE477TRGjhxJZWUlAN/73ve4/fbbOeGEE9i0aROdO3duvp1oBypbOwBrPSeeeCKLFi1iyZIl9O/fn5/97Gf89Kc/3anO6tWr6dOnD5KYPXs2r732Gr169eKAAw5odF0zK588X1Srq6s5//zzAVi3bh1TpkyhsrKSs846i4svvpiLL74YgM985jMMGDAAgDe+8Y088sgjADz33HM89NBDLbE7e6+I6LB/J5xwQnR0Dz30UAwZMiQGDx4cX/nKVyIi4lvf+lZ861vfioiI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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create ideal simulator backend and transpile circuit\n", + "sim_ideal = AerSimulator()\n", + "tbell_circ = transpile(bell_circ, sim_ideal)\n", + "\n", + "ideal_result = sim_ideal.run(tbell_circ).result()\n", + "ideal_counts = ideal_result.get_counts(0)\n", + "plot_histogram(ideal_counts,\n", + " title='Ideal output for iSWAP bell-state preparation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Noisy circuit execution\n", + "\n", + "Finally, let's now simulate it with our custom noise model. Since there is a small amplitude damping error on the two-qubit gates we expect small additional peaks for the 01 and 10 outcome probabilities." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:31.078094Z", + "start_time": "2019-08-19T17:10:30.946144Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create noisy simulator and transpile circuit\n", + "sim_noise = AerSimulator(noise_model=noise_model)\n", + "tbell_circ_noise = transpile(bell_circ, sim_noise)\n", + "\n", + "# Run on the simulator without noise\n", + "noise_result = sim_noise.run(tbell_circ_noise).result()\n", + "noise_counts = noise_result.get_counts(bell_circ)\n", + "plot_histogram(noise_counts,\n", + " title='Noisy output for iSWAP bell-state preparation')" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:10:53.298595Z", + "start_time": "2019-08-19T17:10:53.290949Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
Qiskit0.25.0
Terra0.17.0
Aer0.8.0
Ignis0.6.0
Aqua0.9.0
IBM Q Provider0.12.2
System information
Python3.7.7 (default, May 6 2020, 04:59:01) \n", + "[Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs6
Memory (Gb)32.0
Fri Apr 02 12:12:41 2021 EDT
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/5_noise_transformation.ipynb b/docs/tutorials/5_noise_transformation.ipynb new file mode 100755 index 0000000000..9b9547c7db --- /dev/null +++ b/docs/tutorials/5_noise_transformation.ipynb @@ -0,0 +1,412 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Noise Transformation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This notebook shows how to use the Qiskit Aer utility functions `approximate_quantum_error` and `approximate_noise_model` to transform quantum noise channels into a different, more suitable, noise channel.\n", + "\n", + "Our guiding example is Clifford simulation. A Clifford simulator can efficiently simulate quantum computations which include gates only from a limited, non-universal set of gates (the Clifford gates). Not all quantum noises can be added to such simulations; hence, we aim to find a \"close\" noise channel which can be simulated in a Clifford simulator.\n", + "\n", + "We begin by importing the transformation functions from the Aer provider utilities" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:36.658036Z", + "start_time": "2019-08-19T17:13:34.394912Z" + } + }, + "outputs": [], + "source": [ + "from qiskit_aer.utils import approximate_quantum_error, approximate_noise_model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The name \"approximate\" suggests that these functions generate the closest (in the Hilbert-Schmidt metric) error possible to the given one.\n", + "\n", + "We demonstrate the approximation using several standard error channels defined in Qiskit." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:36.676647Z", + "start_time": "2019-08-19T17:13:36.672883Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "# Import Aer QuantumError functions that will be used\n", + "from qiskit_aer.noise import amplitude_damping_error, reset_error, pauli_error\n", + "\n", + "from qiskit.quantum_info import Kraus" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "A 1-qubit quantum channel is a function $\\mathcal{C}:\\mathbb{C}^{2\\times2}\\to\\mathbb{C}^{2\\times2}$ mapping density operators to density operators (to ensure the image is a density operator $\\mathcal{C}$ is required to be completely positive and trace preserving, **CPTP**).\n", + "\n", + "Given quantum channels $\\mathcal{E}_{1},\\dots,\\mathcal{E}_{r}$, and probabilities $p_1, p_2, \\dots, p_r$ such that $0\\le p_i \\le 1$ and $p_1+\\dots +p_r = 1$, a new quantum channel $\\mathcal{C}_\\mathcal{E}$ can be constructed such that $\\mathcal{C}_\\mathcal{E}(\\rho)$ has the effect of choosing the channel $\\mathcal{E}_i$ with probability $p_i$ and applying it to $\\rho$.\n", + "\n", + "The noise transformation function solves the following optimization problem: Given a channel $\\mathcal{C}$ (\"goal\") and a list of channels $\\mathcal{E}_{1},\\dots,\\mathcal{E}_{r}$, find the probabilities $p_1, p_2, \\dots, p_r$ minimizing $D(\\mathcal{C}, \\mathcal{C}_\\mathcal{E})$ according to some distance metric $D$ (the Hilbert-Schmidt metric is currently used).\n", + "\n", + "To ensure the approximation is honest, in the sense that the approximate error channel serves as an \"upper bound\" for the actual error channel, we add the additional honesty constraint:\n", + "\n", + "$$\\text{F}(I,\\mathcal{C})\\ge F(I,\\mathcal{C}_\\mathcal{E})$$\n", + "\n", + "Where $\\text{F}$ is a fidelity measure and $I$ is the identity channel." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example: Approximating amplitude damping noise with reset noise.\n", + "\n", + "**Amplitude damping** noise is described by a single parameter $0\\le \\gamma \\le 1$ and given by the Kraus operators:\n", + "\n", + "$$\\left(\\begin{array}{cc}\n", + "1 & 0\\\\\n", + "0 & \\sqrt{1-\\gamma}\n", + "\\end{array}\\right),\\left(\\begin{array}{cc}\n", + "0 & \\sqrt{\\gamma}\\\\\n", + "0 & 0\n", + "\\end{array}\\right)$$\n", + "\n", + "**Reset** error is described by probabilities $0\\le p, q\\le 1$ such that $p+q\\le 1$ and given by the Kraus operators:\n", + "\n", + "$$\\left(\\begin{array}{cc}\n", + "\\sqrt{p} & 0\\\\\n", + "0 & 0\n", + "\\end{array}\\right),\\left(\\begin{array}{cc}\n", + "0 & \\sqrt{p}\\\\\n", + "0 & 0\n", + "\\end{array}\\right),\\left(\\begin{array}{cc}\n", + "0 & 0\\\\\n", + "\\sqrt{q} & 0\n", + "\\end{array}\\right),\\left(\\begin{array}{cc}\n", + "0 & 0\\\\\n", + "0 & \\sqrt{q}\n", + "\\end{array}\\right)$$\n", + "\n", + "This can be thought of as \"resetting\" the quantum state of the affected qubit to $\\left|0\\right\\rangle$ with probability $p$, to $\\left|1\\right\\rangle$ with probability $q$, and do nothing with probability $1-(p+q)$.\n", + "\n", + "It is not too difficult to determine analytically the best values of $p,q$ to approximate a $\\gamma$ amplitude damping channel, see the details __[here](https://arxiv.org/abs/1207.0046)__. The best approximation is:\n", + "\n", + "$$p=\\frac{1}{2}\\left(1+\\gamma-\\sqrt{1-\\gamma}\\right), q=0$$" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:36.791986Z", + "start_time": "2019-08-19T17:13:36.704432Z" + } + }, + "outputs": [], + "source": [ + "gamma = 0.23\n", + "error = amplitude_damping_error(gamma)\n", + "results = approximate_quantum_error(error, operator_string=\"reset\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We only needed the above code to perform the actual approximation. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:36.812282Z", + "start_time": "2019-08-19T17:13:36.805559Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.8237482193044617, Circuit = \n", + " ┌───┐\n", + "q: ┤ I ├\n", + " └───┘\n", + " P(1) = 0.17625178069553835, Circuit = \n", + " \n", + "q: ─|0>─\n", + " \n", + " P(2) = 2.158685879252966e-23, Circuit = \n", + " ┌───┐\n", + "q: ─|0>─┤ X ├\n", + " └───┘\n", + "\n", + "Expected results:\n", + "P(0) = 0.8237482193696062\n", + "P(1) = 0.17625178063039387\n", + "P(2) = 0\n" + ] + } + ], + "source": [ + "print(results)\n", + "\n", + "p = (1 + gamma - np.sqrt(1 - gamma)) / 2\n", + "q = 0\n", + "\n", + "print(\"\")\n", + "print(\"Expected results:\")\n", + "print(\"P(0) = {}\".format(1-(p+q)))\n", + "print(\"P(1) = {}\".format(p))\n", + "print(\"P(2) = {}\".format(q))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We got the results predicted analytically.\n", + "\n", + "## Different input types\n", + "\n", + "The approximation function is given two inputs: The error channel to approximate, and a set of error channels that can be used in constructing the approximation.\n", + "\n", + "The **error channel** to approximate can be given as any input that can be converted to the `QuantumError` object. \n", + "\n", + "As an example, we explicitly construct the Kraus matrices of amplitude damping and pass to the same approximation function as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:37.367331Z", + "start_time": "2019-08-19T17:13:37.325594Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.8237482193044623, Circuit = \n", + " ┌───┐\n", + "q: ┤ I ├\n", + " └───┘\n", + " P(1) = 0.1762517806955376, Circuit = \n", + " \n", + "q: ─|0>─\n", + " \n", + " P(2) = 6.463899246563026e-23, Circuit = \n", + " ┌───┐\n", + "q: ─|0>─┤ X ├\n", + " └───┘\n" + ] + } + ], + "source": [ + "gamma = 0.23\n", + "K0 = np.array([[1,0],[0,np.sqrt(1-gamma)]])\n", + "K1 = np.array([[0,np.sqrt(gamma)],[0,0]])\n", + "results = approximate_quantum_error(Kraus([K0, K1]), operator_string=\"reset\")\n", + "print(results)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The **error operators** that are used to construct the approximating channel can be either given as a list, a dictionary or a string indicating hard-coded channels. \n", + "\n", + "Any channel can be either a list of Kraus operators, or 'QuantumError' objects.\n", + "\n", + "The identity channel does not need to be passed directly; it is always implicitly used.\n", + "\n", + "As an example, we approximate amplitude damping using an explicit Kraus representation for reset noises:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:38.083970Z", + "start_time": "2019-08-19T17:13:38.046909Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumError on 1 qubits. Noise circuits:\n", + " P(0) = 0.8237482193044617, Circuit = \n", + " ┌───┐\n", + "q: ┤ I ├\n", + " └───┘\n", + " P(1) = 0.17625178069553835, Circuit = \n", + " ┌───────┐\n", + "q: ┤ kraus ├\n", + " └───────┘\n", + " P(2) = 2.158685879252966e-23, Circuit = \n", + " ┌───────┐\n", + "q: ┤ kraus ├\n", + " └───────┘\n" + ] + } + ], + "source": [ + "reset_to_0 = Kraus([np.array([[1,0],[0,0]]), np.array([[0,1],[0,0]])])\n", + "reset_to_1 = Kraus([np.array([[0,0],[1,0]]), np.array([[0,0],[0,1]])])\n", + "reset_kraus = [reset_to_0, reset_to_1]\n", + "\n", + "gamma = 0.23\n", + "error = amplitude_damping_error(gamma)\n", + "results = approximate_quantum_error(error, operator_list=reset_kraus)\n", + "print(results)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note the difference in the output channel: The probabilities are the same, but the input Kraus operators were converted to general Kraus channels, which cannot be used in a Clifford simulator. Hence, it is always better to pass a `QuantumError` object instead of the Kraus matrices, when possible." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:13:57.085790Z", + "start_time": "2019-08-19T17:13:57.077225Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
qiskit-terra0.24.0.dev0+dba2eff
qiskit-aer0.11.2
qiskit-ignis0.7.1
qiskit-ibmq-provider0.20.0
qiskit0.41.0
System information
Python version3.8.11
Python compilerClang 12.0.5 (clang-1205.0.22.11)
Python builddefault, Jul 27 2021 10:46:38
OSDarwin
CPUs8
Memory (Gb)64.0
Wed Feb 15 14:17:23 2023 JST
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2023.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.11" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "f8729fd834348017bca17aea688b306f536a675180840f7307eb909fff39c285" + } + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/6_extended_stabilizer_tutorial.ipynb b/docs/tutorials/6_extended_stabilizer_tutorial.ipynb new file mode 100755 index 0000000000..77f0a297a3 --- /dev/null +++ b/docs/tutorials/6_extended_stabilizer_tutorial.ipynb @@ -0,0 +1,545 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Extended Stabilizer Simulator" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Introduction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Extended Simulator is a new method for classically simulating quantum circuits available in the latest release of [Qiskit-Aer](https://github.com/qiskit/qiskit-aer). \n", + "\n", + "This method is an implementation of the ideas published in the paper _Simulation of quantum circuits by low-rank stabilizer decompositions_ by Bravyi, Browne, Calpin, Campbell, Gosset & Howard, 2018, [**arXiv:1808.00128**](https://arxiv.org/abs/1808.00128).\n", + "\n", + "It uses a different representation of a quantum circuit, that gives it some unique capabilities. This notebook will give some examples of what the extended stabilizer method can do.\n", + "\n", + "For example:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:17:32.305227Z", + "start_time": "2019-08-19T17:17:32.300959Z" + } + }, + "outputs": [], + "source": [ + "from qiskit import QuantumCircuit, transpile\n", + "from qiskit_aer import AerSimulator\n", + "from qiskit.tools.visualization import plot_histogram\n", + "\n", + "import random" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:21:43.756467Z", + "start_time": "2019-08-19T17:21:43.742436Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "circ = QuantumCircuit(40, 40)\n", + "\n", + "# Initialize with a Hadamard layer\n", + "circ.h(range(40))\n", + "# Apply some random CNOT and T gates\n", + "qubit_indices = [i for i in range(40)]\n", + "for i in range(10):\n", + " control, target, t = random.sample(qubit_indices, 3)\n", + " circ.cx(control, target)\n", + " circ.t(t)\n", + "circ.measure(range(40), range(40))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We've created a random circuit with just 60 gates, that acts on 40 qubits. But, because of the number of qubits, if we wanted to run this with say the statevector simulator then I hope you have access to terabytes of RAM!" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:21:58.295935Z", + "start_time": "2019-08-19T17:21:58.246239Z" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Simulation failed and returned the following error message:\n", + "ERROR: [Experiment 0] Insufficient memory to run circuit \"circuit-2\" using the statevector simulator.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This succeeded?: False\n", + "Why not? ERROR: [Experiment 0] Insufficient memory to run circuit \"circuit-2\" using the statevector simulator.\n" + ] + } + ], + "source": [ + "# Create statevector method simulator\n", + "statevector_simulator = AerSimulator(method='statevector')\n", + "\n", + "# Transpile circuit for backend\n", + "tcirc = transpile(circ, statevector_simulator)\n", + "\n", + "# Try and run circuit\n", + "statevector_result = statevector_simulator.run(tcirc, shots=1).result()\n", + "print('This succeeded?: {}'.format(statevector_result.success))\n", + "print('Why not? {}'.format(statevector_result.status))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The Extended Stabilizer method, in contrast, handles this circuit just fine. (Though it needs a couple of minutes!)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:22:26.158636Z", + "start_time": "2019-08-19T17:22:22.534910Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This succeeded?: True\n" + ] + } + ], + "source": [ + "# Create extended stabilizer method simulator\n", + "extended_stabilizer_simulator = AerSimulator(method='extended_stabilizer')\n", + "\n", + "# Transpile circuit for backend\n", + "tcirc = transpile(circ, extended_stabilizer_simulator)\n", + "\n", + "extended_stabilizer_result = extended_stabilizer_simulator.run(tcirc, shots=1).result()\n", + "print('This succeeded?: {}'.format(extended_stabilizer_result.success))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How does this work?\n", + "If you're interested in how exactly we can handle such large circuits, then for a detailed explanation you can [read the paper!](https://arxiv.org/abs/1808.00128)\n", + "\n", + "For running circuits, however, it's important to just understand the basics.\n", + "\n", + "The Extended Stabilizer method is made up of two parts. The first is a method of decomposing quantum circuits into _stabilizer circuits_, a special class of circuit that can be efficiently simulated classically. The second is then a way of combining these circuits to perform measurements.\n", + "\n", + "The number of terms you need scales with the number of what we call _non-Clifford Gates_. At the moment, the method knows how to handle the following methods:\n", + "```python\n", + "circ.t(qr[qubit])\n", + "circ.tdg(qr[qubit])\n", + "circ.ccx(qr[control_1], qr[control_2], qr[target])\n", + "circ.u1(rotation_angle, qr[qubit])\n", + "```\n", + "\n", + "The simulator is also able to handle circuits of up to 63 qubits.\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One thing that's important to note is these decompositions are approximate. This means that the results aren't exactly the same as with the State Vector simulator." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:23:52.873028Z", + "start_time": "2019-08-19T17:23:52.863305Z" + } + }, + "outputs": [], + "source": [ + "small_circ = QuantumCircuit(2, 2)\n", + "small_circ.h(0)\n", + "small_circ.cx(0, 1)\n", + "small_circ.t(0)\n", + "small_circ.measure([0, 1], [0, 1])\n", + "# This circuit should give 00 or 11 with equal probability...\n", + "expected_results ={'00': 50, '11': 50}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:23:54.879606Z", + "start_time": "2019-08-19T17:23:53.249255Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100 shots in 0.4958779811859131s\n" + ] + } + ], + "source": [ + "tsmall_circ = transpile(small_circ, extended_stabilizer_simulator)\n", + "result = extended_stabilizer_simulator.run(\n", + " tsmall_circ, shots=100).result()\n", + "counts = result.get_counts(0)\n", + "print('100 shots in {}s'.format(result.time_taken))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:23:55.222602Z", + "start_time": "2019-08-19T17:23:55.101958Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAnEAAAE6CAYAAAB5+gQKAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de1xVZd7//9eHk2KAIQoCYiCa5iE8YI5laplj6Yw16jimU3l3uu1g08NfBzvdU02lNZ3LsmwmLb9pp3vGqcypqVHHcizUMLXUFEwFFdE8JHK8fn/sDTchCIzIZun7+XjwYK+1rrX2Z0E73l5rXdcy5xwiIiIi4i1BgS5AREREROpPIU5ERETEgxTiRERERDxIIU5ERETEgxTiRERERDxIIU5ERETEg0ICXUAgtW7d2iUnJwe6DBERaUJWrly5xznXptJybEhIyCtAd9T5IY2nDFhbUlJybZ8+fXZX1+CUDnHJyclkZGQEugwREWlCzGxr5eWQkJBX2rZte1abNm32BQUFaXJVaRRlZWWWl5fXdefOna8AI6tro39RiIiIHFv3Nm3aHFCAk8YUFBTk2rRpsx9fD3D1bRqxHhERES8KUoCTQPD/d1djVlOIExEREfEghTgREREJuGeffTamT58+nQNdh5ec0gMbRERE/hPXPU2fE3n8Wbeysi7tEhMTe+Tn54dWvtw7ZsyY/Ndee+37E1dd9c4555zO48aNy58yZcqexn7vU5VCnIiIiIfNnz9/02WXXXYw0HVI49PlVBERkZPMhAkT2l988cUdypdvuOGGxP79+59ZVlbG+++/HxkXF3f21KlT20ZHR6clJib2ePHFF1uVty0oKLDrr7++XXx8fI+YmJi08ePHtz906JCVb587d+7pXbp06RoREdErKSmp+zvvvBM1efLkxJUrV0bcdddd7Vu0aNHryiuvbA+wevXq5ueee26nli1b9kxOTu7+yiuvRJcfZ+fOncEXXnhhx4iIiF49evQ4a/Pmzc0a6+dzslCIExEROcnMnDlz+7ffftvi2WefjVm0aFHE/PnzW8+dOzcrKMj3Zz8/Pz90z549ITk5OWtefvnlrClTppyRmZnZDOCmm25q99133zX/6quv1n/33Xdf79y5M+zOO+9MAPjnP//Z4oYbbkieNm3a9v37969eunTphtTU1KLnnntuR58+fQ5Nmzbt+8OHD69+7bXXvj9w4EDQJZdccuZvfvObvXv27Plq7ty5W26//fb2GRkZzQGuvfbaM5o3b16Wk5OT+ec//zlr3rx5rQP2A/MohTgREREPGz9+fMfIyMie5V9PPPFE68jIyLJXX311y3333Zd09dVXp0yfPn1bampqceX9nnzyyZzw8HA3YsSIQxdccMH+uXPntiorK2PevHmtn3vuuW1xcXGl0dHRZXfffXfuX//611YAs2bNajN27Nj8X/3qVweCg4NJSUkp7tWr15Hq6nrzzTdbJiYmFv7ud7/LDw0NZcCAAYcvueSSH+bNmxddUlLCokWLTn/44YdzoqKiyvr27Xtk7Nix+Y3x8zqZ6J44ERERD3vjjTe+q+6euAsuuOBwUlJSYX5+fsg111yzt/K2yMjIkqioqLLy5aSkpKKcnJzQ3NzckCNHjgT97Gc/O6ty+9LSUgPYsWNH6LBhw/bXpa6tW7eGrVmz5rTIyMielY/zq1/9Kj8nJyektLTUUlNTi8q3nXHGGYXLly+PqPuZi0KciIjISWjatGltioqKLDY2tvi+++5rO23atJ3l2w4ePBhy4MCBoPIgt3379rCuXbsWtG3btqR58+Zla9asWZeSklJc9ZiJiYnFmzdvbl7d+5nZTyZETkpKKu7bt+/Bzz//fFPVtiUlJQQHB7vNmzeHlffkff/997onrp50OVVEROQks2bNmmbTpk1LnDNnTtbcuXOzZsyY0fbzzz8Pr9zmtttuSzhy5IgtWrQo4tNPP205fvz4fcHBwYwbN27PTTfdlLRjx44QgKysrNB33303CuC6667Le+utt2IWLFgQWVpaSlZWVujq1aubA7Rp06Zky5YtFUFs7NixP2RnZzefMWNGq8LCQissLLQlS5a0WLVqVfOQkBCGDRv2w3333Zdw8ODBoJUrVzZ/6623YhrzZ3QyUE+ciIhIPdV1HrfGMG7cuE6V54k777zzDuzcuTPs5ptv3tm/f/8CgHvuuWfHxIkTUzIzM78BiImJKY6Oji6Jj48/u3nz5mWPP/741vIesRkzZmy/4447Evr163fWDz/8EBIbG1t09dVX5wEHLrjggsMzZszIvv3225PGjx/fLCYmpvipp576vlevXkduvfXWXddee23Ka6+91mbUqFH5s2fP3vbhhx9u/N3vfpd07733JjnnrEuXLoeffPLJbQCzZs36fsKECcnx8fFpKSkpR8aNG7dn2bJlkQH4EXqWOXfqPg4uPT3dZWRkBLoMERFpQsxspXMuvXw5MzMzOy0t7aSZwPb999+PvOaaa1J27dq1JtC1SO0yMzNbp6WlJVe3TZdTRURERDxIIU5ERETEgxTiRERETiG/+MUvDupS6slBIU5ERETEgxTiRERERDxIIU5ERETEgxTiRERERDxIIU5ERETEgxTixLMWLVpE586d6dixI9OnTz9q++LFi2nZsiU9e/akZ8+ePPjggxXbnnrqKbp160b37t25/PLLOXLkCAB79+5l6NChdOrUiaFDh7Jv375GOx8RkZPF+++/HxkXF3d2Y+/b0O81fvz49rfffnt8dW07duzY7f333w/oEyb02C3xpNLSUm666SY+/vhj2rVrR9++fRk5ciRdu3b9Sbvzzz+f999//yfrduzYwbPPPsv69esJDw9n7NixzJ8/n4kTJzJ9+nSGDBnC1KlTmT59OtOnT+fRRx9tzFMTEQ/Y3Pn+Pify+Kkb7q/TY70SExN75Ofnh1Z+7NaYMWPyX3vtte+PtZ+Xn9owd+7c0x966KGE7du3NwsNDS3r0qVLwezZs7M7d+5cNGXKlITNmzc3W7BgQVZDvNcbb7xR48/xu+++W9cQ73E8FOLEk7744gs6duxIhw4dABg3bhwLFiw4KsTVpKSkhIKCAkJDQzl8+DAJCQkALFiwgMWLFwNw1VVXMXjwYIU4EWnS5s+fv+myyy47GOg6GsPatWub3XDDDclz587d/Mtf/vLg/v37gxYsWBAVFOT9C4vFxcWEhobWax/vn7Wcknbs2EFSUlLFcrt27dixY8dR7ZYvX05aWhqXXHIJ69b5/tGUmJjIbbfdRvv27YmPj6dly5b8/Oc/B2DXrl3Ex8cDEB8fz+7duxvhbEREGt6ECRPaX3zxxR3Kl2+44YbE/v37n3ngwIGgMWPGdMrLywtt0aJFrxYtWvTKzs4OLS0t5e67726blJTU/fTTT+85fPjwDrt27QoG2LBhQ5iZ9Xnuuedi4uPje0RHR6fdeeedbcuPfejQIRs9enRyVFRUz9TU1G4rVqxoUbmW7Ozs0GHDhqVGR0enJSYm9njooYdi67pvZV9++WWLxMTEoksvvfRgUFAQ0dHRZRMnTvyhU6dORe+8807Uc8891/aDDz6IbtGiRa/OnTt3BXjmmWdiOnTo0O20007r1a5dux5//OMfW1c97tSpU9uW1/biiy+2Kl8/evTo5FtuuSWhuloSExN7/PWvf40EiIyM7Fn+swwPD+9lZn02bNgQBjBv3ryWXbp06RoZGdmzV69eXVasWBFe+Rj33HNP2zPPPLPraaed1ru4uPhYv9KjKMSJJznnjlpnZj9Z7t27N1u3biUzM5PJkydz2WWXAbBv3z4WLFhAVlYWOTk5/Pjjj8ydO7dR6hYRaSwzZ87c/u2337Z49tlnYxYtWhQxf/781nPnzs2Kiooqe+eddza1adOm+PDhw6sPHz68Ojk5ufjhhx+O/eCDD05fvHjxhtzc3MzTTz+99Nprr21f+ZifffZZxKZNm9YuXLhw41NPPZWwatWq5gB33HFHQnZ2drNNmzZ9/fe//33j/PnzK4JSaWkpI0aM6NijR4/Dubm5az7++OMNM2fOjHv33Xejatu3qn79+v2YlZXV/Jprrkl67733Ivfv31+RY8aMGXNg8uTJO0eMGLHv8OHDqzds2LAeIC4uruS999777uDBg6tfeumlrP/5n/9JWrZsWUVQzM/PD92zZ09ITk7OmpdffjlrypQpZ2RmZjarz8/64MGDX5X/LK+55prdffr0OZScnFy8bNmyFjfddFPyCy+8sHXfvn1fXX311XmjRo3qWFBQUPEH69133221cOHCTXv37l2tnjg5JbRr145t27ZVLG/fvr3ikmi5qKgoIiIiABg+fDjFxcXs2bOHf/zjH6SkpNCmTRtCQ0MZNWoUn3/+OQBxcXHk5uYCkJubS2xsLCIiTdn48eM7RkZG9iz/euKJJ1oDREZGlr366qtb7rvvvqSrr746Zfr06dtSU1Nr7OqZPXt2mwcffHBHampqcXh4uJs2bVrOhx9+GF25d+jhhx/OiYiIcP379y/o3LlzQUZGRjjA3/72t1Z33XVXblxcXGnHjh2L//u//3tX+T5Lliw5be/evSGPP/54bvPmzV3Xrl2Lrrjiirx58+a1qm3fqrp27Vq0cOHCDbm5uaFXXnllh9jY2J6jR49Orhzmqho3btz+bt26FQYFBTFixIhD55133oF//vOfEZXbPPnkkznh4eFuxIgRhy644IL9c+fObVXT8Y5l1qxZ0X/5y19a/e1vf9vcrFkz9+KLL7a+4oor8i688MIfQ0JCmDx5cn5oaKj79NNPTyvfZ9KkSbs6duxYHBERcXTvRC10T5x4Ut++fdm0aRNZWVkkJiYyf/583njjjZ+02blzJ3FxcZgZX3zxBWVlZcTExNC+fXv+/e9/c/jwYcLDw/nkk09IT08HYOTIkcyZM4epU6cyZ84cLr300kCcnohInb3xxhvf1XRP3AUXXHA4KSmpMD8/P+Saa67Ze6zj5Obmhk2YMKGjmVWEieDgYLZv317RPdS+ffuKRBceHl526NChYIC8vLzQ5OTkovJtKSkpFa+3bNkSlpeXFxYZGdmzfF1ZWZmlp6cfrG3f6gwZMuTHIUOGbAFYsmRJiwkTJqTefffd8TNmzDj6nhrgrbfeinr44YcTsrOzm5eVlXHkyJGgbt26FZRvj4yMLImKiiorX05KSirKycmpX5cY8Nlnn4Xfcccd7d9///2NCQkJJQDbt28P+9///d+YP//5zxU9AiUlJbZ9+/aw8uUzzjijftdQK1GIE08KCQnh+eefZ9iwYZSWlnL11VfTrVs3Zs6cCcCkSZN45513ePHFFwkJCSE8PJz58+djZvTr148xY8bQu3dvQkJC6NWrF9dffz0AU6dOZezYsfzpT3+iffv2vP3224E8TRGR4zJt2rQ2RUVFFhsbW3zfffe1nTZt2k6AykGtXFxcXPHLL7+c9fOf//zHqtvK7++qSevWrYuzs7PD0tPTjwBkZ2dXtE9OTi5KTEws3Lp169r67lubQYMGHR4+fPi+b775Jry68yooKLCrrroq9YUXXsgeP378D82aNXMXXXRRauVbcg4ePBhy4MCBoPIgt3379rCuXbsWUA85OTkhY8eO7fjYY499f95551Xsm5iYWHzLLbfkPvrooztr2re630Vd6XKqeNbw4cPZuHEjmzdv5p577gF84W3SpEkA3Hzzzaxbt47MzEz+/e9/c+6551bs+8ADD/Dtt9+ydu1aXn/9dZo1893+EBMTwyeffMKmTZv45JNPaNXqP+pRFxEJuDVr1jSbNm1a4pw5c7Lmzp2bNWPGjLaff/55OEBCQkLJ/v37Q/Lz84PL2//Xf/3X7nvvvbfdxo0bw8AXTObOnXt6Xd5r5MiRex999NH4vLy84M2bN4e+9NJLFT1PgwcP/jEiIqL0nnvuaXvo0CErKSnhyy+/bL5kyZIWte1b1d///veIJ554ovWOHTtCAFavXt38o48+Or1v376HwHf/2/bt28NKS0sBOHLkiBUVFQXFxsYWh4aGurfeeivqs88+i6p63Ntuuy3hyJEjtmjRoohPP/205fjx4+s8SWhxcTEjR45MHTVq1N7rrrvuJ/tNmjQpb86cObGffvrpaWVlZRw4cCBo/vz5Lfft29cg+avRe+LM7EbgdiAeWAfc6pz71zHaG/A7YBKQAuwF5jjnplZqMwh4EugG5ACPOedmnrCTEBGRU1pd53FrDOPGjetUeZ64884778DChQs3T5gwocPNN9+8s3///gUA99xzz46JEyemZGZmftOrV68jv/zlL/empqb2KC0t5euvv15377337nbO2c9//vMz8/LyQlu1alV86aWX7vvtb3/7Q201PPbYY7lXXnll+w4dOvSIjY0tHj9+/J6XX345DnxXTj744IPvJk+e3C45OfnsoqIiS0lJOfLAAw/sqG3fqlq1alXywQcfnP7II48kFhQUBEVHR5eMHDly74MPPrgL4Morr9z75ptvtoqOju7Zrl27wvXr13/z0EMPfX/llVemFhUV2ZAhQ/YPGTJkf+VjxsTEFEdHR5fEx8ef3bx587LHH398a69evY7U9ee/ZcuWsJUrV0asW7euxZ/+9KeKAJqZmblu4MCBh59//vnsW265pf3WrVubNW/evCw9Pf3QsGHDGmRKGKtulN+JYma/AeYCNwLL/N//C+jqnKt2Qj0zexL4Bb7g9zXQEoh3zi30b08B1gJ/Bl4ABvi/j3POvXusetLT011GRkYDnJmIiJwszGylcy69fDkzMzM7LS1tTyBrklNXZmZm67S0tOTqtjV2T9wUYLZzbpZ/ebKZXQzcANxVtbGZdQYmA2c7576ptGl1pdeTgBzn3GT/8jdm1g+4DThmiBMRERHxqka7J87MwoA+wEdVNn0EnHv0HgBcCmwBLjazLWaWbWZzzKzy9fL+1Rzz70C6mdV7dImIiIiIFzRmT1xrIBioOv/LLuCiGvbpAJwBjAMmAg54HHjPzPo758qAtsA/qjlmiP89cytvMLPrgesBEhISKh6x1KFDByIjI8nMzAR8N7h369aNpUuXAr5r+gMGDGDVqlUcOHAAgPT0dHbt2lUxX1mnTp1o1qwZa9f6BuDExsZy5plnsmzZMgCaNWtG//79ycjI4NChQwD069eP7du3VzxtoHPnzgQHB7N+/XoA2rZtS0pKCsuXLwcgPDycfv36sWLFCgoKfANg+vfvT1ZWFjt3+ga/dO3aldLSUjZs2AD4nlDQrl07VqxYAUBERATp6eksX76cwsJCAAYMGMDGjRsrnlDQvXt3CgsL2bRpEwBJSUnExcVRfvk5KiqK3r17s2zZMkpKSgAYOHAg69atIz8/H4C0tDQOHjzIli1bAEhOTqZVq1asWrUKgOjoaNLS0liyZAnOOcyMQYMGkZmZWfHg+d69e7N3716ys7P1e9LvSb8n/Z4a5fck4hWNdk+cmSUAO4CBlQcymNnvgcudc12q2edl4Dqgs3Nuo3/dmcAG4GfOuRVmthF43Tn3h0r7DQIW47t3rsZhvbonTkREqtI9cdKUHOueuMacYmQPUIqv56yyWI7unSuXC5SUBzi/TUAJUP4okJ01HLMEyD+egkVERICysrIyq72ZSMPy/3dXVtP2RgtxzrkiYCUwtMqmocDnNez2GRBiZqmV1nXAd6l0q395OUdfjh0KZDjn/uNZkEVERPzW5uXltVSQk8ZUVlZmeXl5LfHNwFGtxh6d+iTwupl9gS+gTQISgJkAZjYNOMc5N8Tf/h/AKuDPZnarf93TwAqg/DroTOBmM3saeAk4D9/9c5ef8LMREZGTXklJybU7d+58ZefOnd3RJPnSeMqAtSUlJdfW1KBRQ5xz7k0ziwHuxTfZ71pguHOuvFctHkit1L7MzH4BPAssBQqAj4Ep/kENOOeyzGw48BS+qUpygFtqmyNORESkLvr06bMbGBnoOkSqatTJfpsaDWwQEZGqqg5sEGmq1C0sIiIi4kGN/uxUObVs7nx/oEtoslI33B/oEkRExMPUEyciIiLiQQpxIiIiIh6kECciIiLiQQpxIiIiIh6kECciIiLiQQpxIiIiIh6kECciIiLiQQpxIiIiIh6kECciIiLiQQpxIiIiIh6kECciIiLiQQpxTdxDDz1EWFgYoaGhXHzxxUdtf/rppzEzwsPDCQ8P58ILL6x1382bNxMTE0NYWBgxMTFkZWU1yrmINAX6TInIyUIhrgkrKirigQce4KOPPmLfvn0sWbKEv/3tb0e1i42NpaCggIKCAj799NNa97388svp27cvRUVF9O3bl3HjxjXqeYkEij5TInIyUYhrwmbPnk3Lli0ZPHgwERERDBw4kBkzZhz3vl999RXTp08HYPr06axevfqEnYNIU6LPlIicTBTimrANGzYQExNTsZySkkJubu5R7fLy8ggPD6dNmzYsWLCg1n2Li4vp2bMnAD179qSkpOREnoZIk6HPlIicTBTimjDnXK1txowZQ05ODgUFBdx88838+te/rvO+IqcafaZE5GSiENeEdenShfz8/IrlrKws4uPjf9KmXbt2tG3bFoDf//73lJWVsWHDhmPuGxoayldffQX4LgOFhISc6FMRaRL0mRKRk4lCXBN25ZVXsn//fpYuXcqhQ4dYunQpN95440/arFmzhrKyMgBeffVVADp16nTMfdPS0pg6dSoAU6dOrbgMJHKy02dKRE4m+udiE9a8eXPuvfdeLrroIpxzDB48mEsvvZTx48cD8MYbb/CHP/yB9957DzMjODiYGTNmEBQUVOO+5fv169ePsLAwIiIi+PLLLwN5miKNRp8pETmZ2Kl8n0d6errLyMgIdBkntc2d7w90CU1W6ob7A12CiFTDzFY659IDXYdIbXQ5VURERMSDFOJEREREPEghTkRERMSDFOJEREREPEghTkRERMSDFOJEREREPEghTkRERMSDFOJEREREPEghTkRERMSDFOJEREREPEjPTm0A1z0d6AqarqmBLkA8SZ+pms26NdAViEhToZ44EREREQ9SiBMRERHxIIU4EREREQ9SiBMRERHxIIU4EREREQ9SiBMRERHxIIU4EREREQ9SiBMRERHxIIU4EREREQ9SiBMRERHxIIU4EREREQ9SiBMRERHxIIU4EREREQ9SiBMREQAWLVpE586d6dixI9OnT6+x3ZdffklwcDDvvPMOANu2beOCCy7grLPOolu3bjzzzDMVbffu3cvQoUPp1KkTQ4cOZd++fSf8PEROFQpxIiJCaWkpN910Ex9++CHr169n3rx5rF+/vtp2d955J8OGDatYFxISwhNPPME333zDv//9b2bMmFGx7/Tp0xkyZAibNm1iyJAhxwyHIlI/jR7izOxGM8sysyNmttLMzq/jfp3M7KCZHaqyfrCZuWq+upyYMxAROfl88cUXdOzYkQ4dOhAWFsa4ceNYsGDBUe2ee+45Ro8eTWxsbMW6+Ph4evfuDUBkZCRnnXUWO3bsAGDBggVcddVVAFx11VX89a9/bYSzETk1NGqIM7PfAM8AjwC9gM+BD82sfS37hQHzgaXHaNYN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eK+FQIlzbuSx2qWnp7vyP3bH47qnj/sQJ62pL94f6BKarNQN9we6hCZLn6ma6TNVs4b6TJnZSudceoMcTOQEasyBDXuAUqBtlfWxHN07d7xWAJ0a+JgiIiIiTUajhTjnXBGwEhhaZdNQfKNUG1JPfJdZRURERE5KjT069UngdTP7AvgM38jTBGAmgJlNA85xzg0p38HMuuK71601EGFmPQGcc1/5t98KZAPr/O1+C1wGjG6cUxIRERFpfI0a4pxzb5pZDHAvvrnc1gLDnXNb/U3iOXqUwELgjErLq/3fzf89DHgcSAQK8IW5Ec65hQ1/BiIiIiJNQ2P3xOGcewF4oYZtE6tZl1zL8R4DHmuI2kRERES8Qs9OFREREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD2r0EGdmN5pZlpkdMbOVZnZ+Le17mNkSMyswsx1m9j9mZlXaDPIf64iZbTGzSSf2LEREREQCq1FDnJn9BngGeAToBXwOfGhm7WtoHwV8DOwC+gK3ALcDUyq1SQEW+o/VC5gGPGdmo0/cmYiIiIgEVmP3xE0BZjvnZjnnvnHOTQZygRtqaD8BaAFc5Zxb65x7F3gUmFKpN24SkOOcm+w/5ixgDnDbiT0VERERkcAx51zjvJFZGHAYuNw593al9TOA7s65QdXs8xoQ45wbUWldX+ALoINzLsvMlgJfO+duqtTm18AbQAvnXHGVY14PXO9f7AxsaKhzlGq1BvYEugiRk4g+UyfeGc65NoEuQqQ2IY34Xq2BYHyXRivbBVxUwz5tge3VtC/fluX//o9q2oT43zO38gbn3MvAy/UpXP5zZpbhnEsPdB0iJwt9pkSkXCBGp1bt+rNq1tXWvur6urQREREROWk0ZojbA5Ti6zmrLJaje+fK7ayhPZX2qalNCZD/H1UqIiIi0sQ1WohzzhUBK4GhVTYNxTeytDrLgfPNrHmV9jlAdqU2VS/HDgUyqt4PJwGhS9ciDUufKREBGnFgA1RMMfI6cCPwGb6RpdcA3ZxzW81sGnCOc26Iv31LfAMPFgMPAWcCs4EHnHNP+NukAGuBWcBLwHnAC/gGULzbaCcnIiIi0ogac2ADzrk3zSwGuBeIxxe+hjvntvqbxAOpldrvN7OhwAwgA9gHPAE8WalNlpkNB57CN1VJDnCLApyIiIiczBq1J05EREREGoaenSoiIiLiQQpxIiIiIh6kECciIiLiQY06sEFODWbWFegKtAR+BFY457ICW5WIiMjJRQMbpEGZ2VRgAtAJ2IFvwuUyYDW+59l+Bjin//BERESOi0KcNBj/9DHZwO3OuZlmlgScA/QH+gDNgbucc4sDVqSIh5hZKJACbHXOFQa6HhFpWnRPnDSkXwPfOudmAjjntjnn3nXO3Qbciq9n7m9m1iGQRYp4yE34erFnmtkvzaytmQVXbmBmUWZ2iT/wicgpRCFOGlI+0NrMBgKYWXD5HxznXCbwW2A9cHHgShTxlN8AXwAdgb/ie8zgH81sgP+JNgDjgd/rMYMipx6FOGlIHwBbgf/PzHo450qdc6XlG51zR4ASICZQBYp4hZm1AYqBWc6584EzgD8BvwCWAp+a2Z34erlXBKxQEQkY3RMnDcLMzDnnzOw84DmgB/Ahvj86a4BWwLnAg0Av51x2oGoV8QIziwfGAeudc3+vsq0XcK1/ezSQ5Jzb0fhVikggKcRJgzKzKKAZvoEMVwAj/Ms78fXCPe+ceyZwFYp4h5mF4xvNfcTMrHx9+ehuM3sY3/OnewWqRhEJHM0TJ8fNzGLxBbYpwF7gCJCD7/Lq74HTgfbAZ865XYGqU8RrnHMF5eGt6rQ8ZtYCGA28GojaRCTw1BMnx83MZgPdgPfwhbhWQBrQBV+Yu9s592XAChTxGH+P9sFjzadoZs3xDXyY55wrarTiRKTJUIiT4+LvJTiI75LO0krr2gP98N230wH4tXNudcAKFfEQM3sJ36jUL/DNEXegmjanO+d+aPTiRKTJ0OhUOV5dgSygoifA+Wx1zr2FbyTdD8DYANUn4ilmdjlwHfAEsADflCK/MrNU/z1y5ffKzTGz7gEsVUQCTD1xclz8f0zeB1oAVwKbnXNlVdpMBq5xzvUMQIkinmJms4BS4DFgFHAVkApsABYCnwCdgWecc2GBqlNEAk89cXJcnHMFwD1AOPAacKWZJZnZaVBx8/UgYG3gqhTxBjMLwdez/YNzbotz7nHnXA+gL7AEX6B7C980Pq8HrlIRaQrUEycNwn9Z5z5gJPAjvpnl84CLgFzgWufc14GrUMQbzCwaiHPOfWtmYUBx5QEOZvYbYB7Q2zn3VaDqFJHAU4iTBuWfbmQEcBm+qUbWAm87574NaGEiHmZmQfj+f11qZtfhu5TaItB1iUhgKcTJCWNmQVXvjxOR42NmU4Bg59wfA12LiASWQpyIiIeYWShQqn8giYhCnIiIiIgHaXSqiIiIiAcpxImIiIh4kEKciIiIiAcpxImIiIh4kEKciIiIiAcpxImIiIh40P8Pdb3zIK07LxEAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "plot_histogram([expected_results, counts],\n", + " legend=['Expected', 'Extended Stabilizer'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can control this approximation error using the `extended_stabilizer_approximation_error` in Qiskit Aer. The default error is 0.05. The smaller the error, the more precise the results, but also the longer your simulation will take and the more memory it will require." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:24:01.638958Z", + "start_time": "2019-08-19T17:23:57.206364Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100 shots in 1.404871940612793s\n" + ] + }, + { + "data": { + "image/png": "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HulYs99Zs2Z1ra7tmWee6VC9/KKLLkq54YYbEmurJSkpacBrr70WAxATEzOw+ruMiooaZGZDsrKyIgAWL17cvk+fPn1jYmIGDho0qM/q1auj/Pu44447up500kl927VrN7ipQU4hrhVzzjXY5uKLLyY3N5fi4mKuv/56fvWrXzV6Wy+rbf/M7AfvBw8ezLZt28jMzGTGjBlccMEFjd5Wjk86puqmY+r4M2/evB1fffVV2zlz5sS/++670UuWLOm4aNGi7NjY2MpXXnllc6dOncqqR+9SUlLKHnjggc5vv/32CcuWLcvKy8vLPOGEEyqmTZvW3b/Pjz/+OHrz5s0bly5d+vUf//jHxHXr1kUC3HLLLYk5OTltNm/e/Pl777339ZIlS44EpYqKCsaPH99rwIABh/Ly8jZ88MEHWfPmzevy6quvxja0bU2nnXba99nZ2ZFXXnll8ptvvhmzf//+Iznm4osvPjBjxoxd48eP33fo0KH1WVlZXwB06dKl/M033/ymqKho/Z/+9Kfs//mf/0leuXLlkaBYWFgYvmfPnrDc3NwN8+fPz545c+aJmZmZbZryXRcVFX1W/V1eeeWVu4cMGXIwJSWlbOXKlW2vu+66lKeffnrbvn37PrviiisKLrzwwl7FxcVHDpBXX321w9KlSzfv3bt3fXh4eFM+ViGuNevTpw+FhYVH3mdnZx/5jbZat27d6NrV98vQ3XffTWVlJVlZWfVuGx4ezmeffQb4TgOFhXnv0shu3bqxffv2I+937NhBYuIPf1mKjY0lOjoagHHjxlFWVsaePXvq3bZLly5HRlfy8vLo3LkzcvzQMVU3HVPeNXny5F4xMTEDq3/+8Ic/dASIiYmpfO6557beddddyVdccUWP2bNnb09NTa1zqGfhwoWd7rvvvp2pqallUVFRLiMjI/edd96J8x8deuCBB3Kjo6PdsGHDik8++eTiNWvWRAG88cYbHW677ba8Ll26VPTq1avst7/97ZGLSpcvX95u7969YY8++mheZGSk69u3b+lll11WsHjx4g4NbVtT3759S5cuXZqVl5cXPmXKlJ6dO3ceeNFFF6X4h7maJk2atL9fv34lISEhjB8//uDw4cMP/POf/4z2b/PYY4/lRkVFufHjxx8888wz9y9atKhDXf3VZ8GCBXF///vfO7zxxhtb2rRp45555pmOl112WcFZZ531fVhYGDNmzCgMDw93H330UbvqbaZPn57fq1evsujo6Cb/pqgQ14pNmTKF/fv3s2LFCg4ePMiKFSu49tprf9Bmw4YNVFZWAvDcc88B0Lt373q3TUtLY9asWQDMmjXryGkgLxk6dCibN28mOzub0tJSlixZctTF6bt27ToyQvDpp59SWVlJfHx8vdtOmDCB559/HoDnn3+e888/P7g7Js1Kx1TddEx514svvvhNUVHRZ9U///3f/72net2ZZ555KDk5ucQ5x5VXXrm3vn7y8vIiLr300iOBsH///v1CQ0PZsWPHkeGh7t27H0l0UVFRlQcPHgwFKCgoCE9JSSmtXtejR48jr7du3RpRUFAQ4R8058yZk1BQUBDW0La1Ofvss79funTp1n379mW+//77X61evTrm9ttvT6ir/csvvxyblpbWp3379gNjYmIGLl++vP2ePXuO/KYVExNTHhsbW1n9Pjk5uTQ3N7dpQ2LAxx9/HHXLLbd0f/nll79JTEwsB9ixY0fE/Pnzu/jve35+fviOHTsiqrc78cQTm34xXBXv/br4IxIZGcmdd97JOeecg3OO0aNHc/755zN58mQAXnzxRX7/+9/z5ptvYmaEhoYyd+5cQkJC6ty2ervTTjuNiIgIoqOj+fe//92Su/kfCQsL46mnnmLs2LFUVFRwxRVX0K9fP+bNmwfA9OnTeeWVV3jmmWcICwsjKiqKJUuWYGZ1bgu+f4AnTpzIn//8Z7p3787f/va3ltxNCTAdU3XTMXV8ysjI6FRaWmqdO3cuu+uuu7pmZGTsAjCzo0Z9unTpUjZ//vzsn/3sZ9/XXFd9fVddOnbsWJaTkxORnp5+GCAnJ+dI+5SUlNKkpKSSbdu2bWzqtg0ZNWrUoXHjxu378ssvo2rbr+LiYrv88stTn3766ZzJkyd/16ZNG3fOOeek+l8CUFRUFHbgwIGQ6iC3Y8eOiL59+xY3tgaA3NzcsIkTJ/Z6+OGHvx0+fPiRbZOSkspuuOGGvIceemhXXdvW9nfRWApxrdzdd9/N3Xff/YNlL7744pHX9f0PsbZtwTeqsHdvvb+QecK4ceMYN27cD5ZNnz79yOvrr7+e66+/vtHbAsTHx/Phhx8GtlBpVXRM1U3H1PFlw4YNbTIyMpLee++9rOjo6Mrhw4f/5Be/+MX+008/vTgxMbF8//79YYWFhaHx8fEVAP/1X/+1+8477+yWkpKSfdJJJ5Xm5uaGffTRR9G/+c1vvmvosyZMmLD3oYceShg1atT3Bw4cCPnTn/505Lz56NGjv4+Ojq644447ut522235kZGRbv369ZGHDh0KGTVq1KH6tq3pvffei964cWPkpEmTvktKSipfv3595Pvvv3/CJZdcsgd8178tW7YsoqKigtDQUA4fPmylpaUhnTt3LgsPD3cvv/xy7McffxxbM6TddNNNiXPmzNm5bNmydh999FH7+++/P7ex33NZWRkTJkxIvfDCC/deddVVP5gIcfr06QUTJ07sNXbs2KLRo0d/f/DgwZClS5fGjB07tiguLq6yrj4bSyFORESkiRo7j1swTJo0qbf/PHHDhw8/sHTp0i2XXnppz+uvv37XsGHDigHuuOOOnVOnTu2RmZn55aBBgw7/4he/2JuamjqgoqKCzz//fNOdd9652zlnP/vZz04qKCgI79ChQ9n555+/rzEh7uGHH86bMmVK9549ew7o3Llz2eTJk/fMnz+/C/hGed9+++1vZsyY0S0lJeWU0tJS69Gjx+F77713Z0Pb1tShQ4fyt99++4QHH3wwqbi4OCQuLq58woQJe++77758gClTpux96aWXOsTFxQ3s1q1byRdffPHl/fff/+2UKVNSS0tL7eyzz95/9tln7/fvMz4+viwuLq48ISHhlMjIyMpHH31026BBgw439vvfunVrxNq1a6M3bdrU9s9//vORAJqZmblp5MiRh5566qmcG264ofu2bdvaREZGVqanpx8cO3ZsUWP7r48d73dc1Sc9Pd2tWbOmpcsQEZFWxMzWOufSq99nZmbmpKWl7alvG5HmkpmZ2TEtLS2ltnW6sUFERETEgxTiRERERDxIIU5ERETEgxTiRERERDxIIU5ERKR+lZWVlXqOmARd1X93dU5FohAnIiJSv40FBQXtFeQkmCorK62goKA9UOskyaB54kREROpVXl4+bdeuXc/u2rWrPxr8kOCpBDaWl5dPq6uBQpyIiEg9hgwZshuY0GBDkSDTbxQiIiIiHqQQJyIiIuJBOp0aAFc93tIVtF6znrmnpUtotVKz7mnpElotHVN10zFVNx1T8mOjkTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERDwp6iDOza80s28wOm9laMzujkdv1NrMiMztYY/loM3O1/PRpnj0QERERaXlBDXFmdgnwBPAgMAj4BHjHzLo3sF0EsARYUU+zfkCC38/mQNQsIiIi0hoFeyRuJrDQObfAOfelc24GkAdc08B2DwEbgL/V02a3c26X309FgGoWERERaXWCFuKqRtOGAO/XWPU+cHo9240Hfg7c0MBHrDGzPDP70MzOPKZiRURERFq5sCB+VkcgFMivsTwfOKe2DcwsAVgAXOicKzKz2ppVj+T9G4gALgM+NLPRzrmjTr+a2dXA1QCJiYksW7YMgJ49exITE0NmZiYA8fHx9OvXjxUrfF2EhYUxYsQI1q1bx4EDBwBIT08nPz8fSG3sdyByxO7du6moqCArKwuApKQkunXrxurVqwGIjo4mPT2dVatWUVJSAsCIESP4+uuv2b17NwD9+/enpKSEzZt9Vw8kJyfTpUsX1qxZA0BsbCyDBw9m5cqVlJeXAzBy5Eg2bdpEYWEhAGlpaRQVFbF161YAUlJS6NChA+vWrQMgLi6OtLQ0li9fjnMOM2PUqFFkZmayb98+AAYPHszevXvJyckBju142r59OzA68F+4HPfy8vICcjyJeIU554LzQWaJwE5gpHPuX37L7wZ+7Zw76kYEM/sQWOac+33V+6nAU8656AY+aylQ7pybUF+79PR0V/2P3bG46vFj7uK4NeuZe1q6hFYrNeueli6h1dIxVTcdU3UL1DFlZmudc+kB6UykGQXzmrg9QAXQtcbyzhw9OlftLOBuMys3s3Lgz0C7qvdX1/NZq4Hex1qwiIiISGsVtNOpzrlSM1sLjOGHNyiMAV6tY7MBNd6fD9wBnIpvVK8uA/GdZhURERE5LgXzmjiAx4C/mtmnwMfAdCARmAdgZhnAqc65swGccxv9NzazdKDSf7mZ3QjkAJvwXRP3G+AC4KLm3hkRERGRlhLUEOece8nM4oE78c3lthEY55zbVtUkgabfJRABPAokAcX4wtx459zSwFQtIiIi0voEeyQO59zTwNN1rJvawLYLgYU1lj0MPByY6kRERES8Qc9OFREREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD1KIExEREfEghTgRERERD2pSiDOzEDML8Xvf1cymmdnwwJcmIiIiInVp6kjc28AMADOLBtYAjwDLzGxKgGsTERERkTo0NcQNAT6qen0hcADoDFwF3BTAukRERESkHk0NcTHAd1Wvfwb83TlXhi/YpQayMBERERGpW1ND3LfAcDNrB4wFPqha3gE4FMjCRERERKRuYU1s/xjwV+AgsA1YUbV8JPB5AOsSERERkXo0KcQ55/5kZmuBZOAD51xl1aotwF2BLk5EREREatfUkTicc2vw3ZXqv+ztgFUkIiIiIg1q8mS/ZnatmW0ys0Nm1rNq2a1mNjHw5YmIiIhIbZo62e+NwJ3AfMD8VuUC1wewLhERERGpR1NH4qYDVznnngDK/ZavA/oFrCoRERERqVdTQ9yJwMZalpcBUcdejoiIiIg0RlND3FZgcC3LxwFfHHs5IiIiItIYTb079VHgKTNri++auGFmdhlwC3BFoIsTERERkdo1dZ6458wsDHgQaItv4t+dwA3OuZeaoT4RERERqcV/Mk/cAmCBmXUEQpxzuwNfloiIiIjUp8khrppzbk8gCxERERGRxmswxJnZBmCUc26fmX0OuLraOudOCWRxIiIiIlK7xozEvQqU+L2uM8SJiIiISHA0GOKcc/f6vb6nWasRERERkUZp6mO3PjKzE2pZHmtmHwWuLBERERGpT1Mn+x0NRNSyPBI445irEREREZFGadTdqWbm/5SGU8xsr9/7UGAsvvniRERERCQIGjvFyBp8NzQ44P1a1hcDMwJVlIiIiIjUr7Ehrge+x2xtBU4FCvzWlQK7nXMVAa5NREREROrQqBDnnNtW9bKp19CJiIiISDNozGS/FwJvOufKql7XyTn3/wWsMhERERGpU2NG4l4BugK7q17XxeG7yUFEREREmlljJvsNqe21iIiIiLQchTIRERERD2rsNXGNomviRERERIKjsdfENYauiRMREREJkiZdEyciIiIirYMCmoiIiIgHaZ44EREREQ8K+jxxZnYtcDOQAGwC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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Add runtime options for extended stabilizer simulator\n", + "opts = {'extended_stabilizer_approximation_error': 0.03}\n", + "\n", + "reduced_error = extended_stabilizer_simulator.run(\n", + " tsmall_circ, shots=100, **opts).result()\n", + "\n", + "reduced_error_counts = reduced_error.get_counts(0)\n", + "print('100 shots in {}s'.format(reduced_error.time_taken))\n", + "plot_histogram([expected_results, reduced_error_counts],\n", + " legend=['Expected', 'Extended Stabilizer'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simulator Options" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "There are several other options you can tweak to control how the extended stabilizer method performs. What these options are and their explanation can all be found in the Qiskit Aer documentation. However, I want to highlight two important ones that can help to optimize your simulations.\n", + "\n", + "To perform measurements, the extended stabilizer method uses a Markov chain method to sample outcomes at random. This Markov chain has to be run for some time we call the 'mixing time' before it will start sampling, and has to be re-mixed for every circuit shot.\n", + "\n", + "If you expect your circuit output to be concentrated on just a few output states, then you can likely optimize your simulations by reducing the `extended_stabilizer_mixing_time` option.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:24:03.270544Z", + "start_time": "2019-08-19T17:24:03.132881Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The circuit above, with 100 shots at precision 0.03 and default mixing time, needed 1s\n", + "Dialing down the mixing time, we completed in just 1.4710919857025146s\n" + ] + } + ], + "source": [ + "print(\"The circuit above, with 100 shots at precision 0.03 \"\n", + " \"and default mixing time, needed {}s\".format(int(reduced_error.time_taken)))\n", + "\n", + "opts = {\n", + " 'extended_stabilizer_approximation_error': 0.03,\n", + " 'extended_stabilizer_mixing_time': 100\n", + "}\n", + "\n", + "optimized = extended_stabilizer_simulator.run(\n", + " tsmall_circ, shots=100, **opts).result()\n", + "\n", + "print('Dialing down the mixing time, we completed in just {}s'.format(optimized.time_taken))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Similarly, if your circuit has some non-zero probability on all amplitudes (e.g. if it's a random circuit), then you can avoid this expensive re-mixing step to take multiple shots from the output at once. This can be enabled by setting `extended_stabilizer_measure_sampling=True`.\n", + "\n", + "For example, let's look again at the random circuit from the start of the tutorial, running for 100 shots:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:24:20.656799Z", + "start_time": "2019-08-19T17:24:04.332417Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "100 shots took 29.634929895401 s\n" + ] + } + ], + "source": [ + "# We set these options here only to make the example run more quickly.\n", + "opts = {'extended_stabilizer_mixing_time': 100}\n", + "\n", + "multishot = extended_stabilizer_simulator.run(\n", + " tcirc, shots=100, **opts).result()\n", + "print(\"100 shots took {} s\".format(multishot.time_taken))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:24:21.070233Z", + "start_time": "2019-08-19T17:24:20.918300Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "With the optimization, 100 shots took 0.4958779811859131 s\n" + ] + } + ], + "source": [ + "opts = {\n", + " 'extended_stabilizer_measure_sampling': True,\n", + " 'extended_stabilizer_mixing_time': 100\n", + "}\n", + "\n", + "measure_sampling = extended_stabilizer_simulator.run(\n", + " circ, shots=100, **opts).result()\n", + "print(\"With the optimization, 100 shots took {} s\".format(result.time_taken))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## When shall I use it?\n", + "\n", + "If you have smaller circuits with lots of non-Clifford gates, then the statevector method will likely perform better than the extended stabilizer. If however you want to look at circuits on many qubits, without needing access to high performance computation, then give this method a try!" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:24:45.930560Z", + "start_time": "2019-08-19T17:24:45.920568Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
Qiskit0.25.0
Terra0.17.0
Aer0.8.0
Ignis0.6.0
Aqua0.9.0
IBM Q Provider0.12.2
System information
Python3.7.7 (default, May 6 2020, 04:59:01) \n", + "[Clang 4.0.1 (tags/RELEASE_401/final)]
OSDarwin
CPUs6
Memory (Gb)32.0
Fri Apr 02 12:28:14 2021 EDT
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.5" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/7_matrix_product_state_method.ipynb b/docs/tutorials/7_matrix_product_state_method.ipynb new file mode 100644 index 0000000000..1252efff34 --- /dev/null +++ b/docs/tutorials/7_matrix_product_state_method.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Matrix product state simulation method\n", + "\n", + "## Simulation methods\n", + "The `AerSimulator` has several simulation methods including `statevector`, `stabilizer`, `extended_stabilizer` and `matrix_product_state`. Each of these determines the internal representation of the quantum circuit and the algorithms used to process the quantum operations. They each have advantages and disadvantages, and choosing the best method is a matter of investigation.\n", + "In this tutorial, we focus on the `matrix product state simulation method`.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Matrix product state simulation method\n", + "This simulation method is based on the concept of `matrix product states`. This structure was initially proposed in the paper *Efficient classical simulation of slightly entangled quantum computations* by Vidal in https://arxiv.org/abs/quant-ph/0301063. There are additional papers that describe the structure in more detail, for example *The density-matrix renormalization group in the age of matrix product states* by Schollwoeck https://arxiv.org/abs/1008.3477." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A pure quantum state is usually described as a state vector, by the expression $|\\psi\\rangle = \\sum_{i_1=0}^1 {\\ldots} \\sum_{i_n=0}^1 c_{i_1 \\ldots i_n} |i_i\\rangle {\\otimes} {\\ldots} {\\otimes} |i_n\\rangle$.\n", + "\n", + "The state vector representation implies an exponential size representation, regardless of the actual circuit. Every quantum gate operating on this representation requires exponential time and memory.\n", + "\n", + "The matrix product state (MPS) representation offers a local representation, in the form:\n", + "$\\Gamma^{[1]} \\lambda^{[1]} \\Gamma^{[2]} \\lambda^{[2]}\\ldots \\Gamma^{[1]} \\lambda^{[n-1]} \\Gamma^{[n]}$, such that all the information contained in the $c_{i_1 \\ldots i_n}$, can be generated out of the MPS representation.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Every $\\Gamma^{[i]}$ is a tensor of complex numbers that represents qubit $i$. Every $\\lambda^{[i]}$ is a matrix of real numbers that is used to normalize the amplitudes of qubits $i$ and $i+1$. Single-qubit gates operate only on the relevant tensor. \n", + "\n", + "Two-qubit gates operate on consecutive qubits $i$ and $i+1$. This involves a tensor-contract operation over $\\lambda^{[i-1]}$, $\\Gamma^{[i-1]}$, $\\lambda^{[i]}$, $\\Gamma^{[i+1]}$ and $\\lambda^{[i+1]}$, that creates a single tensor. We apply the gate to this tensor, and then decompose back to the original structure. This operation may increase the size of the respective tensors. Gates that involve two qubits that are not consecutive, require a series of swap gates to bring the two qubits next to each other and then the reverse swaps. \n", + "\n", + "In the worst case, the tensors may grow exponentially. However, the size of the overall structure remains 'small' for circuits that do not have 'many' two-qubit gates. This allows much more efficient operations in circuits with relatively 'low' entanglement. Characterizing when to use this method over other methods is a subject of current research." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Using the matrix product state simulation method\n", + "The matrix product state simulation method is invoked in the `AerSimulator` by setting the simulation method. \n", + "Other than that, all operations are controlled by the `AerSimulator` itself, as in the following example:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:28:36.535599Z", + "start_time": "2019-08-19T17:28:36.463583Z" + }, + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'11': 515, '00': 509}" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import numpy as np\n", + "\n", + "# Import Qiskit\n", + "from qiskit import QuantumCircuit, transpile\n", + "from qiskit_aer import AerSimulator\n", + "\n", + "# Construct quantum circuit\n", + "circ = QuantumCircuit(2, 2)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "circ.measure([0,1], [0,1])\n", + "\n", + "# Select the AerSimulator from the Aer provider\n", + "simulator = AerSimulator(method='matrix_product_state')\n", + "\n", + "# Run and get counts, using the matrix_product_state method\n", + "tcirc = transpile(circ, simulator)\n", + "result = simulator.run(tcirc).result()\n", + "counts = result.get_counts(0)\n", + "counts" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To see the internal state vector of the circuit we can use the `save_statevector` instruction. To return the full internal MPS structure we can also use the `save_matrix_product_state` instruction." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:28:31.715708Z", + "start_time": "2019-08-19T17:28:31.646419Z" + } + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'counts': {'0x0': 494, '0x3': 530},\n", + " 'my_sv': array([0.70710678+0.j, 0. +0.j, 0. +0.j, 0.70710678+0.j]),\n", + " 'my_mps': ([(array([[1.-0.j, 0.-0.j]]), array([[0.-0.j, 1.-0.j]])),\n", + " (array([[1.-0.j],\n", + " [0.-0.j]]),\n", + " array([[0.-0.j],\n", + " [1.-0.j]]))],\n", + " [array([0.70710678, 0.70710678])])}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "circ = QuantumCircuit(2, 2)\n", + "circ.h(0)\n", + "circ.cx(0, 1)\n", + "\n", + "# Define a snapshot that shows the current state vector\n", + "circ.save_statevector(label='my_sv')\n", + "circ.save_matrix_product_state(label='my_mps')\n", + "circ.measure([0,1], [0,1])\n", + "\n", + "# Execute and get saved data\n", + "tcirc = transpile(circ, simulator)\n", + "result = simulator.run(tcirc).result()\n", + "data = result.data(0)\n", + "\n", + "#print the result data\n", + "data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Running circuits using the matrix product state simulation method can be fast, relative to other methods. However, if we generate the state vector during the execution, then the conversion to state vector is, of course, exponential in memory and time, and therefore we don't benefit from using this method. We can benefit if we only do operations that don't require printing the full state vector. For example, if we run a circuit and then take measurement. The circuit below has 200 qubits. We create an `EPR state` involving all these qubits. Although this state is highly entangled, it is handled well by the matrix product state method, because there are effectively only two states. \n", + "\n", + "We can handle more qubits than this, but execution may take a few minutes. Try running a similar circuit with 500 qubits! Or maybe even 1000 (you can get a cup of coffee while waiting)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:29:38.913752Z", + "start_time": "2019-08-19T17:29:04.306048Z" + }, + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Time taken: 0.31022214889526367 sec\n" + ] + }, + { + "data": { + "text/plain": [ + "{'11111111111111111111111111111111111111111111111111': 548,\n", + " '00000000000000000000000000000000000000000000000000': 476}" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num_qubits = 50\n", + "circ = QuantumCircuit(num_qubits, num_qubits)\n", + "\n", + "# Create EPR state\n", + "circ.h(0)\n", + "for i in range (0, num_qubits-1):\n", + " circ.cx(i, i+1)\n", + "\n", + "# Measure\n", + "circ.measure(range(num_qubits), range(num_qubits))\n", + "\n", + "tcirc = transpile(circ, simulator)\n", + "result = simulator.run(tcirc).result()\n", + "print(\"Time taken: {} sec\".format(result.time_taken))\n", + "result.get_counts()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2019-08-19T17:39:56.328435Z", + "start_time": "2019-08-19T17:39:56.320197Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "

Version Information

Qiskit SoftwareVersion
Qiskit0.25.0
Terra0.17.0
Aer0.8.0
Ignis0.6.0
Aqua0.9.0
IBM Q Provider0.12.2
System information
Python3.8.8 | packaged by conda-forge | (default, Feb 20 2021, 16:22:27) \n", + "[GCC 9.3.0]
OSLinux
CPUs8
Memory (Gb)31.38858413696289
Tue Apr 20 15:22:58 2021 UTC
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

This code is a part of Qiskit

© Copyright IBM 2017, 2021.

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.

" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import qiskit.tools.jupyter\n", + "%qiskit_version_table\n", + "%qiskit_copyright" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst new file mode 100644 index 0000000000..acf239143a --- /dev/null +++ b/docs/tutorials/index.rst @@ -0,0 +1,15 @@ +.. _tutorials: + +#################################### +High-Performance Simulator Tutorials +#################################### + +.. nbgallery:: + :glob: + + * + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/pyproject.toml b/pyproject.toml index 0e025a32f5..46e2c752c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,6 @@ [build-system] requires = [ "setuptools", - "wheel", "conan<2.0.0", "scikit-build>=0.11.0", "cmake!=3.17.1,!=3.17.0", @@ -33,3 +32,7 @@ environment = { CMAKE_GENERATOR = "Visual Studio 16 2019"} [[tool.cibuildwheel.overrides]] select = "cp3{7,8,9,10,11}-manylinux_i686" before-all = "yum install -y wget && bash {project}/tools/install_openblas_i686.sh && bash {project}/tools/install_rust.sh" + +[tool.black] +line-length = 100 +target-version = ['py37', 'py38', 'py39', 'py310', 'py311'] diff --git a/qiskit_aer/VERSION.txt b/qiskit_aer/VERSION.txt index ac454c6a1f..34a83616bb 100644 --- a/qiskit_aer/VERSION.txt +++ b/qiskit_aer/VERSION.txt @@ -1 +1 @@ -0.12.0 +0.12.1 diff --git a/qiskit_aer/__init__.py b/qiskit_aer/__init__.py index 730eed4e52..8a76e0305f 100644 --- a/qiskit_aer/__init__.py +++ b/qiskit_aer/__init__.py @@ -62,6 +62,7 @@ # before loading our simulators, so we force it using this ugly trick if platform.system() == "Darwin": import numpy as np + np.dot(np.zeros(100), np.zeros(100)) # ... ¯\_(ツ)_/¯ diff --git a/qiskit_aer/aererror.py b/qiskit_aer/aererror.py index 4e3311c0ff..d1ffa74eab 100644 --- a/qiskit_aer/aererror.py +++ b/qiskit_aer/aererror.py @@ -23,7 +23,7 @@ class AerError(QiskitError): def __init__(self, *message): """Set the error message.""" super().__init__(*message) - self.message = ' '.join(message) + self.message = " ".join(message) def __str__(self): """Return the message.""" diff --git a/qiskit_aer/aerprovider.py b/qiskit_aer/aerprovider.py index 82bf38ca62..5ad1956495 100644 --- a/qiskit_aer/aerprovider.py +++ b/qiskit_aer/aerprovider.py @@ -30,36 +30,39 @@ class AerProvider(Provider): _BACKENDS = None - def __init__(self): + @staticmethod + def _get_backends(): if AerProvider._BACKENDS is None: # Populate the list of Aer simulator backends. methods = AerSimulator().available_methods() devices = AerSimulator().available_devices() backends = [] for method in methods: - name = 'aer_simulator' - if method not in [None, 'automatic']: - name += f'_{method}' - device_name = 'CPU' + name = "aer_simulator" + if method not in [None, "automatic"]: + name += f"_{method}" + device_name = "CPU" backends.append((name, AerSimulator, method, device_name)) # Add GPU device backends - if method in ['statevector', 'density_matrix', 'unitary']: + if method in ["statevector", "density_matrix", "unitary"]: for device in devices: - if device != 'CPU': - new_name = f'{name}_{device}'.lower() + if device != "CPU": + new_name = f"{name}_{device}".lower() device_name = device backends.append((new_name, AerSimulator, method, device_name)) # Add legacy backend names backends += [ - ('qasm_simulator', QasmSimulator, None, None), - ('statevector_simulator', StatevectorSimulator, None, None), - ('unitary_simulator', UnitarySimulator, None, None), - ('pulse_simulator', PulseSimulator, None, None) + ("qasm_simulator", QasmSimulator, None, None), + ("statevector_simulator", StatevectorSimulator, None, None), + ("unitary_simulator", UnitarySimulator, None, None), + ("pulse_simulator", PulseSimulator, None, None), ] AerProvider._BACKENDS = backends + return AerProvider._BACKENDS + def get_backend(self, name=None, **kwargs): if name == "pulse_simulator": warnings.warn( @@ -76,15 +79,18 @@ def backends(self, name=None, filters=None, **kwargs): # Instantiate a new backend instance so if config options # are set they will only last as long as that backend object exists backends = [] - for backend_name, backend_cls, method, device in self._BACKENDS: - opts = {'provider': self} + + # pylint: disable=not-an-iterable + # pylint infers _get_backends to always return None + for backend_name, backend_cls, method, device in self._get_backends(): + opts = {"provider": self} if method is not None: - opts['method'] = method + opts["method"] = method if device is not None: - opts['device'] = device + opts["device"] = device if name is None or backend_name == name: backends.append(backend_cls(**opts)) return filter_backends(backends, filters=filters) def __str__(self): - return 'AerProvider' + return "AerProvider" diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index 9f5e714b16..eafd0e7a1c 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -16,20 +16,20 @@ import itertools from copy import copy from typing import List +from warnings import warn +from concurrent.futures import Executor +import numpy as np from qiskit.circuit import QuantumCircuit, Clbit, ParameterExpression from qiskit.extensions import Initialize from qiskit.providers.options import Options from qiskit.pulse import Schedule, ScheduleBlock -from qiskit.circuit.controlflow import ( - WhileLoopOp, - ForLoopOp, - IfElseOp, - BreakLoopOp, - ContinueLoopOp) +from qiskit.circuit.controlflow import WhileLoopOp, ForLoopOp, IfElseOp, BreakLoopOp, ContinueLoopOp from qiskit.compiler import transpile from qiskit.qobj import QobjExperimentHeader from qiskit_aer.aererror import AerError +from qiskit_aer.noise import NoiseModel + # pylint: disable=import-error, no-name-in-module from qiskit_aer.backends.controller_wrappers import AerCircuit, AerConfig from .backend_utils import circuit_optypes @@ -37,7 +37,7 @@ class AerCompiler: - """ Aer Compiler to convert instructions of control-flow to mark and jump instructions""" + """Aer Compiler to convert instructions of control-flow to mark and jump instructions""" def __init__(self): self._last_flow_id = -1 @@ -67,15 +67,14 @@ def compile(self, circuits, basis_gates=None, optypes=None): # Make a shallow copy incase we modify it compiled_optypes = list(optypes) if isinstance(circuits, list): - basis_gates = basis_gates + ['mark', 'jump'] + basis_gates = basis_gates + ["mark", "jump"] compiled_circuits = [] for idx, circuit in enumerate(circuits): # Resolve initialize circuit = self._inline_initialize(circuit, compiled_optypes[idx]) if self._is_dynamic(circuit, compiled_optypes[idx]): compiled_circ = transpile( - self._inline_circuit(circuit, None, None), - basis_gates=basis_gates + self._inline_circuit(circuit, None, None), basis_gates=basis_gates ) compiled_circuits.append(compiled_circ) # Recompute optype for compiled circuit @@ -96,7 +95,9 @@ def _inline_initialize(self, circ, optype): return circ for inst, _, _ in circ.data: - if isinstance(inst, Initialize) and not isinstance(inst.params[0], complex): + if isinstance(inst, Initialize) and ( + (not isinstance(inst.params[0], complex)) or (len(inst.params) == 1) + ): break else: return circ @@ -104,7 +105,9 @@ def _inline_initialize(self, circ, optype): new_circ = circ.copy() new_circ.data = [] for inst, qargs, cargs in circ.data: - if isinstance(inst, Initialize) and not isinstance(inst.params[0], complex): + if isinstance(inst, Initialize) and ( + (not isinstance(inst.params[0], complex)) or (len(inst.params) == 1) + ): # Assume that the decomposed circuit of inst.definition consists of basis gates new_circ.compose(inst.definition.decompose(), qargs, cargs, inplace=True) else: @@ -118,9 +121,7 @@ def _is_dynamic(circuit, optype=None): if not isinstance(circuit, QuantumCircuit): return False - controlflow_types = ( - WhileLoopOp, ForLoopOp, IfElseOp, BreakLoopOp, ContinueLoopOp - ) + controlflow_types = (WhileLoopOp, ForLoopOp, IfElseOp, BreakLoopOp, ContinueLoopOp) # Check via optypes if isinstance(optype, set): @@ -200,12 +201,12 @@ def _inline_for_loop_op(self, instruction, parent, bit_map): self._last_flow_id += 1 loop_id = self._last_flow_id - loop_name = f'loop_{loop_id}' + loop_name = f"loop_{loop_id}" inlined_body = None - break_label = f'{loop_name}_end' + break_label = f"{loop_name}_end" for index in indexset: - continue_label = f'{loop_name}_{index}' + continue_label = f"{loop_name}_{index}" inlined_body = self._inline_circuit(body, continue_label, break_label, inner_bit_map) if loop_parameter is not None: inlined_body = inlined_body.bind_parameters({loop_parameter: index}) @@ -218,15 +219,15 @@ def _inline_for_loop_op(self, instruction, parent, bit_map): def _inline_while_loop_op(self, instruction, parent, bit_map): """inline while_loop body with jump and mark instructions""" condition_tuple = self._convert_c_if_args(instruction.operation.condition, bit_map) - body, = instruction.operation.params + (body,) = instruction.operation.params self._last_flow_id += 1 loop_id = self._last_flow_id - loop_name = f'while_{loop_id}' + loop_name = f"while_{loop_id}" - continue_label = f'{loop_name}_continue' - loop_start_label = f'{loop_name}_start' - break_label = f'{loop_name}_end' + continue_label = f"{loop_name}_continue" + loop_start_label = f"{loop_name}_start" + break_label = f"{loop_name}_end" inlined_body = self._inline_circuit( body, continue_label, @@ -243,18 +244,24 @@ def _inline_while_loop_op(self, instruction, parent, bit_map): cargs = [bit_map[c] for c in instruction.clbits] mark_cargs = cargs.copy() mark_cargs.extend( - bit_map[c] for c in ( + bit_map[c] + for c in ( ( - {condition_tuple[0]} if isinstance(condition_tuple[0], Clbit) + {condition_tuple[0]} + if isinstance(condition_tuple[0], Clbit) else set(condition_tuple[0]) - ) - set(instruction.clbits) + ) + - set(instruction.clbits) ) ) c_if_args = self._convert_c_if_args(condition_tuple, bit_map) parent.append(AerMark(continue_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) - parent.append(AerJump(loop_start_label, len(qargs), len(mark_cargs)).c_if(*c_if_args), - qargs, mark_cargs) + parent.append( + AerJump(loop_start_label, len(qargs), len(mark_cargs)).c_if(*c_if_args), + qargs, + mark_cargs, + ) parent.append(AerJump(break_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(loop_start_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(inlined_body, qargs, cargs) @@ -268,12 +275,12 @@ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, b self._last_flow_id += 1 if_id = self._last_flow_id - if_name = f'if_{if_id}' + if_name = f"if_{if_id}" - if_true_label = f'{if_name}_true' - if_end_label = f'{if_name}_end' + if_true_label = f"{if_name}_true" + if_end_label = f"{if_name}_end" if false_body: - if_else_label = f'{if_name}_else' + if_else_label = f"{if_name}_else" else: if_else_label = if_end_label @@ -283,11 +290,14 @@ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, b cargs = [bit_map[c] for c in instruction.clbits] mark_cargs = cargs.copy() mark_cargs.extend( - bit_map[c] for c in ( + bit_map[c] + for c in ( ( - {condition_tuple[0]} if isinstance(condition_tuple[0], Clbit) + {condition_tuple[0]} + if isinstance(condition_tuple[0], Clbit) else set(condition_tuple[0]) - ) - set(instruction.clbits) + ) + - set(instruction.clbits) ) ) @@ -334,10 +344,95 @@ def compile_circuit(circuits, basis_gates=None, optypes=None): return AerCompiler().compile(circuits, basis_gates, optypes) +BACKEND_RUN_ARG_TYPES = { + "shots": (int, np.integer), + "method": (str), + "device": (str), + "precision": (str), + "max_job_size": (int, np.integer), + "max_shot_size": (int, np.integer), + "enable_truncation": (bool, np.bool_), + "executor": Executor, + "zero_threshold": (float, np.floating), + "validation_threshold": (int, np.integer), + "max_parallel_threads": (int, np.integer), + "max_parallel_experiments": (int, np.integer), + "max_parallel_shots": (int, np.integer), + "max_memory_mb": (int, np.integer), + "fusion_enable": (bool, np.bool_), + "fusion_verbose": (bool, np.bool_), + "fusion_max_qubit": (int, np.integer), + "fusion_threshold": (int, np.integer), + "accept_distributed_results": (bool, np.bool_), + "memory": (bool, np.bool_), + "noise_model": (NoiseModel), + "seed_simulator": (int, np.integer), + "cuStateVec_enable": (int, np.integer), + "blocking_qubits": (int, np.integer), + "blocking_enable": (bool, np.bool_), + "chunk_swap_buffer_qubits": (int, np.integer), + "batched_shots_gpu": (bool, np.bool_), + "batched_shots_gpu_max_qubits": (int, np.integer), + "num_threads_per_device": (int, np.integer), + "statevector_parallel_threshold": (int, np.integer), + "statevector_sample_measure_opt": (int, np.integer), + "stabilizer_max_snapshot_probabilities": (int, np.integer), + "extended_stabilizer_sampling_method": (str), + "extended_stabilizer_metropolis_mixing_time": (int, np.integer), + "extended_stabilizer_approximation_error": (float, np.floating), + "extended_stabilizer_norm_estimation_samples": (int, np.integer), + "extended_stabilizer_norm_estimation_repetitions": (int, np.integer), + "extended_stabilizer_parallel_threshold": (int, np.integer), + "extended_stabilizer_probabilities_snapshot_samples": (int, np.integer), + "matrix_product_state_truncation_threshold": (float, np.floating), + "matrix_product_state_max_bond_dimension": (int, np.integer), + "mps_sample_measure_algorithm": (str), + "mps_log_data": (bool, np.bool_), + "mps_swap_direction": (str), + "chop_threshold": (float, np.floating), + "mps_parallel_threshold": (int, np.integer), + "mps_omp_threads": (int, np.integer), + "tensor_network_num_sampling_qubits": (int, np.integer), + "use_cuTensorNet_autotuning": (bool, np.bool_), + "parameterizations": (list), + "fusion_parallelization_threshold": (int, np.integer), +} + + +def _validate_option(k, v): + """validate backend.run arguments""" + if v is None: + return v + if k not in BACKEND_RUN_ARG_TYPES: + raise AerError(f"invalid argument: name={k}") + if isinstance(v, BACKEND_RUN_ARG_TYPES[k]): + return v + + expected_type = BACKEND_RUN_ARG_TYPES[k][0] + + if expected_type in (int, float, bool, str): + try: + ret = expected_type(v) + if not isinstance(v, BACKEND_RUN_ARG_TYPES[k]): + warn( + f'A type of an option "{k}" should be {expected_type.__name__} ' + "but {v.__class__.__name__} was specified." + "Implicit cast for an argument has been deprecated as of qiskit-aer 0.12.1.", + DeprecationWarning, + stacklevel=5, + ) + return ret + except Exception: # pylint: disable=broad-except + pass + + raise TypeError( + f"invalid option type: name={k}, " + f"type={v.__class__.__name__}, expected={BACKEND_RUN_ARG_TYPES[k][0].__name__}" + ) + + def generate_aer_config( - circuits: List[QuantumCircuit], - backend_options: Options, - **run_options + circuits: List[QuantumCircuit], backend_options: Options, **run_options ) -> AerConfig: """generates a configuration to run simulation. @@ -357,9 +452,11 @@ def generate_aer_config( config.n_qubits = num_qubits for key, value in backend_options.__dict__.items(): if hasattr(config, key) and value is not None: + value = _validate_option(key, value) setattr(config, key, value) for key, value in run_options.items(): if hasattr(config, key) and value is not None: + value = _validate_option(key, value) setattr(config, key, value) return config @@ -373,16 +470,20 @@ def assemble_circuit(circuit: QuantumCircuit): qreg_sizes = [] creg_sizes = [] - global_phase = float(circuit.global_phase) + if ( + isinstance(circuit.global_phase, ParameterExpression) + and len(circuit.global_phase.parameters) > 0 + ): + global_phase = 0.0 + else: + global_phase = float(circuit.global_phase) for qreg in circuit.qregs: qreg_sizes.append([qreg.name, qreg.size]) for creg in circuit.cregs: creg_sizes.append([creg.name, creg.size]) - is_conditional = any( - getattr(inst.operation, "condition", None) for inst in circuit.data - ) + is_conditional = any(getattr(inst.operation, "condition", None) for inst in circuit.data) header = QobjExperimentHeader( n_qubits=num_qubits, @@ -393,9 +494,6 @@ def assemble_circuit(circuit: QuantumCircuit): global_phase=global_phase, ) - if circuit.metadata is not None: - header.metadata = circuit.metadata - qubit_indices = {qubit: idx for idx, qubit in enumerate(circuit.qubits)} clbit_indices = {clbit: idx for idx, clbit in enumerate(circuit.clbits)} @@ -426,8 +524,7 @@ def assemble_circuit(circuit: QuantumCircuit): aer_circ.bfunc(f"0x{mask:X}", f"0x{val:X}", "==", conditional_reg) max_conditional_idx += 1 - _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, - is_conditional, conditional_reg) + _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, conditional_reg) return aer_circ @@ -442,49 +539,59 @@ def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, c copied = False for i, param in enumerate(params): - if (isinstance(param, ParameterExpression) and len(param.parameters) > 0): + if isinstance(param, ParameterExpression) and len(param.parameters) > 0: if not copied: params = copy(params) copied = True params[i] = 0.0 - if name in {'ccx', 'ccz', 'cp', 'cswap', 'csx', 'cx', 'cy', 'cz', 'delay', 'ecr', - 'h', 'id', 'mcp', 'mcphase', 'mcr', 'mcrx', 'mcry', 'mcrz', 'mcswap', - 'mcsx', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcx', 'mcx_gray', 'mcy', 'mcz', - 'p', 'r', 'rx', 'rxx', 'ry', 'ryy', 'rz', 'rzx', 'rzz', 's', 'sdg', 'swap', - 'sx', 'sxdg', 't', 'tdg', 'u', 'x', 'y', 'z', 'u1', 'u2', 'u3', - 'cu', 'cu1', 'cu2', 'cu3'}: + # fmt: off + if name in { + "ccx", "ccz", "cp", "cswap", "csx", "cx", "cy", "cz", "delay", "ecr", "h", + "id", "mcp", "mcphase", "mcr", "mcrx", "mcry", "mcrz", "mcswap", "mcsx", + "mcu", "mcu1", "mcu2", "mcu3", "mcx", "mcx_gray", "mcy", "mcz", "p", "r", + "rx", "rxx", "ry", "ryy", "rz", "rzx", "rzz", "s", "sdg", "swap", "sx", "sxdg", + "t", "tdg", "u", "x", "y", "z", "u1", "u2", "u3", "cu", "cu1", "cu2", "cu3", + }: aer_circ.gate(name, qubits, params, [], conditional_reg, label if label else name) - elif name == 'measure': + elif name == "measure": if is_conditional: aer_circ.measure(qubits, clbits, clbits) else: aer_circ.measure(qubits, clbits, []) - elif name == 'reset': + elif name == "reset": aer_circ.reset(qubits) - elif name == 'diagonal': - aer_circ.diagonal(qubits, params, label if label else 'diagonal') - elif name == 'unitary': - aer_circ.unitary(qubits, params[0], conditional_reg, label if label else 'unitary') - elif name == 'pauli': + elif name == "diagonal": + aer_circ.diagonal(qubits, params, label if label else "diagonal") + elif name == "unitary": + aer_circ.unitary(qubits, params[0], conditional_reg, label if label else "unitary") + elif name == "pauli": aer_circ.gate(name, qubits, [], params, conditional_reg, label if label else name) - elif name == 'initialize': + elif name == "initialize": aer_circ.initialize(qubits, params) - elif name == 'roerror': + elif name == "roerror": aer_circ.roerror(qubits, params) - elif name == 'multiplexer': + elif name == "multiplexer": aer_circ.multiplexer(qubits, params, conditional_reg, label if label else name) - elif name == 'kraus': + elif name == "kraus": aer_circ.kraus(qubits, params, conditional_reg) - elif name in {'save_statevector', 'save_statevector_dict', 'save_clifford', - 'save_probabilities', 'save_probabilities_dict', 'save_matrix_product_state', - 'save_unitary', 'save_superop', 'save_density_matrix', 'save_state', - 'save_stabilizer'}: + elif name in { + "save_statevector", + "save_statevector_dict", + "save_clifford", + "save_probabilities", + "save_probabilities_dict", + "save_matrix_product_state", + "save_unitary", + "save_superop", + "save_density_matrix", + "save_state", + "save_stabilizer", + }: aer_circ.save_state(qubits, name, operation._subtype, label if label else name) - elif name in {'save_amplitudes', 'save_amplitudes_sq'}: - aer_circ.save_amplitudes(qubits, name, params, operation._subtype, - label if label else name) - elif name in ('save_expval', 'save_expval_var'): + elif name in {"save_amplitudes", "save_amplitudes_sq"}: + aer_circ.save_amplitudes(qubits, name, params, operation._subtype, label if label else name) + elif name in ("save_expval", "save_expval_var"): paulis = [] coeff_reals = [] coeff_imags = [] @@ -492,41 +599,47 @@ def _assemble_op(aer_circ, inst, qubit_indices, clbit_indices, is_conditional, c paulis.append(pauli) coeff_reals.append(coeff[0]) coeff_imags.append(coeff[1]) - aer_circ.save_expval(qubits, name, paulis, coeff_reals, coeff_imags, operation._subtype, - label if label else name) - elif name == 'set_statevector': + aer_circ.save_expval( + qubits, + name, + paulis, + coeff_reals, + coeff_imags, + operation._subtype, + label if label else name, + ) + elif name == "set_statevector": aer_circ.set_statevector(qubits, params) - elif name == 'set_unitary': + elif name == "set_unitary": aer_circ.set_unitary(qubits, params) - elif name == 'set_density_matrix': + elif name == "set_density_matrix": aer_circ.set_density_matrix(qubits, params) - elif name == 'set_stabilizer': + elif name == "set_stabilizer": aer_circ.set_clifford(qubits, params) - elif name == 'set_superop': + elif name == "set_superop": aer_circ.set_superop(qubits, params) - elif name == 'set_matrix_product_state': + elif name == "set_matrix_product_state": aer_circ.set_matrix_product_state(qubits, params) - elif name == 'superop': + elif name == "superop": aer_circ.superop(qubits, params[0], conditional_reg) - elif name == 'barrier': - pass - elif name == 'jump': + elif name == "barrier": + aer_circ.barrier(qubits) + elif name == "jump": aer_circ.jump(qubits, params, conditional_reg) - elif name == 'mark': + elif name == "mark": aer_circ.mark(qubits, params) - elif name == 'qerror_loc': + elif name == "qerror_loc": aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg) - elif name in ('for_loop', 'while_loop', 'if_else'): - raise AerError('control-flow instructions must be converted ' - f'to jump and mark instructions: {name}') + elif name in ("for_loop", "while_loop", "if_else"): + raise AerError( + "control-flow instructions must be converted " f"to jump and mark instructions: {name}" + ) else: - raise AerError(f'unknown instruction: {name}') + raise AerError(f"unknown instruction: {name}") -def assemble_circuits( - circuits: List[QuantumCircuit] -) -> List[AerCircuit]: +def assemble_circuits(circuits: List[QuantumCircuit]) -> List[AerCircuit]: """converts a list of Qiskit circuits into circuits mapped AER::Circuit Args: diff --git a/qiskit_aer/backends/aer_simulator.py b/qiskit_aer/backends/aer_simulator.py index 6a4911dbd7..e438ad50ca 100644 --- a/qiskit_aer/backends/aer_simulator.py +++ b/qiskit_aer/backends/aer_simulator.py @@ -22,10 +22,15 @@ from ..version import __version__ from .aerbackend import AerBackend, AerError -from .backend_utils import (cpp_execute_circuits, cpp_execute_qobj, - available_methods, available_devices, - MAX_QUBITS_STATEVECTOR, - BASIS_GATES) +from .backend_utils import ( + cpp_execute_circuits, + cpp_execute_qobj, + available_methods, + available_devices, + MAX_QUBITS_STATEVECTOR, + BASIS_GATES, +) + # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -203,7 +208,6 @@ class AerSimulator(AerBackend): qubits which do not affect the simulation outcome from the simulated circuits (Default: True). - * ``zero_threshold`` (double): Sets the threshold for truncating small values to zero in the result data (Default: 1e-10). @@ -283,6 +287,9 @@ class AerSimulator(AerBackend): threads per GPU. This parameter is used to optimize Pauli noise simulation with multiple-GPUs (Default: 1). + * ``accept_distributed_results`` (bool): This option enables storing + results independently in each process (Default: None). + These backend options only apply when using the ``"statevector"`` simulation method: @@ -303,9 +310,7 @@ class AerSimulator(AerBackend): simulation method: * ``stabilizer_max_snapshot_probabilities`` (int): set the maximum - qubit number for the - `~qiskit_aer.extensions.SnapshotProbabilities` - instruction (Default: 32). + qubit number for the :class:`~qiskit_aer.library.SaveProbabilities` instruction (Default: 32). These backend options only apply when using the ``"extended_stabilizer"`` simulation method: @@ -402,6 +407,13 @@ class AerSimulator(AerBackend): Possible values are "mps_swap_right" and "mps_swap_left". (Default: "mps_swap_left") + * ``chop_threshold`` (float): This option sets a threshold for + truncating snapshots (Default: 1e-8). + + * ``mps_parallel_threshold`` (int): This option sets OMP number threshold (Default: 14). + + * ``mps_omp_threads`` (int): This option sets the number of OMP threads (Default: 1). + These backend options only apply when using the ``tensor_network`` simulation method: @@ -448,137 +460,226 @@ class AerSimulator(AerBackend): _BASIS_GATES = BASIS_GATES _CUSTOM_INSTR = { - 'statevector': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', - 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes', 'save_amplitudes_sq', - 'save_density_matrix', 'save_state', 'save_statevector', - 'save_statevector_dict', 'set_statevector', - 'if_else', 'for_loop', 'while_loop', 'break_loop', 'continue_loop', - ]), - 'density_matrix': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', 'superop', - 'save_state', 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_density_matrix', 'save_amplitudes_sq', 'set_density_matrix', - 'if_else', 'for_loop', 'while_loop', 'break_loop', 'continue_loop', - ]), - 'matrix_product_state': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', - 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_state', 'save_matrix_product_state', 'save_statevector', - 'save_density_matrix', 'save_amplitudes', 'save_amplitudes_sq', - 'set_matrix_product_state', - 'if_else', 'for_loop', 'while_loop', 'break_loop', 'continue_loop', - ]), - 'stabilizer': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', - 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes_sq', 'save_state', 'save_clifford', - 'save_stabilizer', 'set_stabilizer', - 'if_else', 'for_loop', 'while_loop', 'break_loop', 'continue_loop', - ]), - 'extended_stabilizer': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'save_statevector', - ]), - 'unitary': sorted([ - 'save_state', 'save_unitary', 'set_unitary', - ]), - 'superop': sorted([ - 'quantum_channel', 'qerror_loc', 'kraus', 'superop', 'save_state', - 'save_superop', 'set_superop', - ]), - 'tensor_network': sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', 'superop', - 'save_state', 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_density_matrix', 'save_amplitudes', 'save_amplitudes_sq', - 'save_statevector', 'save_statevector_dict', - 'set_statevector', 'set_density_matrix' - ]) + "statevector": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_amplitudes", + "save_amplitudes_sq", + "save_density_matrix", + "save_state", + "save_statevector", + "save_statevector_dict", + "set_statevector", + "if_else", + "for_loop", + "while_loop", + "break_loop", + "continue_loop", + ] + ), + "density_matrix": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "superop", + "save_state", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_density_matrix", + "save_amplitudes_sq", + "set_density_matrix", + "if_else", + "for_loop", + "while_loop", + "break_loop", + "continue_loop", + ] + ), + "matrix_product_state": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_state", + "save_matrix_product_state", + "save_statevector", + "save_density_matrix", + "save_amplitudes", + "save_amplitudes_sq", + "set_matrix_product_state", + "if_else", + "for_loop", + "while_loop", + "break_loop", + "continue_loop", + ] + ), + "stabilizer": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_amplitudes_sq", + "save_state", + "save_clifford", + "save_stabilizer", + "set_stabilizer", + "if_else", + "for_loop", + "while_loop", + "break_loop", + "continue_loop", + ] + ), + "extended_stabilizer": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "save_statevector", + ] + ), + "unitary": sorted( + [ + "save_state", + "save_unitary", + "set_unitary", + ] + ), + "superop": sorted( + [ + "quantum_channel", + "qerror_loc", + "kraus", + "superop", + "save_state", + "save_superop", + "set_superop", + ] + ), + "tensor_network": sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "superop", + "save_state", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_density_matrix", + "save_amplitudes", + "save_amplitudes_sq", + "save_statevector", + "save_statevector_dict", + "set_statevector", + "set_density_matrix", + ] + ), } # Automatic method custom instructions are the union of statevector, # density matrix, and stabilizer methods - _CUSTOM_INSTR[None] = _CUSTOM_INSTR['automatic'] = sorted( - set(_CUSTOM_INSTR['statevector']).union( - _CUSTOM_INSTR['stabilizer']).union( - _CUSTOM_INSTR['density_matrix']).union( - _CUSTOM_INSTR['matrix_product_state']).union( - _CUSTOM_INSTR['unitary']).union( - _CUSTOM_INSTR['superop']).union( - _CUSTOM_INSTR['tensor_network'])) + _CUSTOM_INSTR[None] = _CUSTOM_INSTR["automatic"] = sorted( + set(_CUSTOM_INSTR["statevector"]) + .union(_CUSTOM_INSTR["stabilizer"]) + .union(_CUSTOM_INSTR["density_matrix"]) + .union(_CUSTOM_INSTR["matrix_product_state"]) + .union(_CUSTOM_INSTR["unitary"]) + .union(_CUSTOM_INSTR["superop"]) + .union(_CUSTOM_INSTR["tensor_network"]) + ) _DEFAULT_CONFIGURATION = { - 'backend_name': 'aer_simulator', - 'backend_version': __version__, - 'n_qubits': MAX_QUBITS_STATEVECTOR, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'local': True, - 'conditional': True, - 'open_pulse': False, - 'memory': True, - 'max_shots': int(1e6), - 'description': 'A C++ QasmQobj simulator with noise', - 'coupling_map': None, - 'basis_gates': BASIS_GATES['automatic'], - 'custom_instructions': _CUSTOM_INSTR['automatic'], - 'gates': [] + "backend_name": "aer_simulator", + "backend_version": __version__, + "n_qubits": MAX_QUBITS_STATEVECTOR, + "url": "https://github.com/Qiskit/qiskit-aer", + "simulator": True, + "local": True, + "conditional": True, + "open_pulse": False, + "memory": True, + "max_shots": int(1e6), + "description": "A C++ QasmQobj simulator with noise", + "coupling_map": None, + "basis_gates": BASIS_GATES["automatic"], + "custom_instructions": _CUSTOM_INSTR["automatic"], + "gates": [], } _SIMULATION_METHODS = [ - 'automatic', 'statevector', 'density_matrix', - 'stabilizer', 'matrix_product_state', 'extended_stabilizer', - 'unitary', 'superop', 'tensor_network' + "automatic", + "statevector", + "density_matrix", + "stabilizer", + "matrix_product_state", + "extended_stabilizer", + "unitary", + "superop", + "tensor_network", ] _AVAILABLE_METHODS = None - _SIMULATION_DEVICES = ('CPU', 'GPU', 'Thrust') + _SIMULATION_DEVICES = ("CPU", "GPU", "Thrust") _AVAILABLE_DEVICES = None - def __init__(self, - configuration=None, - properties=None, - provider=None, - **backend_options): - + def __init__(self, configuration=None, properties=None, provider=None, **backend_options): self._controller = aer_controller_execute() # Update available methods and devices for class if AerSimulator._AVAILABLE_DEVICES is None: AerSimulator._AVAILABLE_DEVICES = available_devices( - self._controller, AerSimulator._SIMULATION_DEVICES) + self._controller, AerSimulator._SIMULATION_DEVICES + ) if AerSimulator._AVAILABLE_METHODS is None: AerSimulator._AVAILABLE_METHODS = available_methods( - self._controller, AerSimulator._SIMULATION_METHODS, AerSimulator._AVAILABLE_DEVICES) + self._controller, AerSimulator._SIMULATION_METHODS, AerSimulator._AVAILABLE_DEVICES + ) # Default configuration if configuration is None: - configuration = QasmBackendConfiguration.from_dict( - AerSimulator._DEFAULT_CONFIGURATION) + configuration = QasmBackendConfiguration.from_dict(AerSimulator._DEFAULT_CONFIGURATION) # Cache basis gates since computing the intersection # of noise model, method, and config gates is expensive. - self._cached_basis_gates = self._BASIS_GATES['automatic'] + self._cached_basis_gates = self._BASIS_GATES["automatic"] - super().__init__(configuration, - properties=properties, - provider=provider, - backend_options=backend_options) + super().__init__( + configuration, properties=properties, provider=provider, backend_options=backend_options + ) @classmethod def _default_options(cls): return Options( # Global options shots=1024, - method='automatic', - device='CPU', + method="automatic", + device="CPU", precision="double", executor=None, max_job_size=None, @@ -614,7 +715,7 @@ def _default_options(cls): # stabilizer options stabilizer_max_snapshot_probabilities=32, # extended stabilizer options - extended_stabilizer_sampling_method='resampled_metropolis', + extended_stabilizer_sampling_method="resampled_metropolis", extended_stabilizer_metropolis_mixing_time=5000, extended_stabilizer_approximation_error=0.05, extended_stabilizer_norm_estimation_samples=100, @@ -624,35 +725,35 @@ def _default_options(cls): # MPS options matrix_product_state_truncation_threshold=1e-16, matrix_product_state_max_bond_dimension=None, - mps_sample_measure_algorithm='mps_heuristic', + mps_sample_measure_algorithm="mps_heuristic", mps_log_data=False, - mps_swap_direction='mps_swap_left', + mps_swap_direction="mps_swap_left", chop_threshold=1e-8, mps_parallel_threshold=14, mps_omp_threads=1, # tensor network options tensor_network_num_sampling_qubits=10, - use_cuTensorNet_autotuning=False + use_cuTensorNet_autotuning=False, ) def __repr__(self): """String representation of an AerSimulator.""" display = super().__repr__() - noise_model = getattr(self.options, 'noise_model', None) + noise_model = getattr(self.options, "noise_model", None) if noise_model is None or noise_model.is_ideal(): return display - pad = ' ' * (len(self.__class__.__name__) + 1) - return f'{display[:-1]}\n{pad}noise_model={repr(noise_model)})' + pad = " " * (len(self.__class__.__name__) + 1) + return f"{display[:-1]}\n{pad}noise_model={repr(noise_model)})" def name(self): """Format backend name string for simulator""" name = self._configuration.backend_name - method = getattr(self.options, 'method', None) - if method not in [None, 'automatic']: - name += f'_{method}' - device = getattr(self.options, 'device', None) - if device not in [None, 'CPU']: - name += f'_{device}'.lower() + method = getattr(self.options, "method", None) + if method not in [None, "automatic"]: + name += f"_{method}" + device = getattr(self.options, "device", None) + if device not in [None, "CPU"]: + name += f"_{device}".lower() return name @classmethod @@ -682,26 +783,24 @@ def from_backend(cls, backend, **options): # Customize configuration name name = configuration.backend_name - configuration.backend_name = f'aer_simulator({name})' + configuration.backend_name = f"aer_simulator({name})" else: raise TypeError( "The backend argument requires a BackendV2 or BackendV1 object, " f"not a {type(backend)} object" ) # Use automatic noise model if none is provided - if 'noise_model' not in options: + if "noise_model" not in options: # pylint: disable=import-outside-toplevel # Avoid cyclic import from ..noise.noise_model import NoiseModel noise_model = NoiseModel.from_backend(backend) if not noise_model.is_ideal(): - options['noise_model'] = noise_model + options["noise_model"] = noise_model # Initialize simulator - sim = cls(configuration=configuration, - properties=properties, - **options) + sim = cls(configuration=configuration, properties=properties, **options) return sim def available_methods(self): @@ -724,15 +823,15 @@ def configuration(self): # Update basis gates based on custom options, config, method, # and noise model config.custom_instructions = self._CUSTOM_INSTR[ - getattr(self.options, 'method', 'automatic')] + getattr(self.options, "method", "automatic") + ] config.basis_gates = self._cached_basis_gates + config.custom_instructions # Update simulator name config.backend_name = self.name() return config def _execute_circuits(self, aer_circuits, noise_model, config): - """Execute circuits on the backend. - """ + """Execute circuits on the backend.""" ret = cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) return ret @@ -752,10 +851,11 @@ def set_option(self, key, value): self._set_configuration_option(key, value) return if key == "method": - if (value is not None and value not in self.available_methods()): + if value is not None and value not in self.available_methods(): raise AerError( f"Invalid simulation method {value}. Available methods" - f" are: {self.available_methods()}") + f" are: {self.available_methods()}" + ) self._set_method_config(value) super().set_option(key, value) if key in ["method", "noise_model", "basis_gates"]: @@ -776,9 +876,9 @@ def _validate(self, qobj): break if no_data: logger.warning( - 'No measure or save instruction in circuit "%s": ' - 'results will be empty.', - experiment.header.name) + 'No measure or save instruction in circuit "%s": ' "results will be empty.", + experiment.header.name, + ) def _basis_gates(self): """Return simualtor basis gates. @@ -788,21 +888,20 @@ def _basis_gates(self): and method supported basis gates. """ # Use option value for basis gates if set - if 'basis_gates' in self._options_configuration: - return self._options_configuration['basis_gates'] + if "basis_gates" in self._options_configuration: + return self._options_configuration["basis_gates"] # Compute intersection with method basis gates - method = getattr(self._options, 'method', 'automatic') + method = getattr(self._options, "method", "automatic") method_gates = self._BASIS_GATES[method] config_gates = self._configuration.basis_gates if config_gates: - basis_gates = set(config_gates).intersection( - method_gates) + basis_gates = set(config_gates).intersection(method_gates) else: basis_gates = method_gates # Compute intersection with noise model basis gates - noise_model = getattr(self.options, 'noise_model', None) + noise_model = getattr(self.options, "noise_model", None) if noise_model: noise_gates = noise_model.basis_gates basis_gates = basis_gates.intersection(noise_gates) @@ -814,36 +913,43 @@ def _basis_gates(self): "The intersection of configuration basis gates (%s), " "simulation method basis gates (%s), and " "noise model basis gates (%s) is empty", - config_gates, method_gates, noise_gates) + config_gates, + method_gates, + noise_gates, + ) return sorted(basis_gates) def _set_method_config(self, method=None): """Set non-basis gate options when setting method""" # Update configuration description and number of qubits - if method == 'statevector': - description = 'A C++ statevector simulator with noise' + if method == "statevector": + description = "A C++ statevector simulator with noise" n_qubits = MAX_QUBITS_STATEVECTOR - elif method == 'density_matrix': - description = 'A C++ density matrix simulator with noise' + elif method == "density_matrix": + description = "A C++ density matrix simulator with noise" n_qubits = MAX_QUBITS_STATEVECTOR // 2 - elif method == 'unitary': - description = 'A C++ unitary matrix simulator' + elif method == "unitary": + description = "A C++ unitary matrix simulator" n_qubits = MAX_QUBITS_STATEVECTOR // 2 - elif method == 'superop': - description = 'A C++ superop matrix simulator with noise' + elif method == "superop": + description = "A C++ superop matrix simulator with noise" n_qubits = MAX_QUBITS_STATEVECTOR // 4 - elif method == 'matrix_product_state': - description = 'A C++ matrix product state simulator with noise' + elif method == "matrix_product_state": + description = "A C++ matrix product state simulator with noise" n_qubits = 63 # TODO: not sure what to put here? - elif method == 'stabilizer': - description = 'A C++ Clifford stabilizer simulator with noise' + elif method == "stabilizer": + description = "A C++ Clifford stabilizer simulator with noise" n_qubits = 10000 # TODO: estimate from memory - elif method == 'extended_stabilizer': - description = 'A C++ Clifford+T extended stabilizer simulator with noise' + elif method == "extended_stabilizer": + description = "A C++ Clifford+T extended stabilizer simulator with noise" n_qubits = 63 # TODO: estimate from memory else: # Clear options to default description = None n_qubits = None - self._set_configuration_option('description', description) - self._set_configuration_option('n_qubits', n_qubits) + + if self._configuration.coupling_map: + n_qubits = max(list(map(max, self._configuration.coupling_map))) + 1 + + self._set_configuration_option("description", description) + self._set_configuration_option("n_qubits", n_qubits) diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index 501ff50756..859cb986da 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -35,6 +35,9 @@ from .aer_compiler import compile_circuit, assemble_circuits, generate_aer_config from .backend_utils import format_save_type, circuit_optypes +# pylint: disable=import-error, no-name-in-module +from .controller_wrappers import AerConfig + # Logger logger = logging.getLogger(__name__) @@ -42,12 +45,9 @@ class AerBackend(Backend, ABC): """Qiskit Aer Backend class.""" - def __init__(self, - configuration, - properties=None, - defaults=None, - backend_options=None, - provider=None): + def __init__( + self, configuration, properties=None, defaults=None, backend_options=None, provider=None + ): """Aer class for backends. This method should initialize the module and its configuration, and @@ -84,28 +84,34 @@ def __init__(self, def _convert_circuit_binds(self, circuit, binds): parameterizations = [] + + def append_param_values(index, bind_pos, param): + if param in binds: + parameterizations.append([(index, bind_pos), binds[param]]) + elif isinstance(param, ParameterExpression): + # If parameter expression has no unbound parameters + # it's already bound and should be skipped + if not param.parameters: + return + if not binds: + raise AerError("The element of parameter_binds is empty.") + len_vals = len(next(iter(binds.values()))) + bind_list = [ + { + parameter: binds[parameter][i] + for parameter in param.parameters & binds.keys() + } + for i in range(len_vals) + ] + bound_values = [float(param.bind(x)) for x in bind_list] + parameterizations.append([(index, bind_pos), bound_values]) + + append_param_values(AerConfig.GLOBAL_PHASE_POS, -1, circuit.global_phase) + for index, instruction in enumerate(circuit.data): if instruction.operation.is_parameterized(): for bind_pos, param in enumerate(instruction.operation.params): - if param in binds: - parameterizations.append([(index, bind_pos), binds[param]]) - elif isinstance(param, ParameterExpression): - # If parameter expression has no unbound parameters - # it's already bound and should be skipped - if not param.parameters: - continue - if not binds: - raise AerError("The element of parameter_binds is empty.") - len_vals = len(next(iter(binds.values()))) - bind_list = [ - { - parameter: binds[parameter][i] - for parameter in param.parameters & binds.keys() - } - for i in range(len_vals) - ] - bound_values = [float(param.bind(x)) for x in bind_list] - parameterizations.append([(index, bind_pos), bound_values]) + append_param_values(index, bind_pos, param) return parameterizations def _convert_binds(self, circuits, parameter_binds): @@ -120,17 +126,13 @@ def _convert_binds(self, circuits, parameter_binds): "parameter bind dictionaries" ) parameterizations = [ - self._convert_circuit_binds( - circuit, parameter_binds[idx]) for idx, circuit in enumerate(circuits) + self._convert_circuit_binds(circuit, parameter_binds[idx]) + for idx, circuit in enumerate(circuits) ] return parameterizations # pylint: disable=arguments-differ - def run(self, - circuits, - validate=False, - parameter_binds=None, - **run_options): + def run(self, circuits, validate=False, parameter_binds=None, **run_options): """Run a qobj on the backend. Args: @@ -172,11 +174,13 @@ def run(self, if isinstance(circuits, (QasmQobj, PulseQobj)): warnings.warn( - 'Using a qobj for run() is deprecated as of qiskit-aer 0.9.0' - ' and will be removed no sooner than 3 months from that release' - ' date. Transpiled circuits should now be passed directly using' - ' `backend.run(circuits, **run_options).', - DeprecationWarning, stacklevel=2) + "Using a qobj for run() is deprecated as of qiskit-aer 0.9.0" + " and will be removed no sooner than 3 months from that release" + " date. Transpiled circuits should now be passed directly using" + " `backend.run(circuits, **run_options).", + DeprecationWarning, + stacklevel=2, + ) if parameter_binds: raise TypeError("Parameter binds can't be used with an input qobj") # A work around to support both qobj options and run options until @@ -191,8 +195,8 @@ def run(self, for key, value in circuits.config.__dict__.items(): if key not in run_options and value is not None: run_options[key] = value - if 'parameter_binds' in run_options: - parameter_binds = run_options.pop('parameter_binds') + if "parameter_binds" in run_options: + parameter_binds = run_options.pop("parameter_binds") return self._run_qobj(circuits, validate, parameter_binds, **run_options) only_circuits = True @@ -203,12 +207,14 @@ def run(self, if only_circuits and not only_pulse: if validate: - raise TypeError("bad input to run() function;" - "`validation` argument is only effective for input qobj") - - executor = run_options.get('executor', None) - if executor is None and 'executor' in self.options.__dict__: - executor = self.options.__dict__.get('executor', None) + raise TypeError( + "bad input to run() function;" + "`validation` argument is only effective for input qobj" + ) + + executor = run_options.get("executor", None) + if executor is None and "executor" in self.options.__dict__: + executor = self.options.__dict__.get("executor", None) if executor: # This path remains for DASK execution to split a qobj insttance # into sub-qobj instances. This will be replaced with _run_circuits path @@ -219,39 +225,41 @@ def run(self, elif not only_circuits and only_pulse: return self._run_qobj(circuits, validate, parameter_binds, **run_options) elif not only_circuits and not only_pulse: - raise TypeError("bad input to run() function;" - "circuits and schedules cannot be mixed in a single run") + raise TypeError( + "bad input to run() function;" + "circuits and schedules cannot be mixed in a single run" + ) else: - raise TypeError("bad input to run() function;" - "circuits must be either circuits or schedules") - - def _run_circuits(self, - circuits, - parameter_binds, - **run_options): - """Run circuits by generating native circuits. - """ + raise TypeError( + "bad input to run() function;" "circuits must be either circuits or schedules" + ) + + def _run_circuits(self, circuits, parameter_binds, **run_options): + """Run circuits by generating native circuits.""" circuits, noise_model = self._compile(circuits, **run_options) if parameter_binds: - run_options['parameterizations'] = self._convert_binds(circuits, parameter_binds) + run_options["parameterizations"] = self._convert_binds(circuits, parameter_binds) + elif not all([len(circuit.parameters) == 0 for circuit in circuits]): + raise AerError("circuits have parameters but parameter_binds is not specified.") config = generate_aer_config(circuits, self.options, **run_options) # Submit job job_id = str(uuid.uuid4()) - aer_job = AerJob(self, job_id, self._execute_circuits_job, - circuits=circuits, noise_model=noise_model, config=config) + aer_job = AerJob( + self, + job_id, + self._execute_circuits_job, + circuits=circuits, + noise_model=noise_model, + config=config, + ) aer_job.submit() return aer_job # pylint: disable=arguments-differ - def _run_qobj(self, - circuits, - validate=False, - parameter_binds=None, - **run_options): - """Run circuits by assembling qobj. - """ + def _run_qobj(self, circuits, validate=False, parameter_binds=None, **run_options): + """Run circuits by assembling qobj.""" qobj = self._assemble(circuits, parameter_binds=parameter_binds, **run_options) # Optional validation @@ -259,19 +267,21 @@ def _run_qobj(self, self._validate(qobj) # Get executor from qobj config and delete attribute so qobj can still be serialized - executor = getattr(qobj.config, 'executor', None) - if hasattr(qobj.config, 'executor'): - delattr(qobj.config, 'executor') + executor = getattr(qobj.config, "executor", None) + if hasattr(qobj.config, "executor"): + delattr(qobj.config, "executor") # Optionally split the job experiments = split_qobj( - qobj, max_size=getattr(qobj.config, 'max_job_size', None), - max_shot_size=getattr(qobj.config, 'max_shot_size', None)) + qobj, + max_size=getattr(qobj.config, "max_job_size", None), + max_shot_size=getattr(qobj.config, "max_shot_size", None), + ) # Temporarily remove any executor from options so that job submission # can work with Dask client executors which can't be pickled - opts_executor = getattr(self._options, 'executor', None) - if hasattr(self._options, 'executor'): + opts_executor = getattr(self._options, "executor", None) + if hasattr(self._options, "executor"): self._options.executor = None # Submit job @@ -279,12 +289,13 @@ def _run_qobj(self, if isinstance(experiments, list): aer_job = AerJobSet(self, job_id, self._execute_qobj_job, experiments, executor) else: - aer_job = AerJob(self, job_id, self._execute_qobj_job, qobj=experiments, - executor=executor) + aer_job = AerJob( + self, job_id, self._execute_qobj_job, qobj=experiments, executor=executor + ) aer_job.submit() # Restore removed executor after submission - if hasattr(self._options, 'executor'): + if hasattr(self._options, "executor"): self._options.executor = opts_executor return aer_job @@ -300,7 +311,7 @@ def configuration(self): setattr(config, key, val) # If config has custom instructions add them to # basis gates to include them for the terra transpiler - if hasattr(config, 'custom_instructions'): + if hasattr(config, "custom_instructions"): config.basis_gates = config.basis_gates + config.custom_instructions return config @@ -350,9 +361,10 @@ def status(self): backend_version=self.configuration().backend_version, operational=True, pending_jobs=0, - status_msg='') + status_msg="", + ) - def _execute_qobj_job(self, qobj, job_id='', format_result=True): + def _execute_qobj_job(self, qobj, job_id="", format_result=True): """Run a qobj job""" # Start timer start = time.time() @@ -385,9 +397,8 @@ def _execute_qobj_job(self, qobj, job_id='', format_result=True): if not isinstance(output, dict): logger.error("%s: simulation failed.", self.name()) if output: - logger.error('Output: %s', output) - raise AerError( - "simulation terminated without returning valid output.") + logger.error("Output: %s", output) + raise AerError("simulation terminated without returning valid output.") # Format results output["job_id"] = job_id @@ -397,9 +408,11 @@ def _execute_qobj_job(self, qobj, job_id='', format_result=True): # Push metadata to experiment headers for result in output["results"]: - if ("header" in result and - "metadata" in result["header"] and - "metadata_index" in result["header"]["metadata"]): + if ( + "header" in result + and "metadata" in result["header"] + and "metadata_index" in result["header"]["metadata"] + ): metadata_index = result["header"]["metadata"]["metadata_index"] result["header"]["metadata"] = metadata_list[metadata_index] @@ -416,34 +429,24 @@ def _execute_qobj_job(self, qobj, job_id='', format_result=True): return self._format_results(output) return output - def _execute_circuits_job(self, circuits, noise_model, config, job_id='', - format_result=True): + def _execute_circuits_job(self, circuits, noise_model, config, job_id="", format_result=True): """Run a job""" # Start timer start = time.time() - # Take metadata from headers of experiments to work around JSON serialization error - metadata_list = [] - for idx, circ in enumerate(circuits): - if circ.metadata: - metadata = circ.metadata - metadata_list.append(metadata) - circ.metadata = {} - circ.metadata["metadata_index"] = idx - else: - metadata_list.append(None) - # Run simulation aer_circuits = assemble_circuits(circuits) + metadata_map = { + aer_circuit: circuit.metadata for aer_circuit, circuit in zip(aer_circuits, circuits) + } output = self._execute_circuits(aer_circuits, noise_model, config) # Validate output if not isinstance(output, dict): logger.error("%s: simulation failed.", self.name()) if output: - logger.error('Output: %s', output) - raise AerError( - "simulation terminated without returning valid output.") + logger.error("Output: %s", output) + raise AerError("simulation terminated without returning valid output.") # Format results output["job_id"] = job_id @@ -453,14 +456,9 @@ def _execute_circuits_job(self, circuits, noise_model, config, job_id='', # Push metadata to experiment headers for result in output["results"]: - if ("header" in result and - "metadata" in result["header"] and result["header"]["metadata"] and - "metadata_index" in result["header"]["metadata"]): - metadata_index = result["header"]["metadata"]["metadata_index"] - result["header"]["metadata"] = metadata_list[metadata_index] - - for circ, metadata in zip(circuits, metadata_list): - circ.metadata = metadata + if "header" not in result: + continue + result["header"]["metadata"] = metadata_map[result["circuit"]] # Add execution time output["time_taken"] = time.time() - start @@ -496,13 +494,13 @@ def _compile(self, circuits, **run_options): # Compile Qasm3 instructions circuits, optypes = compile_circuit( - circuits, - basis_gates=self.configuration().basis_gates, - optypes=optypes) + circuits, basis_gates=self.configuration().basis_gates, optypes=optypes + ) # run option noise model circuits, noise_model, run_options = self._assemble_noise_model( - circuits, optypes, **run_options) + circuits, optypes, **run_options + ) return circuits, noise_model @@ -517,7 +515,7 @@ def _assemble(self, circuits, parameter_binds=None, **run_options): # If noise model exists, add it to the run options if noise_model: - run_options['noise_model'] = noise_model + run_options["noise_model"] = noise_model if parameter_binds: # Handle parameter binding @@ -529,7 +527,8 @@ def _assemble(self, circuits, parameter_binds=None, **run_options): [circuit], backend=self, parameter_binds=[assemble_bind], - parameterizations=parameterizations) + parameterizations=parameterizations, + ) if qobj: qobj.experiments.append(qobj_tmp.experiments[0]) else: @@ -557,8 +556,7 @@ def _assemble_noise_model(self, circuits, optypes, **run_options): # This avoids unnecessarily copying the noise model for circuits # that do not contain a quantum error updated_noise = False - noise_model = run_options.get( - 'noise_model', getattr(self.options, 'noise_model', None)) + noise_model = run_options.get("noise_model", getattr(self.options, "noise_model", None)) # Add custom pass noise only to QuantumCircuit objects that contain delay # instructions since this is the only instruction handled by the noise pass @@ -567,9 +565,7 @@ def _assemble_noise_model(self, circuits, optypes, **run_options): npm = noise_model._pass_manager() if npm is not None: # Get indicies of circuits that need noise transpiling - transpile_idxs = [ - idx for idx, optype in enumerate(optypes) if Delay in optype - ] + transpile_idxs = [idx for idx, optype in enumerate(optypes) if Delay in optype] # Transpile only the required circuits transpiled_circuits = npm.run([run_circuits[i] for i in transpile_idxs]) @@ -584,7 +580,8 @@ def _assemble_noise_model(self, circuits, optypes, **run_options): # Check if circuits contain quantum error instructions for idx, circ in enumerate(run_circuits): if QuantumChannelInstruction in optypes[idx] and not isinstance( - circ, (Schedule, ScheduleBlock)): + circ, (Schedule, ScheduleBlock) + ): updated_circ = False new_data = [] for inst, qargs, cargs in circ.data: @@ -618,10 +615,10 @@ def _assemble_noise_model(self, circuits, optypes, **run_options): def _get_executor(self, **run_options): """Get the executor""" - if 'executor' in run_options: - return run_options['executor'] + if "executor" in run_options: + return run_options["executor"] else: - return getattr(self._options, 'executor', None) + return getattr(self._options, "executor", None) @abstractmethod def _execute_qobj(self, qobj): @@ -718,4 +715,4 @@ def __repr__(self): """String representation of an AerBackend.""" name = self.__class__.__name__ display = f"'{self.name()}'" - return f'{name}({display})' + return f"{name}({display})" diff --git a/qiskit_aer/backends/backend_utils.py b/qiskit_aer/backends/backend_utils.py index c85049e6f2..446406c796 100644 --- a/qiskit_aer/backends/backend_utils.py +++ b/qiskit_aer/backends/backend_utils.py @@ -22,11 +22,10 @@ from qiskit.qobj import QasmQobjInstruction from qiskit.result import ProbDistribution from qiskit.quantum_info import Clifford -from .compatibility import ( - Statevector, DensityMatrix, StabilizerState, Operator, SuperOp) +from .compatibility import Statevector, DensityMatrix, StabilizerState, Operator, SuperOp # Available system memory -SYSTEM_MEMORY_GB = local_hardware_info()['memory'] +SYSTEM_MEMORY_GB = local_hardware_info()["memory"] # Max number of qubits for complex double statevector # given available system memory @@ -49,71 +48,370 @@ } BASIS_GATES = { - 'statevector': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcp', 'mcphase', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', - 'initialize', 'delay', 'pauli', 'mcx_gray', 'ecr' - ]), - 'density_matrix': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'cp', 'cu1', 'rxx', 'ryy', 'rzz', 'rzx', 'ccx', - 'unitary', 'diagonal', 'delay', 'pauli', 'ecr', - ]), - 'matrix_product_state': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'cp', 'cx', 'cy', 'cz', 'id', 'x', 'y', 'z', 'h', 's', - 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'ccx', 'unitary', 'roerror', 'delay', 'pauli', - 'r', 'rx', 'ry', 'rz', 'rxx', 'ryy', 'rzz', 'rzx', 'csx', 'cswap', 'diagonal', - 'initialize' - ]), - 'stabilizer': sorted([ - 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 'cx', 'cy', 'cz', - 'swap', 'delay', 'pauli' - ]), - 'extended_stabilizer': sorted([ - 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', - 'swap', 'u0', 't', 'tdg', 'u1', 'p', 'ccx', 'ccz', 'delay', 'pauli' - ]), - 'unitary': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcp', 'mcphase', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'delay', 'pauli', - 'ecr', - ]), - 'superop': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'cp', 'cu1', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'unitary', 'diagonal', 'delay', 'pauli' - ]), - 'tensor_network': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcp', 'mcphase', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', - 'initialize', 'delay', 'pauli', 'mcx_gray' - ]) + "statevector": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cp", + "cu", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcp", + "mcphase", + "mcu", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "initialize", + "delay", + "pauli", + "mcx_gray", + "ecr", + ] + ), + "density_matrix": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "cp", + "cu1", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "unitary", + "diagonal", + "delay", + "pauli", + "ecr", + ] + ), + "matrix_product_state": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "cp", + "cx", + "cy", + "cz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "ccx", + "unitary", + "roerror", + "delay", + "pauli", + "r", + "rx", + "ry", + "rz", + "rxx", + "ryy", + "rzz", + "rzx", + "csx", + "cswap", + "diagonal", + "initialize", + ] + ), + "stabilizer": sorted( + [ + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "cx", + "cy", + "cz", + "swap", + "delay", + "pauli", + ] + ), + "extended_stabilizer": sorted( + [ + "cx", + "cz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "swap", + "u0", + "t", + "tdg", + "u1", + "p", + "ccx", + "ccz", + "delay", + "pauli", + ] + ), + "unitary": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cp", + "cu", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcp", + "mcphase", + "mcu", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "delay", + "pauli", + "ecr", + ] + ), + "superop": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "cp", + "cu1", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "unitary", + "diagonal", + "delay", + "pauli", + ] + ), + "tensor_network": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cp", + "cu", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcp", + "mcphase", + "mcu", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "initialize", + "delay", + "pauli", + "mcx_gray", + ] + ), } # Automatic method basis gates are the union of statevector, # density matrix, and stabilizer methods -BASIS_GATES[None] = BASIS_GATES['automatic'] = sorted( - set(BASIS_GATES['statevector']).union( - BASIS_GATES['stabilizer']).union( - BASIS_GATES['density_matrix']).union( - BASIS_GATES['matrix_product_state']).union( - BASIS_GATES['unitary']).union( - BASIS_GATES['superop']).union( - BASIS_GATES['tensor_network'])) +BASIS_GATES[None] = BASIS_GATES["automatic"] = sorted( + set(BASIS_GATES["statevector"]) + .union(BASIS_GATES["stabilizer"]) + .union(BASIS_GATES["density_matrix"]) + .union(BASIS_GATES["matrix_product_state"]) + .union(BASIS_GATES["unitary"]) + .union(BASIS_GATES["superop"]) + .union(BASIS_GATES["tensor_network"]) +) def cpp_execute_qobj(controller, qobj): @@ -147,13 +445,11 @@ def available_methods(controller, methods, devices): for device in devices: for method in methods: if method not in valid_methods: - qobj = assemble(dummy_circ, - optimization_level=0, - shots=1, - method=method, - device=device) + qobj = assemble( + dummy_circ, optimization_level=0, shots=1, method=method, device=device + ) result = cpp_execute_qobj(controller, qobj) - if result.get('success', False): + if result.get("success", False): valid_methods.append(method) return tuple(valid_methods) @@ -166,13 +462,11 @@ def available_devices(controller, devices): valid_devices = [] for device in devices: - qobj = assemble(dummy_circ, - optimization_level=0, - shots=1, - method="statevector", - device=device) + qobj = assemble( + dummy_circ, optimization_level=0, shots=1, method="statevector", device=device + ) result = cpp_execute_qobj(controller, qobj) - if result.get('success', False): + if result.get("success", False): valid_devices.append(device) return tuple(valid_devices) @@ -186,7 +480,8 @@ def save_inst(num_qubits): name=f"save_{state}", qubits=list(range(num_qubits)), label=f"{state}", - snapshot_type="single") + snapshot_type="single", + ) for exp in qobj.experiments: num_qubits = exp.config.n_qubits @@ -200,10 +495,7 @@ def add_final_save_op(aer_circuits, state): for aer_circuit in aer_circuits: num_qubits = aer_circuit.num_qubits - aer_circuit.save_state(list(range(num_qubits)), - f"save_{state}", - "single", - state) + aer_circuit.save_state(list(range(num_qubits)), f"save_{state}", "single", state) return aer_circuits @@ -241,9 +533,11 @@ def format_save_type(data, save_type, save_subtype): return data if save_subtype in ["list", "c_list"]: + def func(data): init_fn = init_fns[save_type] return [init_fn(i) for i in data] + else: func = init_fns[save_type] diff --git a/qiskit_aer/backends/compatibility.py b/qiskit_aer/backends/compatibility.py index faaccda273..068edda4be 100644 --- a/qiskit_aer/backends/compatibility.py +++ b/qiskit_aer/backends/compatibility.py @@ -30,7 +30,7 @@ def _forward_attr(attr): # Pass Iterable magic methods on to the Numpy array. We can't pass # `__getitem__` (and consequently `__setitem__`, `__delitem__`) on because # `Statevector` implements them itself. - if attr[:2] == '__' or attr in ['_data', '_op_shape']: + if attr[:2] == "__" or attr in ["_data", "_op_shape"]: return False return True @@ -60,9 +60,9 @@ def __getattr__(self, attr): def __eq__(self, other): return ( - isinstance(self, type(other)) and - self._op_shape == other._op_shape and - np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) + isinstance(self, type(other)) + and self._op_shape == other._op_shape + and np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) ) def __bool__(self): @@ -103,9 +103,9 @@ def __getattr__(self, attr): def __eq__(self, other): return ( - isinstance(self, type(other)) and - self._op_shape == other._op_shape and - np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) + isinstance(self, type(other)) + and self._op_shape == other._op_shape + and np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) ) def __len__(self): @@ -144,9 +144,9 @@ def __getattr__(self, attr): def __eq__(self, other): return ( - isinstance(self, type(other)) and - self._op_shape == other._op_shape and - np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) + isinstance(self, type(other)) + and self._op_shape == other._op_shape + and np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) ) def __bool__(self): @@ -190,9 +190,9 @@ def __getattr__(self, attr): def __eq__(self, other): return ( - isinstance(self, type(other)) and - self._op_shape == other._op_shape and - np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) + isinstance(self, type(other)) + and self._op_shape == other._op_shape + and np.allclose(self.data, other.data, rtol=self.rtol, atol=self.atol) ) def __bool__(self): @@ -253,7 +253,9 @@ def __getitem__(self, item): " Accessing dict items is deprecated and will result in an" " error in a future release. Use the `.clifford.to_dict()` methods to access " " the stored Clifford operator and convert to a dictionary.", - DeprecationWarning, stacklevel=2) + DeprecationWarning, + stacklevel=2, + ) return self._data.to_dict()[item] raise TypeError("'StabilizerState object is not subscriptable'") diff --git a/qiskit_aer/backends/pulse_simulator.py b/qiskit_aer/backends/pulse_simulator.py index b3be282793..4fea7f0974 100644 --- a/qiskit_aer/backends/pulse_simulator.py +++ b/qiskit_aer/backends/pulse_simulator.py @@ -34,22 +34,22 @@ logger = logging.getLogger(__name__) DEFAULT_CONFIGURATION = { - 'backend_name': 'pulse_simulator', - 'backend_version': __version__, - 'n_qubits': 20, - 'coupling_map': None, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'meas_levels': [1, 2], - 'local': True, - 'conditional': True, - 'open_pulse': True, - 'memory': False, - 'max_shots': int(1e6), - 'description': 'A Pulse-based Hamiltonian simulator for Pulse Qobj files', - 'gates': [], - 'basis_gates': [], - 'parametric_pulses': [] + "backend_name": "pulse_simulator", + "backend_version": __version__, + "n_qubits": 20, + "coupling_map": None, + "url": "https://github.com/Qiskit/qiskit-aer", + "simulator": True, + "meas_levels": [1, 2], + "local": True, + "conditional": True, + "open_pulse": True, + "memory": False, + "max_shots": int(1e6), + "description": "A Pulse-based Hamiltonian simulator for Pulse Qobj files", + "gates": [], + "basis_gates": [], + "parametric_pulses": [], } @@ -155,12 +155,10 @@ class PulseSimulator(AerBackend): are ``'atol'``, ``'rtol'``, ``'nsteps'``, ``'max_step'``, ``'num_cpus'``, ``'norm_tol'``, and ``'norm_steps'``. """ - def __init__(self, - configuration=None, - properties=None, - defaults=None, - provider=None, - **backend_options): + + def __init__( + self, configuration=None, properties=None, defaults=None, provider=None, **backend_options + ): warn( "The Pulse simulator backend in Qiskit Aer is deprecated and will " "be removed in a future release. Instead the qiskit-dynamics " @@ -170,8 +168,7 @@ def __init__(self, ) if configuration is None: - configuration = BackendConfiguration.from_dict( - DEFAULT_CONFIGURATION) + configuration = BackendConfiguration.from_dict(DEFAULT_CONFIGURATION) else: configuration = copy.copy(configuration) configuration.meas_levels = self._meas_levels(configuration.meas_levels) @@ -179,24 +176,23 @@ def __init__(self, configuration.parametric_pulses = [] if defaults is None: - defaults = PulseDefaults(qubit_freq_est=[inf], - meas_freq_est=[inf], - buffer=0, - cmd_def=[], - pulse_library=[]) - - super().__init__(configuration, - properties=properties, - defaults=defaults, - provider=provider, - backend_options=backend_options) + defaults = PulseDefaults( + qubit_freq_est=[inf], meas_freq_est=[inf], buffer=0, cmd_def=[], pulse_library=[] + ) + + super().__init__( + configuration, + properties=properties, + defaults=defaults, + provider=provider, + backend_options=backend_options, + ) # Set up default system model - subsystem_list = backend_options.get('subsystem_list', None) - if backend_options.get('system_model') is None: - if hasattr(configuration, 'hamiltonian'): - system_model = PulseSystemModel.from_config( - configuration, subsystem_list) + subsystem_list = backend_options.get("subsystem_list", None) + if backend_options.get("system_model") is None: + if hasattr(configuration, "hamiltonian"): + system_model = PulseSystemModel.from_config(configuration, subsystem_list) self._set_system_model(system_model) @classmethod @@ -218,13 +214,11 @@ def _default_options(cls): executor=None, memory_slots=1, max_job_size=None, - max_shot_size=None) + max_shot_size=None, + ) # pylint: disable=arguments-differ, missing-param-doc - def run(self, - schedules, - validate=True, - **run_options): + def run(self, schedules, validate=True, **run_options): """Run a qobj on the backend. Args: @@ -255,29 +249,29 @@ def run(self, @property def _system_model(self): - return getattr(self._options, 'system_model') + return getattr(self._options, "system_model") @classmethod def from_backend(cls, backend, **options): """Initialize simulator from backend.""" if isinstance(backend, BackendV2): - raise AerError( - "PulseSimulator.from_backend does not currently support V2 Backends." - ) + raise AerError("PulseSimulator.from_backend does not currently support V2 Backends.") configuration = copy.copy(backend.configuration()) defaults = copy.copy(backend.defaults()) properties = copy.copy(backend.properties()) - backend_name = 'pulse_simulator({})'.format(configuration.backend_name) - description = 'A Pulse-based simulator configured from the backend: ' + backend_name = "pulse_simulator({})".format(configuration.backend_name) + description = "A Pulse-based simulator configured from the backend: " description += configuration.backend_name - sim = cls(configuration=configuration, - properties=properties, - defaults=defaults, - backend_name=backend_name, - description=description, - **options) + sim = cls( + configuration=configuration, + properties=properties, + defaults=defaults, + backend_name=backend_name, + description=description, + **options, + ) return sim def _execute_qobj(self, qobj): @@ -293,42 +287,42 @@ def _execute_qobj(self, qobj): return pulse_controller(qobj) def _execute_circuits(self, aer_circuits, noise_model, config): - """Execute circuits on the backend. - """ + """Execute circuits on the backend.""" raise TypeError("pulse simulator does not support circuit execution") def set_option(self, key, value): """Set pulse simulation options and update backend.""" - if key == 'meas_levels': + if key == "meas_levels": self._set_configuration_option(key, self._meas_levels(value)) return # Handle cases that require updating two places - if key in ['dt', 'u_channel_lo']: + if key in ["dt", "u_channel_lo"]: self._set_configuration_option(key, value) if self._system_model is not None: setattr(self._system_model, key, value) return - if key == 'hamiltonian': + if key == "hamiltonian": # if option is hamiltonian, set in configuration and reconstruct pulse system model - subsystem_list = getattr(self._options.get, 'subsystem_list', None) - system_model = PulseSystemModel.from_config(self.configuration(), - subsystem_list) - super().set_option('system_model', system_model) + subsystem_list = getattr(self._options.get, "subsystem_list", None) + system_model = PulseSystemModel.from_config(self.configuration(), subsystem_list) + super().set_option("system_model", system_model) self._set_configuration_option(key, value) return # if system model is specified directly - if key == 'system_model': - if hasattr(self.configuration(), 'hamiltonian'): - warn('Specifying both a configuration with a Hamiltonian and a ' - 'system model may result in inconsistencies.') + if key == "system_model": + if hasattr(self.configuration(), "hamiltonian"): + warn( + "Specifying both a configuration with a Hamiltonian and a " + "system model may result in inconsistencies." + ) # Set config dt and u_channel_lo to system model values self._set_system_model(value) return - if key == 'qubit_lo_freq': + if key == "qubit_lo_freq": value = [freq * 1e-9 for freq in value] # Set all other options from AerBackend @@ -336,11 +330,9 @@ def set_option(self, key, value): def _set_system_model(self, system_model): """Set system model option""" - self._set_configuration_option( - 'dt', getattr(system_model, 'dt', [])) - self._set_configuration_option( - 'u_channel_lo', getattr(system_model, 'u_channel_lo', [])) - super().set_option('system_model', system_model) + self._set_configuration_option("dt", getattr(system_model, "dt", [])) + self._set_configuration_option("u_channel_lo", getattr(system_model, "u_channel_lo", [])) + super().set_option("system_model", system_model) def _validate(self, qobj): """Validation of qobj. @@ -348,28 +340,31 @@ def _validate(self, qobj): Ensures that exactly one Acquire instruction is present in each schedule. Checks SystemModel is in qobj config """ - if getattr(qobj.config, 'system_model', None) is None: + if getattr(qobj.config, "system_model", None) is None: raise AerError("PulseSimulator requires a system model to run.") for exp in qobj.experiments: num_acquires = 0 for instruction in exp.instructions: - if instruction.name == 'acquire': + if instruction.name == "acquire": num_acquires += 1 if num_acquires > 1: - raise AerError("PulseSimulator does not support multiple Acquire " - "instructions in a single schedule.") + raise AerError( + "PulseSimulator does not support multiple Acquire " + "instructions in a single schedule." + ) if num_acquires == 0: - raise AerError("PulseSimulator requires at least one Acquire " - "instruction per schedule.") + raise AerError( + "PulseSimulator requires at least one Acquire " "instruction per schedule." + ) @staticmethod def _meas_levels(meas_levels): """Function for setting meas_levels in a pulse simulator configuration.""" if 0 in meas_levels: - warn('Measurement level 0 not supported in pulse simulator.') + warn("Measurement level 0 not supported in pulse simulator.") tmp = copy.copy(meas_levels) tmp.remove(0) return tmp diff --git a/qiskit_aer/backends/qasm_simulator.py b/qiskit_aer/backends/qasm_simulator.py index 4a17dd8f37..ade47cd01e 100644 --- a/qiskit_aer/backends/qasm_simulator.py +++ b/qiskit_aer/backends/qasm_simulator.py @@ -23,13 +23,16 @@ from ..version import __version__ from ..aererror import AerError from .aerbackend import AerBackend -from .backend_utils import (cpp_execute_qobj, - cpp_execute_circuits, - available_methods, - MAX_QUBITS_STATEVECTOR, - LEGACY_METHOD_MAP, - map_legacy_method_options, - map_legacy_method_config) +from .backend_utils import ( + cpp_execute_qobj, + cpp_execute_circuits, + available_methods, + MAX_QUBITS_STATEVECTOR, + LEGACY_METHOD_MAP, + map_legacy_method_options, + map_legacy_method_config, +) + # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -197,9 +200,7 @@ class QasmSimulator(AerBackend): simulation method: * ``stabilizer_max_snapshot_probabilities`` (int): set the maximum - qubit number for the - `~qiskit_aer.extensions.SnapshotProbabilities` - instruction (Default: 32). + qubit number for the :class:`~qiskit_aer.library.SaveProbabilities` instruction (Default: 32). These backend options only apply when using the ``"extended_stabilizer"`` simulation method: @@ -303,79 +304,153 @@ class QasmSimulator(AerBackend): than or equal to enable fusion optimization [Default: 14] """ - _DEFAULT_BASIS_GATES = sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcp', 'mcphase', 'mcu', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', - 'initialize', 'delay', 'pauli', 'mcx_gray', 'ecr' - ]) - - _DEFAULT_CUSTOM_INSTR = sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', - 'save_expval', 'save_expval_var', - 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes', 'save_amplitudes_sq', 'save_state', - 'save_density_matrix', 'save_statevector', 'save_statevector_dict', - 'save_stabilizer', 'set_statevector', 'set_density_matrix', - 'set_stabilizer', - ]) + _DEFAULT_BASIS_GATES = sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cp", + "cu", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcp", + "mcphase", + "mcu", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "initialize", + "delay", + "pauli", + "mcx_gray", + "ecr", + ] + ) + + _DEFAULT_CUSTOM_INSTR = sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_amplitudes", + "save_amplitudes_sq", + "save_state", + "save_density_matrix", + "save_statevector", + "save_statevector_dict", + "save_stabilizer", + "set_statevector", + "set_density_matrix", + "set_stabilizer", + ] + ) _DEFAULT_CONFIGURATION = { - 'backend_name': 'qasm_simulator', - 'backend_version': __version__, - 'n_qubits': MAX_QUBITS_STATEVECTOR, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'local': True, - 'conditional': True, - 'open_pulse': False, - 'memory': True, - 'max_shots': int(1e6), - 'description': 'A C++ QasmQobj simulator with noise', - 'coupling_map': None, - 'basis_gates': _DEFAULT_BASIS_GATES, - 'custom_instructions': _DEFAULT_CUSTOM_INSTR, - 'gates': [] + "backend_name": "qasm_simulator", + "backend_version": __version__, + "n_qubits": MAX_QUBITS_STATEVECTOR, + "url": "https://github.com/Qiskit/qiskit-aer", + "simulator": True, + "local": True, + "conditional": True, + "open_pulse": False, + "memory": True, + "max_shots": int(1e6), + "description": "A C++ QasmQobj simulator with noise", + "coupling_map": None, + "basis_gates": _DEFAULT_BASIS_GATES, + "custom_instructions": _DEFAULT_CUSTOM_INSTR, + "gates": [], } _SIMULATION_METHODS = [ - 'automatic', 'statevector', 'statevector_gpu', 'statevector_custatevec', - 'statevector_thrust', 'density_matrix', - 'density_matrix_gpu', 'density_matrix_custatevec', 'density_matrix_thrust', - 'stabilizer', 'matrix_product_state', 'extended_stabilizer' + "automatic", + "statevector", + "statevector_gpu", + "statevector_custatevec", + "statevector_thrust", + "density_matrix", + "density_matrix_gpu", + "density_matrix_custatevec", + "density_matrix_thrust", + "stabilizer", + "matrix_product_state", + "extended_stabilizer", ] _AVAILABLE_METHODS = None - _SIMULATION_DEVICES = ('CPU', 'GPU', 'Thrust') + _SIMULATION_DEVICES = ("CPU", "GPU", "Thrust") _AVAILABLE_DEVICES = None - def __init__(self, - configuration=None, - properties=None, - provider=None, - **backend_options): - - warn('The `QasmSimulator` backend will be deprecated in the' - ' future. It has been superseded by the `AerSimulator`' - ' backend.', PendingDeprecationWarning) + def __init__(self, configuration=None, properties=None, provider=None, **backend_options): + warn( + "The `QasmSimulator` backend will be deprecated in the" + " future. It has been superseded by the `AerSimulator`" + " backend.", + PendingDeprecationWarning, + ) self._controller = aer_controller_execute() # Update available methods for class if QasmSimulator._AVAILABLE_METHODS is None: QasmSimulator._AVAILABLE_METHODS = available_methods( - self._controller, QasmSimulator._SIMULATION_METHODS, - QasmSimulator._SIMULATION_DEVICES) + self._controller, + QasmSimulator._SIMULATION_METHODS, + QasmSimulator._SIMULATION_DEVICES, + ) # Default configuration if configuration is None: - configuration = QasmBackendConfiguration.from_dict( - QasmSimulator._DEFAULT_CONFIGURATION) + configuration = QasmBackendConfiguration.from_dict(QasmSimulator._DEFAULT_CONFIGURATION) else: configuration.open_pulse = False @@ -383,23 +458,22 @@ def __init__(self, # of noise model, method, and config gates is expensive. self._cached_basis_gates = self._DEFAULT_BASIS_GATES - super().__init__(configuration, - properties=properties, - provider=provider, - backend_options=backend_options) + super().__init__( + configuration, properties=properties, provider=provider, backend_options=backend_options + ) def __repr__(self): """String representation of an AerBackend.""" display = super().__repr__()[:-1] - pad = ' ' * (len(self.__class__.__name__) + 1) + pad = " " * (len(self.__class__.__name__) + 1) - method = getattr(self.options, 'method', None) - if method not in [None, 'automatic']: + method = getattr(self.options, "method", None) + if method not in [None, "automatic"]: display += f",\n{pad}method='{method}'" - noise_model = getattr(self.options, 'noise_model', None) + noise_model = getattr(self.options, "noise_model", None) if noise_model is not None and not noise_model.is_ideal(): - display += f',\n{pad}noise_model={repr(noise_model)})' + display += f",\n{pad}noise_model={repr(noise_model)})" display += ")" return display @@ -438,7 +512,7 @@ def _default_options(cls): # stabilizer options stabilizer_max_snapshot_probabilities=32, # extended stabilizer options - extended_stabilizer_sampling_method='resampled_metropolis', + extended_stabilizer_sampling_method="resampled_metropolis", extended_stabilizer_metropolis_mixing_time=5000, extended_stabilizer_approximation_error=0.05, extended_stabilizer_norm_estimation_samples=100, @@ -448,19 +522,18 @@ def _default_options(cls): # MPS options matrix_product_state_truncation_threshold=1e-16, matrix_product_state_max_bond_dimension=None, - mps_sample_measure_algorithm='mps_heuristic', + mps_sample_measure_algorithm="mps_heuristic", mps_log_data=False, chop_threshold=1e-8, mps_parallel_threshold=14, - mps_omp_threads=1) + mps_omp_threads=1, + ) @classmethod def from_backend(cls, backend, **options): """Initialize simulator from backend.""" if isinstance(backend, BackendV2): - raise AerError( - "QasmSimulator.from_backend does not currently support V2 Backends." - ) + raise AerError("QasmSimulator.from_backend does not currently support V2 Backends.") # pylint: disable=import-outside-toplevel # Avoid cyclic import from ..noise.noise_model import NoiseModel @@ -471,18 +544,16 @@ def from_backend(cls, backend, **options): # Customize configuration name name = configuration.backend_name - configuration.backend_name = f'qasm_simulator({name})' + configuration.backend_name = f"qasm_simulator({name})" # Use automatic noise model if none is provided - if 'noise_model' not in options: + if "noise_model" not in options: noise_model = NoiseModel.from_backend(backend) if not noise_model.is_ideal(): - options['noise_model'] = noise_model + options["noise_model"] = noise_model # Initialize simulator - sim = cls(configuration=configuration, - properties=properties, - **options) + sim = cls(configuration=configuration, properties=properties, **options) return sim def configuration(self): @@ -521,8 +592,7 @@ def _execute_qobj(self, qobj): return cpp_execute_qobj(self._controller, qobj) def _execute_circuits(self, aer_circuits, noise_model, config): - """Execute circuits on the backend. - """ + """Execute circuits on the backend.""" config = map_legacy_method_config(config) return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) @@ -534,10 +604,11 @@ def set_option(self, key, value): if value in LEGACY_METHOD_MAP: value, device = LEGACY_METHOD_MAP[value] self.set_option("device", device) - if (value is not None and value not in self.available_methods()): + if value is not None and value not in self.available_methods(): raise AerError( f"Invalid simulation method {value}. Available methods" - f" are: {self.available_methods()}") + f" are: {self.available_methods()}" + ) self._set_method_config(value) super().set_option(key, value) if key in ["method", "noise_model", "basis_gates"]: @@ -562,9 +633,9 @@ def _validate(self, qobj): # Print warning if clbits but no measure if no_measure: logger.warning( - 'No measurements in circuit "%s": ' - 'count data will return all zeros.', - experiment.header.name) + 'No measurements in circuit "%s": ' "count data will return all zeros.", + experiment.header.name, + ) def _basis_gates(self): """Return simualtor basis gates. @@ -574,20 +645,19 @@ def _basis_gates(self): and method supported basis gates. """ # Use option value for basis gates if set - if 'basis_gates' in self._options_configuration: - return self._options_configuration['basis_gates'] + if "basis_gates" in self._options_configuration: + return self._options_configuration["basis_gates"] # Compute intersection with method basis gates method_gates = self._method_basis_gates() config_gates = self._configuration.basis_gates if config_gates: - basis_gates = set(config_gates).intersection( - method_gates) + basis_gates = set(config_gates).intersection(method_gates) else: basis_gates = method_gates # Compute intersection with noise model basis gates - noise_model = getattr(self.options, 'noise_model', None) + noise_model = getattr(self.options, "noise_model", None) if noise_model: noise_gates = noise_model.basis_gates basis_gates = basis_gates.intersection(noise_gates) @@ -599,106 +669,279 @@ def _basis_gates(self): "The intersection of configuration basis gates (%s), " "simulation method basis gates (%s), and " "noise model basis gates (%s) is empty", - config_gates, method_gates, noise_gates) + config_gates, + method_gates, + noise_gates, + ) return sorted(basis_gates) def _method_basis_gates(self): """Return method basis gates and custom instructions""" - method = self._options.get('method', None) - if method in ['density_matrix', 'density_matrix_gpu', - 'density_matrix_custatevec', 'density_matrix_thrust']: - return sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'cp', 'cu1', 'rxx', 'ryy', 'rzz', 'rzx', 'ccx', - 'unitary', 'diagonal', 'delay', 'pauli', 'ecr' - ]) - if method == 'matrix_product_state': - return sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'cp', 'cx', 'cy', 'cz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'ccx', - 'unitary', 'roerror', 'delay', 'pauli', 'r', 'rx', 'ry', 'rz', 'rxx', - 'ryy', 'rzz', 'rzx', 'csx', 'cswap', 'diagonal', 'initialize' - ]) - if method == 'stabilizer': - return sorted([ - 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 'cx', 'cy', 'cz', - 'swap', 'delay', 'pauli' - ]) - if method == 'extended_stabilizer': - return sorted([ - 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', - 'swap', 'u0', 't', 'tdg', 'u1', 'p', 'ccx', 'ccz', 'delay', 'pauli' - ]) + method = self._options.get("method", None) + if method in [ + "density_matrix", + "density_matrix_gpu", + "density_matrix_custatevec", + "density_matrix_thrust", + ]: + return sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "cp", + "cu1", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "unitary", + "diagonal", + "delay", + "pauli", + "ecr", + ] + ) + if method == "matrix_product_state": + return sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "cp", + "cx", + "cy", + "cz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "ccx", + "unitary", + "roerror", + "delay", + "pauli", + "r", + "rx", + "ry", + "rz", + "rxx", + "ryy", + "rzz", + "rzx", + "csx", + "cswap", + "diagonal", + "initialize", + ] + ) + if method == "stabilizer": + return sorted( + [ + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "cx", + "cy", + "cz", + "swap", + "delay", + "pauli", + ] + ) + if method == "extended_stabilizer": + return sorted( + [ + "cx", + "cz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "swap", + "u0", + "t", + "tdg", + "u1", + "p", + "ccx", + "ccz", + "delay", + "pauli", + ] + ) return QasmSimulator._DEFAULT_BASIS_GATES def _custom_instructions(self): """Return method basis gates and custom instructions""" # pylint: disable = too-many-return-statements - if 'custom_instructions' in self._options_configuration: - return self._options_configuration['custom_instructions'] - - method = self._options.get('method', None) - if method in ['statevector', 'statevector_gpu', - 'statevector_custatevec', 'statevector_thrust']: - return sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', 'save_expval', - 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes', 'save_amplitudes_sq', 'save_state', - 'save_density_matrix', 'save_statevector', 'save_statevector_dict', - 'set_statevector' - ]) - if method in ['density_matrix', 'density_matrix_gpu', - 'density_matrix_custatevec', 'density_matrix_thrust']: - return sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', 'superop', - 'save_expval', 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_state', 'save_density_matrix', 'save_amplitudes_sq', - 'set_statevector', 'set_density_matrix' - ]) - if method == 'matrix_product_state': - return sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'kraus', 'save_expval', - 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_density_matrix', 'save_state', 'save_statevector', - 'save_amplitudes', 'save_amplitudes_sq', 'save_matrix_product_state', - 'set_matrix_product_state']) - if method == 'stabilizer': - return sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', 'save_expval', - 'save_expval_var', 'save_probabilities', 'save_probabilities_dict', - 'save_amplitudes_sq', 'save_state', 'save_stabilizer', - 'set_stabilizer' - ]) - if method == 'extended_stabilizer': - return sorted([ - 'quantum_channel', 'qerror_loc', 'roerror', - 'save_statevector']) + if "custom_instructions" in self._options_configuration: + return self._options_configuration["custom_instructions"] + + method = self._options.get("method", None) + if method in [ + "statevector", + "statevector_gpu", + "statevector_custatevec", + "statevector_thrust", + ]: + return sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_amplitudes", + "save_amplitudes_sq", + "save_state", + "save_density_matrix", + "save_statevector", + "save_statevector_dict", + "set_statevector", + ] + ) + if method in [ + "density_matrix", + "density_matrix_gpu", + "density_matrix_custatevec", + "density_matrix_thrust", + ]: + return sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "superop", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_state", + "save_density_matrix", + "save_amplitudes_sq", + "set_statevector", + "set_density_matrix", + ] + ) + if method == "matrix_product_state": + return sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "kraus", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_density_matrix", + "save_state", + "save_statevector", + "save_amplitudes", + "save_amplitudes_sq", + "save_matrix_product_state", + "set_matrix_product_state", + ] + ) + if method == "stabilizer": + return sorted( + [ + "quantum_channel", + "qerror_loc", + "roerror", + "save_expval", + "save_expval_var", + "save_probabilities", + "save_probabilities_dict", + "save_amplitudes_sq", + "save_state", + "save_stabilizer", + "set_stabilizer", + ] + ) + if method == "extended_stabilizer": + return sorted(["quantum_channel", "qerror_loc", "roerror", "save_statevector"]) return QasmSimulator._DEFAULT_CUSTOM_INSTR def _set_method_config(self, method=None): """Set non-basis gate options when setting method""" # Update configuration description and number of qubits - if method in ['statevector', 'statevector_gpu', - 'statevector_custatevec', 'statevector_thrust']: - description = 'A C++ statevector simulator with noise' + if method in [ + "statevector", + "statevector_gpu", + "statevector_custatevec", + "statevector_thrust", + ]: + description = "A C++ statevector simulator with noise" n_qubits = MAX_QUBITS_STATEVECTOR - elif method in ['density_matrix', 'density_matrix_gpu', - 'density_matrix_custatevec', 'density_matrix_thrust']: - description = 'A C++ density matrix simulator with noise' + elif method in [ + "density_matrix", + "density_matrix_gpu", + "density_matrix_custatevec", + "density_matrix_thrust", + ]: + description = "A C++ density matrix simulator with noise" n_qubits = MAX_QUBITS_STATEVECTOR // 2 - elif method == 'matrix_product_state': - description = 'A C++ matrix product state simulator with noise' + elif method == "matrix_product_state": + description = "A C++ matrix product state simulator with noise" n_qubits = 63 # TODO: not sure what to put here? - elif method == 'stabilizer': - description = 'A C++ Clifford stabilizer simulator with noise' + elif method == "stabilizer": + description = "A C++ Clifford stabilizer simulator with noise" n_qubits = 10000 # TODO: estimate from memory - elif method == 'extended_stabilizer': - description = 'A C++ Clifford+T extended stabilizer simulator with noise' + elif method == "extended_stabilizer": + description = "A C++ Clifford+T extended stabilizer simulator with noise" n_qubits = 63 # TODO: estimate from memory else: # Clear options to default description = None n_qubits = None - self._set_configuration_option('description', description) - self._set_configuration_option('n_qubits', n_qubits) + self._set_configuration_option("description", description) + self._set_configuration_option("n_qubits", n_qubits) diff --git a/qiskit_aer/backends/statevector_simulator.py b/qiskit_aer/backends/statevector_simulator.py index 93564bb723..e7affcf1c7 100644 --- a/qiskit_aer/backends/statevector_simulator.py +++ b/qiskit_aer/backends/statevector_simulator.py @@ -23,15 +23,18 @@ from ..aererror import AerError from ..version import __version__ from .aerbackend import AerBackend -from .backend_utils import (cpp_execute_qobj, available_devices, - MAX_QUBITS_STATEVECTOR, - LEGACY_METHOD_MAP, - add_final_save_instruction, - cpp_execute_circuits, - map_legacy_method_options, - map_legacy_method_config, - add_final_save_op, - ) +from .backend_utils import ( + cpp_execute_qobj, + available_devices, + MAX_QUBITS_STATEVECTOR, + LEGACY_METHOD_MAP, + add_final_save_instruction, + cpp_execute_circuits, + map_legacy_method_options, + map_legacy_method_config, + add_final_save_op, +) + # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -135,73 +138,134 @@ class StatevectorSimulator(AerBackend): """ _DEFAULT_CONFIGURATION = { - 'backend_name': 'statevector_simulator', - 'backend_version': __version__, - 'n_qubits': MAX_QUBITS_STATEVECTOR, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'local': True, - 'conditional': True, - 'open_pulse': False, - 'memory': True, - 'max_shots': int(1e6), # Note that this backend will only ever + "backend_name": "statevector_simulator", + "backend_version": __version__, + "n_qubits": MAX_QUBITS_STATEVECTOR, + "url": "https://github.com/Qiskit/qiskit-aer", + "simulator": True, + "local": True, + "conditional": True, + "open_pulse": False, + "memory": True, + "max_shots": int(1e6), # Note that this backend will only ever # perform a single shot. This value is just # so that the default shot value for execute # will not raise an error when trying to run # a simulation - 'description': 'A C++ statevector circuit simulator', - 'coupling_map': None, - 'basis_gates': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cu', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcu', 'mcp', 'mcphase', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', - 'initialize', 'delay', 'pauli' - ]), - 'custom_instructions': sorted([ - 'kraus', 'roerror', 'quantum_channel', 'qerror_loc', - 'save_expval', 'save_density_matrix', 'save_statevector', - 'save_probs', 'save_probs_ket', 'save_amplitudes', - 'save_amplitudes_sq', 'save_state', 'set_statevector' - ]), - 'gates': [] + "description": "A C++ statevector circuit simulator", + "coupling_map": None, + "basis_gates": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cu", + "cp", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcu", + "mcp", + "mcphase", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "initialize", + "delay", + "pauli", + ] + ), + "custom_instructions": sorted( + [ + "kraus", + "roerror", + "quantum_channel", + "qerror_loc", + "save_expval", + "save_density_matrix", + "save_statevector", + "save_probs", + "save_probs_ket", + "save_amplitudes", + "save_amplitudes_sq", + "save_state", + "set_statevector", + ] + ), + "gates": [], } - _SIMULATION_DEVICES = ('CPU', 'GPU', 'Thrust') + _SIMULATION_DEVICES = ("CPU", "GPU", "Thrust") _AVAILABLE_DEVICES = None - def __init__(self, - configuration=None, - properties=None, - provider=None, - **backend_options): - - warn('The `StatevectorSimulator` backend will be deprecated in the' - ' future. It has been superseded by the `AerSimulator`' - ' backend. To obtain legacy functionality initialize with' - ' `AerSimulator(method="statevector")` and append run circuits' - ' with the `save_state` instruction.', PendingDeprecationWarning) + def __init__(self, configuration=None, properties=None, provider=None, **backend_options): + warn( + "The `StatevectorSimulator` backend will be deprecated in the" + " future. It has been superseded by the `AerSimulator`" + " backend. To obtain legacy functionality initialize with" + ' `AerSimulator(method="statevector")` and append run circuits' + " with the `save_state` instruction.", + PendingDeprecationWarning, + ) self._controller = aer_controller_execute() if StatevectorSimulator._AVAILABLE_DEVICES is None: StatevectorSimulator._AVAILABLE_DEVICES = available_devices( - self._controller, StatevectorSimulator._SIMULATION_DEVICES) + self._controller, StatevectorSimulator._SIMULATION_DEVICES + ) if configuration is None: configuration = QasmBackendConfiguration.from_dict( - StatevectorSimulator._DEFAULT_CONFIGURATION) + StatevectorSimulator._DEFAULT_CONFIGURATION + ) else: configuration.open_pulse = False super().__init__( - configuration, - properties=properties, - provider=provider, - backend_options=backend_options) + configuration, properties=properties, provider=provider, backend_options=backend_options + ) @classmethod def _default_options(cls): @@ -225,31 +289,37 @@ def _default_options(cls): fusion_max_qubit=5, fusion_threshold=14, # statevector options - statevector_parallel_threshold=14) + statevector_parallel_threshold=14, + ) def set_option(self, key, value): if key == "method": # Handle deprecation of method option for device option - warn("The method option of the `StatevectorSimulator` has been" - " deprecated as of qiskit-aer 0.9.0. To run a GPU statevector" - " simulation use the option `device='GPU'` instead", - DeprecationWarning) + warn( + "The method option of the `StatevectorSimulator` has been" + " deprecated as of qiskit-aer 0.9.0. To run a GPU statevector" + " simulation use the option `device='GPU'` instead", + DeprecationWarning, + ) if value in LEGACY_METHOD_MAP: value, device = LEGACY_METHOD_MAP[value] self.set_option("device", device) if value != "statevector": raise AerError( - "only the 'statevector' method is supported for the StatevectorSimulator") + "only the 'statevector' method is supported for the StatevectorSimulator" + ) return super().set_option(key, value) def available_methods(self): """Return the available simulation methods.""" - warn("The `available_methods` method of the StatevectorSimulator" - " is deprecated as of qiskit-aer 0.9.0 as this simulator only" - " supports a single method. To check if GPU simulation is available" - " use the `available_devices` method instead.", - DeprecationWarning) + warn( + "The `available_methods` method of the StatevectorSimulator" + " is deprecated as of qiskit-aer 0.9.0 as this simulator only" + " supports a single method. To check if GPU simulation is available" + " use the `available_devices` method instead.", + DeprecationWarning, + ) return ("statevector",) def available_devices(self): @@ -272,8 +342,7 @@ def _execute_qobj(self, qobj): return cpp_execute_qobj(self._controller, qobj) def _execute_circuits(self, aer_circuits, noise_model, config): - """Execute circuits on the backend. - """ + """Execute circuits on the backend.""" config = map_legacy_method_config(config) aer_circuits = add_final_save_op(aer_circuits, "statevector") return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) @@ -286,15 +355,16 @@ def _validate(self, qobj): 2. Check number of qubits will fit in local memory. """ name = self.name() - if getattr(qobj.config, 'noise_model', None) is not None: + if getattr(qobj.config, "noise_model", None) is not None: raise AerError(f"{name} does not support noise.") n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError( - f'Number of qubits ({n_qubits}) is greater than max ({max_qubits}) ' - f'for "{name}" with {int(local_hardware_info()["memory"])} GB system memory.') + f"Number of qubits ({n_qubits}) is greater than max ({max_qubits}) " + f'for "{name}" with {int(local_hardware_info()["memory"])} GB system memory.' + ) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) @@ -302,8 +372,10 @@ def _validate(self, qobj): for experiment in qobj.experiments: exp_name = experiment.header.name - if getattr(experiment.config, 'shots', 1) != 1: + if getattr(experiment.config, "shots", 1) != 1: logger.info( - '"%s" only supports 1 shot. ' - 'Setting shots=1 for circuit "%s".', name, exp_name) + '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', + name, + exp_name, + ) experiment.config.shots = 1 diff --git a/qiskit_aer/backends/unitary_simulator.py b/qiskit_aer/backends/unitary_simulator.py index 7971391f3d..190a3eb508 100644 --- a/qiskit_aer/backends/unitary_simulator.py +++ b/qiskit_aer/backends/unitary_simulator.py @@ -24,15 +24,18 @@ from ..aererror import AerError from ..version import __version__ from .aerbackend import AerBackend -from .backend_utils import (cpp_execute_qobj, - cpp_execute_circuits, - available_devices, - MAX_QUBITS_STATEVECTOR, - LEGACY_METHOD_MAP, - add_final_save_instruction, - map_legacy_method_options, - add_final_save_op, - map_legacy_method_config) +from .backend_utils import ( + cpp_execute_qobj, + cpp_execute_circuits, + available_devices, + MAX_QUBITS_STATEVECTOR, + LEGACY_METHOD_MAP, + add_final_save_instruction, + map_legacy_method_options, + add_final_save_op, + map_legacy_method_config, +) + # pylint: disable=import-error, no-name-in-module from .controller_wrappers import aer_controller_execute @@ -139,66 +142,117 @@ class UnitarySimulator(AerBackend): """ _DEFAULT_CONFIGURATION = { - 'backend_name': 'unitary_simulator', - 'backend_version': __version__, - 'n_qubits': MAX_QUBITS_STATEVECTOR // 2, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'local': True, - 'conditional': False, - 'open_pulse': False, - 'memory': False, - 'max_shots': int(1e6), # Note that this backend will only ever - # perform a single shot. This value is just - # so that the default shot value for execute - # will not raise an error when trying to run - # a simulation - 'description': 'A C++ unitary circuit simulator', - 'coupling_map': None, - 'basis_gates': sorted([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg', 'swap', 'cx', - 'cy', 'cz', 'csx', 'cu', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', - 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', - 'mcu', 'mcp', 'mcphase', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', - 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'delay', 'pauli', - ]), - 'custom_instructions': sorted(['save_unitary', 'save_state', 'set_unitary']), - 'gates': [] + "backend_name": "unitary_simulator", + "backend_version": __version__, + "n_qubits": MAX_QUBITS_STATEVECTOR // 2, + "url": "https://github.com/Qiskit/qiskit-aer", + "simulator": True, + "local": True, + "conditional": False, + "open_pulse": False, + "memory": False, + "max_shots": int(1e6), # Note that this backend will only ever + # perform a single shot. This value is just + # so that the default shot value for execute + # will not raise an error when trying to run + # a simulation + "description": "A C++ unitary circuit simulator", + "coupling_map": None, + "basis_gates": sorted( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + "swap", + "cx", + "cy", + "cz", + "csx", + "cu", + "cp", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ccx", + "cswap", + "mcx", + "mcy", + "mcz", + "mcsx", + "mcu", + "mcp", + "mcphase", + "mcu1", + "mcu2", + "mcu3", + "mcrx", + "mcry", + "mcrz", + "mcr", + "mcswap", + "unitary", + "diagonal", + "multiplexer", + "delay", + "pauli", + ] + ), + "custom_instructions": sorted(["save_unitary", "save_state", "set_unitary"]), + "gates": [], } - _SIMULATION_DEVICES = ('CPU', 'GPU', 'Thrust') + _SIMULATION_DEVICES = ("CPU", "GPU", "Thrust") _AVAILABLE_DEVICES = None - def __init__(self, - configuration=None, - properties=None, - provider=None, - **backend_options): - - warn('The `UnitarySimulator` backend will be deprecated in the' - ' future. It has been superseded by the `AerSimulator`' - ' backend. To obtain legacy functionality initialize with' - ' `AerSimulator(method="unitary")` and append run circuits' - ' with the `save_state` instruction.', PendingDeprecationWarning) + def __init__(self, configuration=None, properties=None, provider=None, **backend_options): + warn( + "The `UnitarySimulator` backend will be deprecated in the" + " future. It has been superseded by the `AerSimulator`" + " backend. To obtain legacy functionality initialize with" + ' `AerSimulator(method="unitary")` and append run circuits' + " with the `save_state` instruction.", + PendingDeprecationWarning, + ) self._controller = aer_controller_execute() if UnitarySimulator._AVAILABLE_DEVICES is None: UnitarySimulator._AVAILABLE_DEVICES = available_devices( - self._controller, UnitarySimulator._SIMULATION_DEVICES) + self._controller, UnitarySimulator._SIMULATION_DEVICES + ) if configuration is None: configuration = QasmBackendConfiguration.from_dict( - UnitarySimulator._DEFAULT_CONFIGURATION) + UnitarySimulator._DEFAULT_CONFIGURATION + ) else: configuration.open_pulse = False - super().__init__(configuration, - properties=properties, - provider=provider, - backend_options=backend_options) + super().__init__( + configuration, properties=properties, provider=provider, backend_options=backend_options + ) @classmethod def _default_options(cls): @@ -224,31 +278,35 @@ def _default_options(cls): blocking_qubits=None, blocking_enable=False, # statevector options - statevector_parallel_threshold=14) + statevector_parallel_threshold=14, + ) def set_option(self, key, value): if key == "method": # Handle deprecation of method option for device option - warn("The method option of the `UnitarySimulator` has been" - " deprecated as of qiskit-aer 0.9.0. To run a GPU statevector" - " simulation use the option `device='GPU'` instead", - DeprecationWarning) + warn( + "The method option of the `UnitarySimulator` has been" + " deprecated as of qiskit-aer 0.9.0. To run a GPU statevector" + " simulation use the option `device='GPU'` instead", + DeprecationWarning, + ) if value in LEGACY_METHOD_MAP: value, device = LEGACY_METHOD_MAP[value] self.set_option("device", device) if value != "unitary": - raise AerError( - "only the 'unitary' method is supported for the UnitarySimulator") + raise AerError("only the 'unitary' method is supported for the UnitarySimulator") return super().set_option(key, value) def available_methods(self): """Return the available simulation methods.""" - warn("The `available_methods` method of the UnitarySimulator" - " is deprecated as of qiskit-aer 0.9.0 as this simulator only" - " supports a single method. To check if GPU simulation is available" - " use the `available_devices` method instead.", - DeprecationWarning) + warn( + "The `available_methods` method of the UnitarySimulator" + " is deprecated as of qiskit-aer 0.9.0 as this simulator only" + " supports a single method. To check if GPU simulation is available" + " use the `available_devices` method instead.", + DeprecationWarning, + ) return ("unitary",) def available_devices(self): @@ -271,8 +329,7 @@ def _execute_qobj(self, qobj): return cpp_execute_qobj(self._controller, qobj) def _execute_circuits(self, aer_circuits, noise_model, config): - """Execute circuits on the backend. - """ + """Execute circuits on the backend.""" config = map_legacy_method_config(config) aer_circuits = add_final_save_op(aer_circuits, "unitary") return cpp_execute_circuits(self._controller, aer_circuits, noise_model, config) @@ -285,27 +342,31 @@ def _validate(self, qobj): 3. Check number of qubits will fit in local memory. """ name = self.name() - if getattr(qobj.config, 'noise_model', None) is not None: + if getattr(qobj.config, "noise_model", None) is not None: raise AerError(f"{name} does not support noise.") n_qubits = qobj.config.n_qubits max_qubits = self.configuration().n_qubits if n_qubits > max_qubits: raise AerError( - f'Number of qubits ({n_qubits}) is greater than ' + f"Number of qubits ({n_qubits}) is greater than " f'max ({max_qubits}) for "{name}" with ' - f"{int(local_hardware_info()['memory'])} GB system memory.") + f"{int(local_hardware_info()['memory'])} GB system memory." + ) if qobj.config.shots != 1: 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: + if getattr(experiment.config, "shots", 1) != 1: logger.info( - '"%s" only supports 1 shot. ' - 'Setting shots=1 for circuit "%s".', name, exp_name) + '"%s" only supports 1 shot. ' 'Setting shots=1 for circuit "%s".', + name, + exp_name, + ) experiment.config.shots = 1 for operation in experiment.instructions: - if operation.name in ['measure', 'reset']: + if operation.name in ["measure", "reset"]: raise AerError( - f'Unsupported {name} instruction {operation.name} in circuit {exp_name}') + f"Unsupported {name} instruction {operation.name} in circuit {exp_name}" + ) diff --git a/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp b/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp index ffd7fe37a9..dbfd7a8ce4 100644 --- a/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp +++ b/qiskit_aer/backends/wrappers/aer_circuit_binding.hpp @@ -20,34 +20,33 @@ DISABLE_WARNING_PUSH #include DISABLE_WARNING_POP #if defined(_MSC_VER) - #undef snprintf +#undef snprintf #endif #include #include "framework/matrix.hpp" -#include "framework/python_parser.hpp" -#include "framework/pybind_json.hpp" #include "framework/pybind_casts.hpp" +#include "framework/pybind_json.hpp" +#include "framework/python_parser.hpp" -#include "framework/types.hpp" #include "framework/results/pybind_result.hpp" +#include "framework/types.hpp" #include "framework/circuit.hpp" namespace py = pybind11; using namespace AER; -template +template void bind_aer_circuit(MODULE m) { - py::class_ aer_circuit(m, "AerCircuit"); + py::class_> aer_circuit(m, "AerCircuit"); aer_circuit.def(py::init()); aer_circuit.def("__repr__", [](const Circuit &circ) { std::stringstream ss; ss << "Circuit(" - << "qubit=" << circ.num_qubits - << ", num_memory=" << circ.num_memory - << ", num_registers=" << circ.num_registers; + << "qubit=" << circ.num_qubits << ", num_memory=" << circ.num_memory + << ", num_registers=" << circ.num_registers; ss << ", ops={"; for (auto i = 0; i < circ.ops.size(); ++i) @@ -57,10 +56,8 @@ void bind_aer_circuit(MODULE m) { ss << "," << circ.ops[i]; ss << "}" - << ", shots=" << circ.shots - << ", seed=" << circ.seed - << ", global_phase_angle=" << circ.global_phase_angle - ; + << ", shots=" << circ.shots << ", seed=" << circ.seed + << ", global_phase_angle=" << circ.global_phase_angle; ss << ")"; return ss.str(); }); @@ -71,10 +68,10 @@ void bind_aer_circuit(MODULE m) { aer_circuit.def_readwrite("seed", &Circuit::seed); aer_circuit.def_readwrite("ops", &Circuit::ops); aer_circuit.def_readwrite("global_phase_angle", &Circuit::global_phase_angle); - aer_circuit.def("set_header", [aer_circuit](Circuit &circ, - const py::handle& header) { - circ.header = header; - }); + aer_circuit.def("set_header", + [aer_circuit](Circuit &circ, const py::handle &header) { + circ.header = header; + }); aer_circuit.def("bfunc", &Circuit::bfunc); aer_circuit.def("gate", &Circuit::gate); aer_circuit.def("diagonal", &Circuit::diagonal); @@ -88,17 +85,19 @@ void bind_aer_circuit(MODULE m) { aer_circuit.def("save_expval", &Circuit::save_expval); aer_circuit.def("initialize", &Circuit::initialize); aer_circuit.def("set_statevector", &Circuit::set_statevector); - aer_circuit.def("set_density_matrix", &Circuit::set_density_matrix); + aer_circuit.def("set_density_matrix", + &Circuit::set_density_matrix); aer_circuit.def("set_unitary", &Circuit::set_unitary); aer_circuit.def("set_superop", &Circuit::set_superop); - aer_circuit.def("set_matrix_product_state", &Circuit::set_matrix_product_state); + aer_circuit.def("set_matrix_product_state", + &Circuit::set_matrix_product_state); aer_circuit.def("set_clifford", &Circuit::set_clifford); aer_circuit.def("jump", &Circuit::jump); aer_circuit.def("mark", &Circuit::mark); + aer_circuit.def("barrier", &Circuit::barrier); aer_circuit.def("measure", &Circuit::measure); aer_circuit.def("reset", &Circuit::reset); aer_circuit.def("set_qerror_loc", &Circuit::set_qerror_loc); - } #endif \ No newline at end of file diff --git a/qiskit_aer/backends/wrappers/aer_controller_binding.hpp b/qiskit_aer/backends/wrappers/aer_controller_binding.hpp index a04a6a8430..bf5296b18a 100644 --- a/qiskit_aer/backends/wrappers/aer_controller_binding.hpp +++ b/qiskit_aer/backends/wrappers/aer_controller_binding.hpp @@ -20,16 +20,16 @@ DISABLE_WARNING_PUSH #include DISABLE_WARNING_POP #if defined(_MSC_VER) - #undef snprintf +#undef snprintf #endif #include #include "framework/matrix.hpp" -#include "framework/python_parser.hpp" #include "framework/pybind_casts.hpp" -#include "framework/types.hpp" +#include "framework/python_parser.hpp" #include "framework/results/pybind_result.hpp" +#include "framework/types.hpp" #include "controllers/aer_controller.hpp" @@ -38,421 +38,549 @@ DISABLE_WARNING_POP namespace py = pybind11; using namespace AER; -template +template class ControllerExecutor { public: - ControllerExecutor() = default; - py::object operator()(const py::handle &qobj) { + ControllerExecutor() = default; + py::object operator()(const py::handle &qobj) { #ifdef TEST_JSON // Convert input qobj to json to test standalone data reading - return AerToPy::to_python(controller_execute(json_t(qobj))); + return AerToPy::to_python(controller_execute(json_t(qobj))); #else - return AerToPy::to_python(controller_execute(qobj)); + return AerToPy::to_python(controller_execute(qobj)); #endif - } + } - py::object execute(std::vector &circuits, Noise::NoiseModel &noise_model, AER::Config& config) const { - return AerToPy::to_python(controller_execute(circuits, noise_model, config)); - } + py::object execute(std::vector> &circuits, + Noise::NoiseModel &noise_model, + AER::Config &config) const { + return AerToPy::to_python( + controller_execute(circuits, noise_model, config)); + } }; -template +template py::tuple write_value(size_t index, const optional &v) { - return py::make_tuple(v.has_value(), v.value()); + return py::make_tuple(v.has_value(), v.value()); } -template +template T write_value(size_t index, const T &v) { - return v; + return v; } -template +template void read_value(const py::tuple &t, size_t index, optional &v) { - if (t[index].cast()[0].cast()) - v.value(t[index].cast()[1].cast()); + if (t[index].cast()[0].cast()) + v.value(t[index].cast()[1].cast()); } -template +template void read_value(const py::tuple &t, size_t index, T &v) { - v = t[index].cast(); + v = t[index].cast(); } -template +template void bind_aer_controller(MODULE m) { - py::class_ > aer_ctrl (m, "aer_controller_execute"); - aer_ctrl.def(py::init<>()); - aer_ctrl.def("__call__", &ControllerExecutor::operator()); - aer_ctrl.def("__reduce__", [aer_ctrl](const ControllerExecutor &self) { - return py::make_tuple(aer_ctrl, py::tuple()); - }); - aer_ctrl.def("execute", [aer_ctrl](ControllerExecutor &self, - std::vector &circuits, - py::object noise_model, - AER::Config &config) { - - Noise::NoiseModel noise_model_native; - if (noise_model) - noise_model_native.load_from_json(noise_model); + py::class_> aer_ctrl(m, + "aer_controller_execute"); + aer_ctrl.def(py::init<>()); + aer_ctrl.def("__call__", &ControllerExecutor::operator()); + aer_ctrl.def("__reduce__", + [aer_ctrl](const ControllerExecutor &self) { + return py::make_tuple(aer_ctrl, py::tuple()); + }); + aer_ctrl.def("execute", + [aer_ctrl](ControllerExecutor &self, + std::vector> &circuits, + py::object noise_model, AER::Config &config) { + Noise::NoiseModel noise_model_native; + if (noise_model) + noise_model_native.load_from_json(noise_model); - return self.execute(circuits, noise_model_native, config); - }); + return self.execute(circuits, noise_model_native, config); + }); - py::class_ aer_config(m, "AerConfig"); - aer_config.def(py::init()); - aer_config.def_readwrite("shots", &Config::shots); - aer_config.def_readwrite("method", &Config::method); - aer_config.def_readwrite("device", &Config::device); - aer_config.def_readwrite("precision", &Config::precision); - aer_config.def_readwrite("enable_truncation", &Config::enable_truncation); - aer_config.def_readwrite("zero_threshold", &Config::zero_threshold); - aer_config.def_readwrite("validation_threshold", &Config::validation_threshold); - aer_config.def_property("max_parallel_threads", - [](const Config &config) { return config.max_parallel_threads.val;}, - [](Config &config, uint_t val) { config.max_parallel_threads.value(val);}); - aer_config.def_property("max_parallel_experiments", - [](const Config &config) { return config.max_parallel_experiments.val;}, - [](Config &config, uint_t val) { config.max_parallel_experiments.value(val);}); - aer_config.def_property("max_parallel_shots", - [](const Config &config) { return config.max_parallel_shots.val;}, - [](Config &config, uint_t val) { config.max_parallel_shots.value(val);}); - aer_config.def_property("max_memory_mb", - [](const Config &config) { return config.max_memory_mb.val;}, - [](Config &config, uint_t val) { config.max_memory_mb.value(val);}); - aer_config.def_readwrite("fusion_enable", &Config::fusion_enable); - aer_config.def_readwrite("fusion_verbose", &Config::fusion_verbose); - aer_config.def_property("fusion_max_qubit", - [](const Config &config) { return config.fusion_max_qubit.val;}, - [](Config &config, uint_t val) { config.fusion_max_qubit.value(val);}); - aer_config.def_property("fusion_threshold", - [](const Config &config) { return config.fusion_threshold.val;}, - [](Config &config, uint_t val) { config.fusion_threshold.value(val);}); - aer_config.def_property("accept_distributed_results", - [](const Config &config) { return config.accept_distributed_results.val;}, - [](Config &config, bool val) { config.accept_distributed_results.value(val);}); - aer_config.def_property("memory", - [](const Config &config) { return config.memory.val;}, - [](Config &config, bool val) { config.memory.value(val);}); - aer_config.def_property("seed_simulator", - [](const Config &config) { return config.seed_simulator.val;}, - [](Config &config, int_t val) { config.seed_simulator.value(val);}); - // # cuStateVec (cuQuantum) option - aer_config.def_property("cuStateVec_enable", - [](const Config &config) { return config.cuStateVec_enable.val;}, - [](Config &config, bool val) { config.cuStateVec_enable.value(val);}); - // # cache blocking for multi-GPUs/MPI options - aer_config.def_property("blocking_qubits", - [](const Config &config) { return config.blocking_qubits.val;}, - [](Config &config, uint_t val) { config.blocking_qubits.value(val);}); - aer_config.def_readwrite("blocking_enable", &Config::blocking_enable); - aer_config.def_property("chunk_swap_buffer_qubits", - [](const Config &config) { return config.chunk_swap_buffer_qubits.val;}, - [](Config &config, uint_t val) { config.chunk_swap_buffer_qubits.value(val);}); - // # multi-shots optimization options (GPU only) - aer_config.def_readwrite("batched_shots_gpu", &Config::batched_shots_gpu); - aer_config.def_readwrite("batched_shots_gpu_max_qubits", &Config::batched_shots_gpu_max_qubits); - aer_config.def_property("num_threads_per_device", - [](const Config &config) { return config.num_threads_per_device.val;}, - [](Config &config, uint_t val) { config.num_threads_per_device.value(val);}); - // # statevector options - aer_config.def_readwrite("statevector_parallel_threshold", &Config::statevector_parallel_threshold); - aer_config.def_readwrite("statevector_sample_measure_opt", &Config::statevector_sample_measure_opt); - // # stabilizer options - aer_config.def_readwrite("stabilizer_max_snapshot_probabilities", &Config::stabilizer_max_snapshot_probabilities); - // # extended stabilizer options - aer_config.def_readwrite("extended_stabilizer_sampling_method", &Config::extended_stabilizer_sampling_method); - aer_config.def_readwrite("extended_stabilizer_metropolis_mixing_time", &Config::extended_stabilizer_metropolis_mixing_time); - aer_config.def_readwrite("extended_stabilizer_approximation_error", &Config::extended_stabilizer_approximation_error); - aer_config.def_readwrite("extended_stabilizer_norm_estimation_samples", &Config::extended_stabilizer_norm_estimation_samples); - aer_config.def_readwrite("extended_stabilizer_norm_estimation_repetitions", &Config::extended_stabilizer_norm_estimation_repetitions); - aer_config.def_readwrite("extended_stabilizer_parallel_threshold", &Config::extended_stabilizer_parallel_threshold); - aer_config.def_readwrite("extended_stabilizer_probabilities_snapshot_samples", &Config::extended_stabilizer_probabilities_snapshot_samples); - // # MPS options - aer_config.def_readwrite("matrix_product_state_truncation_threshold", &Config::matrix_product_state_truncation_threshold); - aer_config.def_property("matrix_product_state_max_bond_dimension", - [](const Config &config) { return config.matrix_product_state_max_bond_dimension.val;}, - [](Config &config, uint_t val) { config.matrix_product_state_max_bond_dimension.value(val);}); - aer_config.def_readwrite("mps_sample_measure_algorithm", &Config::mps_sample_measure_algorithm); - aer_config.def_readwrite("mps_log_data", &Config::mps_log_data); - aer_config.def_readwrite("mps_swap_direction", &Config::mps_swap_direction); - aer_config.def_readwrite("chop_threshold", &Config::chop_threshold); - aer_config.def_readwrite("mps_parallel_threshold", &Config::mps_parallel_threshold); - aer_config.def_readwrite("mps_omp_threads", &Config::mps_omp_threads); - // # tensor network options - aer_config.def_readwrite("tensor_network_num_sampling_qubits", &Config::tensor_network_num_sampling_qubits); - aer_config.def_readwrite("use_cuTensorNet_autotuning", &Config::use_cuTensorNet_autotuning); + py::class_ aer_config(m, "AerConfig"); + aer_config.def(py::init()); + aer_config.def_readwrite("shots", &Config::shots); + aer_config.def_readwrite("method", &Config::method); + aer_config.def_readwrite("device", &Config::device); + aer_config.def_readwrite("precision", &Config::precision); + aer_config.def_readwrite("enable_truncation", &Config::enable_truncation); + aer_config.def_readwrite("zero_threshold", &Config::zero_threshold); + aer_config.def_readwrite("validation_threshold", + &Config::validation_threshold); + aer_config.def_property( + "max_parallel_threads", + [](const Config &config) { return config.max_parallel_threads.val; }, + [](Config &config, uint_t val) { + config.max_parallel_threads.value(val); + }); + aer_config.def_property( + "max_parallel_experiments", + [](const Config &config) { return config.max_parallel_experiments.val; }, + [](Config &config, uint_t val) { + config.max_parallel_experiments.value(val); + }); + aer_config.def_property( + "max_parallel_shots", + [](const Config &config) { return config.max_parallel_shots.val; }, + [](Config &config, uint_t val) { config.max_parallel_shots.value(val); }); + aer_config.def_property( + "max_memory_mb", + [](const Config &config) { return config.max_memory_mb.val; }, + [](Config &config, uint_t val) { config.max_memory_mb.value(val); }); + aer_config.def_readwrite("fusion_enable", &Config::fusion_enable); + aer_config.def_readwrite("fusion_verbose", &Config::fusion_verbose); + aer_config.def_property( + "fusion_max_qubit", + [](const Config &config) { return config.fusion_max_qubit.val; }, + [](Config &config, uint_t val) { config.fusion_max_qubit.value(val); }); + aer_config.def_property( + "fusion_threshold", + [](const Config &config) { return config.fusion_threshold.val; }, + [](Config &config, uint_t val) { config.fusion_threshold.value(val); }); + aer_config.def_property( + "accept_distributed_results", + [](const Config &config) { + return config.accept_distributed_results.val; + }, + [](Config &config, bool val) { + config.accept_distributed_results.value(val); + }); + aer_config.def_property( + "memory", [](const Config &config) { return config.memory.val; }, + [](Config &config, bool val) { config.memory.value(val); }); + aer_config.def_property( + "seed_simulator", + [](const Config &config) { return config.seed_simulator.val; }, + [](Config &config, int_t val) { config.seed_simulator.value(val); }); + // # cuStateVec (cuQuantum) option + aer_config.def_property( + "cuStateVec_enable", + [](const Config &config) { return config.cuStateVec_enable.val; }, + [](Config &config, bool val) { config.cuStateVec_enable.value(val); }); + // # cache blocking for multi-GPUs/MPI options + aer_config.def_property( + "blocking_qubits", + [](const Config &config) { return config.blocking_qubits.val; }, + [](Config &config, uint_t val) { config.blocking_qubits.value(val); }); + aer_config.def_readwrite("blocking_enable", &Config::blocking_enable); + aer_config.def_property( + "chunk_swap_buffer_qubits", + [](const Config &config) { return config.chunk_swap_buffer_qubits.val; }, + [](Config &config, uint_t val) { + config.chunk_swap_buffer_qubits.value(val); + }); + // # multi-shots optimization options (GPU only) + aer_config.def_readwrite("batched_shots_gpu", &Config::batched_shots_gpu); + aer_config.def_readwrite("batched_shots_gpu_max_qubits", + &Config::batched_shots_gpu_max_qubits); + aer_config.def_property( + "num_threads_per_device", + [](const Config &config) { return config.num_threads_per_device.val; }, + [](Config &config, uint_t val) { + config.num_threads_per_device.value(val); + }); + // # statevector options + aer_config.def_readwrite("statevector_parallel_threshold", + &Config::statevector_parallel_threshold); + aer_config.def_readwrite("statevector_sample_measure_opt", + &Config::statevector_sample_measure_opt); + // # stabilizer options + aer_config.def_readwrite("stabilizer_max_snapshot_probabilities", + &Config::stabilizer_max_snapshot_probabilities); + // # extended stabilizer options + aer_config.def_readwrite("extended_stabilizer_sampling_method", + &Config::extended_stabilizer_sampling_method); + aer_config.def_readwrite("extended_stabilizer_metropolis_mixing_time", + &Config::extended_stabilizer_metropolis_mixing_time); + aer_config.def_readwrite("extended_stabilizer_approximation_error", + &Config::extended_stabilizer_approximation_error); + aer_config.def_readwrite( + "extended_stabilizer_norm_estimation_samples", + &Config::extended_stabilizer_norm_estimation_samples); + aer_config.def_readwrite( + "extended_stabilizer_norm_estimation_repetitions", + &Config::extended_stabilizer_norm_estimation_repetitions); + aer_config.def_readwrite("extended_stabilizer_parallel_threshold", + &Config::extended_stabilizer_parallel_threshold); + aer_config.def_readwrite( + "extended_stabilizer_probabilities_snapshot_samples", + &Config::extended_stabilizer_probabilities_snapshot_samples); + // # MPS options + aer_config.def_readwrite("matrix_product_state_truncation_threshold", + &Config::matrix_product_state_truncation_threshold); + aer_config.def_property( + "matrix_product_state_max_bond_dimension", + [](const Config &config) { + return config.matrix_product_state_max_bond_dimension.val; + }, + [](Config &config, uint_t val) { + config.matrix_product_state_max_bond_dimension.value(val); + }); + aer_config.def_readwrite("mps_sample_measure_algorithm", + &Config::mps_sample_measure_algorithm); + aer_config.def_readwrite("mps_log_data", &Config::mps_log_data); + aer_config.def_readwrite("mps_swap_direction", &Config::mps_swap_direction); + aer_config.def_readwrite("chop_threshold", &Config::chop_threshold); + aer_config.def_readwrite("mps_parallel_threshold", + &Config::mps_parallel_threshold); + aer_config.def_readwrite("mps_omp_threads", &Config::mps_omp_threads); + // # tensor network options + aer_config.def_readwrite("tensor_network_num_sampling_qubits", + &Config::tensor_network_num_sampling_qubits); + aer_config.def_readwrite("use_cuTensorNet_autotuning", + &Config::use_cuTensorNet_autotuning); - // system configurations - aer_config.def_readwrite("library_dir", &Config::library_dir); - aer_config.def_readwrite("parameterizations", &Config::param_table); - aer_config.def_property("n_qubits", - [](const Config &config) { return config.n_qubits.val;}, - [](Config &config, uint_t val) { config.n_qubits.value(val);}); - aer_config.def_readwrite("global_phase", &Config::global_phase); - aer_config.def_readwrite("memory_slots", &Config::memory_slots); - aer_config.def_property("_parallel_experiments", - [](const Config &config) { return config._parallel_experiments.val;}, - [](Config &config, uint_t val) { config._parallel_experiments.value(val);}); - aer_config.def_property("_parallel_shots", - [](const Config &config) { return config._parallel_shots.val;}, - [](Config &config, uint_t val) { config._parallel_shots.value(val);}); - aer_config.def_property("_parallel_state_update", - [](const Config &config) { return config._parallel_state_update.val;}, - [](Config &config, uint_t val) { config._parallel_state_update.value(val);}); - aer_config.def_property("fusion_allow_kraus", - [](const Config &config) { return config.fusion_allow_kraus.val;}, - [](Config &config, bool val) { config.fusion_allow_kraus.value(val);}); - aer_config.def_property("fusion_allow_superop", - [](const Config &config) { return config.fusion_allow_superop.val;}, - [](Config &config, bool val) { config.fusion_allow_superop.value(val);}); - aer_config.def_property("fusion_parallelization_threshold", - [](const Config &config) { return config.fusion_parallelization_threshold.val;}, - [](Config &config, uint_t val) { config.fusion_parallelization_threshold.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits", - [](const Config &config) { return config._fusion_enable_n_qubits.val;}, - [](Config &config, bool val) { config._fusion_enable_n_qubits.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits_1", - [](const Config &config) { return config._fusion_enable_n_qubits_1.val;}, - [](Config &config, uint_t val) { config._fusion_enable_n_qubits_1.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits_2", - [](const Config &config) { return config._fusion_enable_n_qubits_2.val;}, - [](Config &config, uint_t val) { config._fusion_enable_n_qubits_2.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits_3", - [](const Config &config) { return config._fusion_enable_n_qubits_3.val;}, - [](Config &config, uint_t val) { config._fusion_enable_n_qubits_3.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits_4", - [](const Config &config) { return config._fusion_enable_n_qubits_4.val;}, - [](Config &config, uint_t val) { config._fusion_enable_n_qubits_4.value(val);}); - aer_config.def_property("_fusion_enable_n_qubits_5", - [](const Config &config) { return config._fusion_enable_n_qubits_5.val;}, - [](Config &config, uint_t val) { config._fusion_enable_n_qubits_5.value(val);}); - aer_config.def_property("_fusion_enable_diagonal", - [](const Config &config) { return config._fusion_enable_diagonal.val;}, - [](Config &config, uint_t val) { config._fusion_enable_diagonal.value(val);}); - aer_config.def_property("_fusion_min_qubit", - [](const Config &config) { return config._fusion_min_qubit.val;}, - [](Config &config, uint_t val) { config._fusion_min_qubit.value(val);}); - aer_config.def_property("fusion_cost_factor", - [](const Config &config) { return config.fusion_cost_factor.val;}, - [](Config &config, double val) { config.fusion_cost_factor.value(val);}); - aer_config.def_property("_fusion_enable_cost_based", - [](const Config &config) { return config._fusion_enable_cost_based.val;}, - [](Config &config, bool val) { config._fusion_enable_cost_based.value(val);}); - aer_config.def_property("_fusion_cost_1", - [](const Config &config) { return config._fusion_cost_1.val;}, - [](Config &config, uint_t val) { config._fusion_cost_1.value(val);}); - aer_config.def_property("_fusion_cost_2", - [](const Config &config) { return config._fusion_cost_2.val;}, - [](Config &config, uint_t val) { config._fusion_cost_2.value(val);}); - aer_config.def_property("_fusion_cost_3", - [](const Config &config) { return config._fusion_cost_3.val;}, - [](Config &config, uint_t val) { config._fusion_cost_3.value(val);}); - aer_config.def_property("_fusion_cost_4", - [](const Config &config) { return config._fusion_cost_4.val;}, - [](Config &config, uint_t val) { config._fusion_cost_4.value(val);}); - aer_config.def_property("_fusion_cost_5", - [](const Config &config) { return config._fusion_cost_5.val;}, - [](Config &config, uint_t val) { config._fusion_cost_5.value(val);}); - aer_config.def_property("_fusion_cost_6", - [](const Config &config) { return config._fusion_cost_6.val;}, - [](Config &config, uint_t val) { config._fusion_cost_6.value(val);}); - aer_config.def_property("_fusion_cost_7", - [](const Config &config) { return config._fusion_cost_7.val;}, - [](Config &config, uint_t val) { config._fusion_cost_7.value(val);}); - aer_config.def_property("_fusion_cost_8", - [](const Config &config) { return config._fusion_cost_8.val;}, - [](Config &config, uint_t val) { config._fusion_cost_8.value(val);}); - aer_config.def_property("_fusion_cost_9", - [](const Config &config) { return config._fusion_cost_9.val;}, - [](Config &config, uint_t val) { config._fusion_cost_9.value(val);}); - aer_config.def_property("_fusion_cost_10", - [](const Config &config) { return config._fusion_cost_10.val;}, - [](Config &config, uint_t val) { config._fusion_cost_10.value(val);}); + // system configurations + aer_config.def_readwrite("library_dir", &Config::library_dir); + aer_config.def_property_readonly_static( + "GLOBAL_PHASE_POS", + [](const py::object &) { return Config::GLOBAL_PHASE_POS; }); + aer_config.def_readwrite("parameterizations", &Config::param_table); + aer_config.def_property( + "n_qubits", [](const Config &config) { return config.n_qubits.val; }, + [](Config &config, uint_t val) { config.n_qubits.value(val); }); + aer_config.def_readwrite("global_phase", &Config::global_phase); + aer_config.def_readwrite("memory_slots", &Config::memory_slots); + aer_config.def_property( + "_parallel_experiments", + [](const Config &config) { return config._parallel_experiments.val; }, + [](Config &config, uint_t val) { + config._parallel_experiments.value(val); + }); + aer_config.def_property( + "_parallel_shots", + [](const Config &config) { return config._parallel_shots.val; }, + [](Config &config, uint_t val) { config._parallel_shots.value(val); }); + aer_config.def_property( + "_parallel_state_update", + [](const Config &config) { return config._parallel_state_update.val; }, + [](Config &config, uint_t val) { + config._parallel_state_update.value(val); + }); + aer_config.def_property( + "fusion_allow_kraus", + [](const Config &config) { return config.fusion_allow_kraus.val; }, + [](Config &config, bool val) { config.fusion_allow_kraus.value(val); }); + aer_config.def_property( + "fusion_allow_superop", + [](const Config &config) { return config.fusion_allow_superop.val; }, + [](Config &config, bool val) { config.fusion_allow_superop.value(val); }); + aer_config.def_property( + "fusion_parallelization_threshold", + [](const Config &config) { + return config.fusion_parallelization_threshold.val; + }, + [](Config &config, uint_t val) { + config.fusion_parallelization_threshold.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits", + [](const Config &config) { return config._fusion_enable_n_qubits.val; }, + [](Config &config, bool val) { + config._fusion_enable_n_qubits.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits_1", + [](const Config &config) { return config._fusion_enable_n_qubits_1.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_n_qubits_1.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits_2", + [](const Config &config) { return config._fusion_enable_n_qubits_2.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_n_qubits_2.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits_3", + [](const Config &config) { return config._fusion_enable_n_qubits_3.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_n_qubits_3.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits_4", + [](const Config &config) { return config._fusion_enable_n_qubits_4.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_n_qubits_4.value(val); + }); + aer_config.def_property( + "_fusion_enable_n_qubits_5", + [](const Config &config) { return config._fusion_enable_n_qubits_5.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_n_qubits_5.value(val); + }); + aer_config.def_property( + "_fusion_enable_diagonal", + [](const Config &config) { return config._fusion_enable_diagonal.val; }, + [](Config &config, uint_t val) { + config._fusion_enable_diagonal.value(val); + }); + aer_config.def_property( + "_fusion_min_qubit", + [](const Config &config) { return config._fusion_min_qubit.val; }, + [](Config &config, uint_t val) { config._fusion_min_qubit.value(val); }); + aer_config.def_property( + "fusion_cost_factor", + [](const Config &config) { return config.fusion_cost_factor.val; }, + [](Config &config, double val) { config.fusion_cost_factor.value(val); }); + aer_config.def_property( + "_fusion_enable_cost_based", + [](const Config &config) { return config._fusion_enable_cost_based.val; }, + [](Config &config, bool val) { + config._fusion_enable_cost_based.value(val); + }); + aer_config.def_property( + "_fusion_cost_1", + [](const Config &config) { return config._fusion_cost_1.val; }, + [](Config &config, uint_t val) { config._fusion_cost_1.value(val); }); + aer_config.def_property( + "_fusion_cost_2", + [](const Config &config) { return config._fusion_cost_2.val; }, + [](Config &config, uint_t val) { config._fusion_cost_2.value(val); }); + aer_config.def_property( + "_fusion_cost_3", + [](const Config &config) { return config._fusion_cost_3.val; }, + [](Config &config, uint_t val) { config._fusion_cost_3.value(val); }); + aer_config.def_property( + "_fusion_cost_4", + [](const Config &config) { return config._fusion_cost_4.val; }, + [](Config &config, uint_t val) { config._fusion_cost_4.value(val); }); + aer_config.def_property( + "_fusion_cost_5", + [](const Config &config) { return config._fusion_cost_5.val; }, + [](Config &config, uint_t val) { config._fusion_cost_5.value(val); }); + aer_config.def_property( + "_fusion_cost_6", + [](const Config &config) { return config._fusion_cost_6.val; }, + [](Config &config, uint_t val) { config._fusion_cost_6.value(val); }); + aer_config.def_property( + "_fusion_cost_7", + [](const Config &config) { return config._fusion_cost_7.val; }, + [](Config &config, uint_t val) { config._fusion_cost_7.value(val); }); + aer_config.def_property( + "_fusion_cost_8", + [](const Config &config) { return config._fusion_cost_8.val; }, + [](Config &config, uint_t val) { config._fusion_cost_8.value(val); }); + aer_config.def_property( + "_fusion_cost_9", + [](const Config &config) { return config._fusion_cost_9.val; }, + [](Config &config, uint_t val) { config._fusion_cost_9.value(val); }); + aer_config.def_property( + "_fusion_cost_10", + [](const Config &config) { return config._fusion_cost_10.val; }, + [](Config &config, uint_t val) { config._fusion_cost_10.value(val); }); - aer_config.def_property("superoperator_parallel_threshold", - [](const Config &config) { return config.superoperator_parallel_threshold.val;}, - [](Config &config, uint_t val) { config.superoperator_parallel_threshold.value(val);}); - aer_config.def_property("unitary_parallel_threshold", - [](const Config &config) { return config.unitary_parallel_threshold.val;}, - [](Config &config, uint_t val) { config.unitary_parallel_threshold.value(val);}); - aer_config.def_property("memory_blocking_bits", - [](const Config &config) { return config.memory_blocking_bits.val;}, - [](Config &config, uint_t val) { config.memory_blocking_bits.value(val);}); - aer_config.def_property("extended_stabilizer_norm_estimation_default_samples", - [](const Config &config) { return config.extended_stabilizer_norm_estimation_default_samples.val;}, - [](Config &config, uint_t val) { config.extended_stabilizer_norm_estimation_default_samples.value(val);}); + aer_config.def_property( + "superoperator_parallel_threshold", + [](const Config &config) { + return config.superoperator_parallel_threshold.val; + }, + [](Config &config, uint_t val) { + config.superoperator_parallel_threshold.value(val); + }); + aer_config.def_property( + "unitary_parallel_threshold", + [](const Config &config) { + return config.unitary_parallel_threshold.val; + }, + [](Config &config, uint_t val) { + config.unitary_parallel_threshold.value(val); + }); + aer_config.def_property( + "memory_blocking_bits", + [](const Config &config) { return config.memory_blocking_bits.val; }, + [](Config &config, uint_t val) { + config.memory_blocking_bits.value(val); + }); + aer_config.def_property( + "extended_stabilizer_norm_estimation_default_samples", + [](const Config &config) { + return config.extended_stabilizer_norm_estimation_default_samples.val; + }, + [](Config &config, uint_t val) { + config.extended_stabilizer_norm_estimation_default_samples.value(val); + }); - aer_config.def(py::pickle( - [](const AER::Config &config) { - return py::make_tuple( - write_value(0, config.shots), - write_value(1, config.method), - write_value(2, config.device), - write_value(3, config.precision), - write_value(4, config.enable_truncation), - write_value(5, config.zero_threshold), - write_value(6, config.validation_threshold), - write_value(7, config.max_parallel_threads), - write_value(8, config.max_parallel_experiments), - write_value(9, config.max_parallel_shots), - write_value(10, config.max_memory_mb), - write_value(11, config.fusion_enable), - write_value(12, config.fusion_verbose), - write_value(13, config.fusion_max_qubit), - write_value(14, config.fusion_threshold), - write_value(15, config.accept_distributed_results), - write_value(16, config.memory), - write_value(17, config.seed_simulator), - write_value(18, config.cuStateVec_enable), - write_value(19, config.blocking_qubits), - write_value(20, config.blocking_enable), - write_value(21, config.chunk_swap_buffer_qubits), - write_value(22, config.batched_shots_gpu), - write_value(23, config.batched_shots_gpu_max_qubits), - write_value(24, config.num_threads_per_device), - write_value(25, config.statevector_parallel_threshold), - write_value(26, config.statevector_sample_measure_opt), - write_value(27, config.stabilizer_max_snapshot_probabilities), - write_value(28, config.extended_stabilizer_sampling_method), - write_value(29, config.extended_stabilizer_metropolis_mixing_time), - write_value(20, config.extended_stabilizer_approximation_error), - write_value(31, config.extended_stabilizer_norm_estimation_samples), - write_value(32, config.extended_stabilizer_norm_estimation_repetitions), - write_value(33, config.extended_stabilizer_parallel_threshold), - write_value(34, config.extended_stabilizer_probabilities_snapshot_samples), - write_value(35, config.matrix_product_state_truncation_threshold), - write_value(36, config.matrix_product_state_max_bond_dimension), - write_value(37, config.mps_sample_measure_algorithm), - write_value(38, config.mps_log_data), - write_value(39, config.mps_swap_direction), - write_value(30, config.chop_threshold), - write_value(41, config.mps_parallel_threshold), - write_value(42, config.mps_omp_threads), - write_value(43, config.tensor_network_num_sampling_qubits), - write_value(44, config.use_cuTensorNet_autotuning), - write_value(45, config.library_dir), - write_value(46, config.param_table), - write_value(47, config.n_qubits), - write_value(48, config.global_phase), - write_value(49, config.memory_slots), - write_value(50, config._parallel_experiments), - write_value(51, config._parallel_shots), - write_value(52, config._parallel_state_update), - write_value(53, config.fusion_allow_kraus), - write_value(54, config.fusion_allow_superop), - write_value(55, config.fusion_parallelization_threshold), - write_value(56, config._fusion_enable_n_qubits), - write_value(57, config._fusion_enable_n_qubits_1), - write_value(58, config._fusion_enable_n_qubits_2), - write_value(59, config._fusion_enable_n_qubits_3), - write_value(60, config._fusion_enable_n_qubits_4), - write_value(61, config._fusion_enable_n_qubits_5), - write_value(62, config._fusion_enable_diagonal), - write_value(63, config._fusion_min_qubit), - write_value(64, config.fusion_cost_factor), - write_value(65, config._fusion_enable_cost_based), - write_value(66, config._fusion_cost_1), - write_value(67, config._fusion_cost_2), - write_value(68, config._fusion_cost_3), - write_value(69, config._fusion_cost_4), - write_value(70, config._fusion_cost_5), - write_value(71, config._fusion_cost_6), - write_value(72, config._fusion_cost_7), - write_value(73, config._fusion_cost_8), - write_value(74, config._fusion_cost_9), - write_value(75, config._fusion_cost_10), + aer_config.def(py::pickle( + [](const AER::Config &config) { + return py::make_tuple( + write_value(0, config.shots), write_value(1, config.method), + write_value(2, config.device), write_value(3, config.precision), + write_value(4, config.enable_truncation), + write_value(5, config.zero_threshold), + write_value(6, config.validation_threshold), + write_value(7, config.max_parallel_threads), + write_value(8, config.max_parallel_experiments), + write_value(9, config.max_parallel_shots), + write_value(10, config.max_memory_mb), + write_value(11, config.fusion_enable), + write_value(12, config.fusion_verbose), + write_value(13, config.fusion_max_qubit), + write_value(14, config.fusion_threshold), + write_value(15, config.accept_distributed_results), + write_value(16, config.memory), + write_value(17, config.seed_simulator), + write_value(18, config.cuStateVec_enable), + write_value(19, config.blocking_qubits), + write_value(20, config.blocking_enable), + write_value(21, config.chunk_swap_buffer_qubits), + write_value(22, config.batched_shots_gpu), + write_value(23, config.batched_shots_gpu_max_qubits), + write_value(24, config.num_threads_per_device), + write_value(25, config.statevector_parallel_threshold), + write_value(26, config.statevector_sample_measure_opt), + write_value(27, config.stabilizer_max_snapshot_probabilities), + write_value(28, config.extended_stabilizer_sampling_method), + write_value(29, config.extended_stabilizer_metropolis_mixing_time), + write_value(20, config.extended_stabilizer_approximation_error), + write_value(31, config.extended_stabilizer_norm_estimation_samples), + write_value(32, + config.extended_stabilizer_norm_estimation_repetitions), + write_value(33, config.extended_stabilizer_parallel_threshold), + write_value( + 34, config.extended_stabilizer_probabilities_snapshot_samples), + write_value(35, config.matrix_product_state_truncation_threshold), + write_value(36, config.matrix_product_state_max_bond_dimension), + write_value(37, config.mps_sample_measure_algorithm), + write_value(38, config.mps_log_data), + write_value(39, config.mps_swap_direction), + write_value(30, config.chop_threshold), + write_value(41, config.mps_parallel_threshold), + write_value(42, config.mps_omp_threads), + write_value(43, config.tensor_network_num_sampling_qubits), + write_value(44, config.use_cuTensorNet_autotuning), + write_value(45, config.library_dir), + write_value(46, config.param_table), + write_value(47, config.n_qubits), + write_value(48, config.global_phase), + write_value(49, config.memory_slots), + write_value(50, config._parallel_experiments), + write_value(51, config._parallel_shots), + write_value(52, config._parallel_state_update), + write_value(53, config.fusion_allow_kraus), + write_value(54, config.fusion_allow_superop), + write_value(55, config.fusion_parallelization_threshold), + write_value(56, config._fusion_enable_n_qubits), + write_value(57, config._fusion_enable_n_qubits_1), + write_value(58, config._fusion_enable_n_qubits_2), + write_value(59, config._fusion_enable_n_qubits_3), + write_value(60, config._fusion_enable_n_qubits_4), + write_value(61, config._fusion_enable_n_qubits_5), + write_value(62, config._fusion_enable_diagonal), + write_value(63, config._fusion_min_qubit), + write_value(64, config.fusion_cost_factor), + write_value(65, config._fusion_enable_cost_based), + write_value(66, config._fusion_cost_1), + write_value(67, config._fusion_cost_2), + write_value(68, config._fusion_cost_3), + write_value(69, config._fusion_cost_4), + write_value(70, config._fusion_cost_5), + write_value(71, config._fusion_cost_6), + write_value(72, config._fusion_cost_7), + write_value(73, config._fusion_cost_8), + write_value(74, config._fusion_cost_9), + write_value(75, config._fusion_cost_10), - write_value(76, config.superoperator_parallel_threshold), - write_value(77, config.unitary_parallel_threshold), - write_value(78, config.memory_blocking_bits), - write_value(79, config.extended_stabilizer_norm_estimation_default_samples) - ); - }, - [](py::tuple t) { - AER::Config config; - if (t.size() != 79) - throw std::runtime_error("Invalid serialization format."); + write_value(76, config.superoperator_parallel_threshold), + write_value(77, config.unitary_parallel_threshold), + write_value(78, config.memory_blocking_bits), + write_value( + 79, + config.extended_stabilizer_norm_estimation_default_samples)); + }, + [](py::tuple t) { + AER::Config config; + if (t.size() != 79) + throw std::runtime_error("Invalid serialization format."); - read_value(t, 0, config.shots); - read_value(t, 1, config.method); - read_value(t, 2, config.device); - read_value(t, 3, config.precision); - read_value(t, 4, config.enable_truncation); - read_value(t, 5, config.zero_threshold); - read_value(t, 6, config.validation_threshold); - read_value(t, 7, config.max_parallel_threads); - read_value(t, 8, config.max_parallel_experiments); - read_value(t, 9, config.max_parallel_shots); - read_value(t, 10, config.max_memory_mb); - read_value(t, 11, config.fusion_enable); - read_value(t, 12, config.fusion_verbose); - read_value(t, 13, config.fusion_max_qubit); - read_value(t, 14, config.fusion_threshold); - read_value(t, 15, config.accept_distributed_results); - read_value(t, 16, config.memory); - read_value(t, 17, config.seed_simulator); - read_value(t, 18, config.cuStateVec_enable); - read_value(t, 19, config.blocking_qubits); - read_value(t, 20, config.blocking_enable); - read_value(t, 21, config.chunk_swap_buffer_qubits); - read_value(t, 22, config.batched_shots_gpu); - read_value(t, 23, config.batched_shots_gpu_max_qubits); - read_value(t, 24, config.num_threads_per_device); - read_value(t, 25, config.statevector_parallel_threshold); - read_value(t, 26, config.statevector_sample_measure_opt); - read_value(t, 27, config.stabilizer_max_snapshot_probabilities); - read_value(t, 28, config.extended_stabilizer_sampling_method); - read_value(t, 29, config.extended_stabilizer_metropolis_mixing_time); - read_value(t, 20, config.extended_stabilizer_approximation_error); - read_value(t, 31, config.extended_stabilizer_norm_estimation_samples); - read_value(t, 32, config.extended_stabilizer_norm_estimation_repetitions); - read_value(t, 33, config.extended_stabilizer_parallel_threshold); - read_value(t, 34, config.extended_stabilizer_probabilities_snapshot_samples); - read_value(t, 35, config.matrix_product_state_truncation_threshold); - read_value(t, 36, config.matrix_product_state_max_bond_dimension); - read_value(t, 37, config.mps_sample_measure_algorithm); - read_value(t, 38, config.mps_log_data); - read_value(t, 39, config.mps_swap_direction); - read_value(t, 30, config.chop_threshold); - read_value(t, 41, config.mps_parallel_threshold); - read_value(t, 42, config.mps_omp_threads); - read_value(t, 43, config.tensor_network_num_sampling_qubits); - read_value(t, 44, config.use_cuTensorNet_autotuning); - read_value(t, 45, config.library_dir); - read_value(t, 46, config.param_table); - read_value(t, 47, config.n_qubits); - read_value(t, 48, config.global_phase); - read_value(t, 49, config.memory_slots); - read_value(t, 50, config._parallel_experiments); - read_value(t, 51, config._parallel_shots); - read_value(t, 52, config._parallel_state_update); - read_value(t, 53, config.fusion_allow_kraus); - read_value(t, 54, config.fusion_allow_superop); - read_value(t, 55, config.fusion_parallelization_threshold); - read_value(t, 56, config._fusion_enable_n_qubits); - read_value(t, 57, config._fusion_enable_n_qubits_1); - read_value(t, 58, config._fusion_enable_n_qubits_2); - read_value(t, 59, config._fusion_enable_n_qubits_3); - read_value(t, 60, config._fusion_enable_n_qubits_4); - read_value(t, 61, config._fusion_enable_n_qubits_5); - read_value(t, 62, config._fusion_enable_diagonal); - read_value(t, 63, config._fusion_min_qubit); - read_value(t, 64, config.fusion_cost_factor); - read_value(t, 65, config._fusion_enable_cost_based); - read_value(t, 66, config._fusion_cost_1); - read_value(t, 67, config._fusion_cost_2); - read_value(t, 68, config._fusion_cost_3); - read_value(t, 69, config._fusion_cost_4); - read_value(t, 70, config._fusion_cost_5); - read_value(t, 71, config._fusion_cost_6); - read_value(t, 72, config._fusion_cost_7); - read_value(t, 73, config._fusion_cost_8); - read_value(t, 74, config._fusion_cost_9); - read_value(t, 75, config._fusion_cost_10); + read_value(t, 0, config.shots); + read_value(t, 1, config.method); + read_value(t, 2, config.device); + read_value(t, 3, config.precision); + read_value(t, 4, config.enable_truncation); + read_value(t, 5, config.zero_threshold); + read_value(t, 6, config.validation_threshold); + read_value(t, 7, config.max_parallel_threads); + read_value(t, 8, config.max_parallel_experiments); + read_value(t, 9, config.max_parallel_shots); + read_value(t, 10, config.max_memory_mb); + read_value(t, 11, config.fusion_enable); + read_value(t, 12, config.fusion_verbose); + read_value(t, 13, config.fusion_max_qubit); + read_value(t, 14, config.fusion_threshold); + read_value(t, 15, config.accept_distributed_results); + read_value(t, 16, config.memory); + read_value(t, 17, config.seed_simulator); + read_value(t, 18, config.cuStateVec_enable); + read_value(t, 19, config.blocking_qubits); + read_value(t, 20, config.blocking_enable); + read_value(t, 21, config.chunk_swap_buffer_qubits); + read_value(t, 22, config.batched_shots_gpu); + read_value(t, 23, config.batched_shots_gpu_max_qubits); + read_value(t, 24, config.num_threads_per_device); + read_value(t, 25, config.statevector_parallel_threshold); + read_value(t, 26, config.statevector_sample_measure_opt); + read_value(t, 27, config.stabilizer_max_snapshot_probabilities); + read_value(t, 28, config.extended_stabilizer_sampling_method); + read_value(t, 29, config.extended_stabilizer_metropolis_mixing_time); + read_value(t, 20, config.extended_stabilizer_approximation_error); + read_value(t, 31, config.extended_stabilizer_norm_estimation_samples); + read_value(t, 32, + config.extended_stabilizer_norm_estimation_repetitions); + read_value(t, 33, config.extended_stabilizer_parallel_threshold); + read_value(t, 34, + config.extended_stabilizer_probabilities_snapshot_samples); + read_value(t, 35, config.matrix_product_state_truncation_threshold); + read_value(t, 36, config.matrix_product_state_max_bond_dimension); + read_value(t, 37, config.mps_sample_measure_algorithm); + read_value(t, 38, config.mps_log_data); + read_value(t, 39, config.mps_swap_direction); + read_value(t, 30, config.chop_threshold); + read_value(t, 41, config.mps_parallel_threshold); + read_value(t, 42, config.mps_omp_threads); + read_value(t, 43, config.tensor_network_num_sampling_qubits); + read_value(t, 44, config.use_cuTensorNet_autotuning); + read_value(t, 45, config.library_dir); + read_value(t, 46, config.param_table); + read_value(t, 47, config.n_qubits); + read_value(t, 48, config.global_phase); + read_value(t, 49, config.memory_slots); + read_value(t, 50, config._parallel_experiments); + read_value(t, 51, config._parallel_shots); + read_value(t, 52, config._parallel_state_update); + read_value(t, 53, config.fusion_allow_kraus); + read_value(t, 54, config.fusion_allow_superop); + read_value(t, 55, config.fusion_parallelization_threshold); + read_value(t, 56, config._fusion_enable_n_qubits); + read_value(t, 57, config._fusion_enable_n_qubits_1); + read_value(t, 58, config._fusion_enable_n_qubits_2); + read_value(t, 59, config._fusion_enable_n_qubits_3); + read_value(t, 60, config._fusion_enable_n_qubits_4); + read_value(t, 61, config._fusion_enable_n_qubits_5); + read_value(t, 62, config._fusion_enable_diagonal); + read_value(t, 63, config._fusion_min_qubit); + read_value(t, 64, config.fusion_cost_factor); + read_value(t, 65, config._fusion_enable_cost_based); + read_value(t, 66, config._fusion_cost_1); + read_value(t, 67, config._fusion_cost_2); + read_value(t, 68, config._fusion_cost_3); + read_value(t, 69, config._fusion_cost_4); + read_value(t, 70, config._fusion_cost_5); + read_value(t, 71, config._fusion_cost_6); + read_value(t, 72, config._fusion_cost_7); + read_value(t, 73, config._fusion_cost_8); + read_value(t, 74, config._fusion_cost_9); + read_value(t, 75, config._fusion_cost_10); - read_value(t, 76, config.superoperator_parallel_threshold); - read_value(t, 77, config.unitary_parallel_threshold); - read_value(t, 78, config.memory_blocking_bits); - read_value(t, 79, config.extended_stabilizer_norm_estimation_default_samples); - return config; - })); + read_value(t, 76, config.superoperator_parallel_threshold); + read_value(t, 77, config.unitary_parallel_threshold); + read_value(t, 78, config.memory_blocking_bits); + read_value(t, 79, + config.extended_stabilizer_norm_estimation_default_samples); + return config; + })); } #endif diff --git a/qiskit_aer/backends/wrappers/aer_state_binding.hpp b/qiskit_aer/backends/wrappers/aer_state_binding.hpp index 36fd4da9a4..45dd55bcbf 100644 --- a/qiskit_aer/backends/wrappers/aer_state_binding.hpp +++ b/qiskit_aer/backends/wrappers/aer_state_binding.hpp @@ -20,17 +20,17 @@ DISABLE_WARNING_PUSH #include DISABLE_WARNING_POP #if defined(_MSC_VER) - #undef snprintf +#undef snprintf #endif -#include #include +#include #include "framework/matrix.hpp" -#include "framework/python_parser.hpp" #include "framework/pybind_casts.hpp" -#include "framework/types.hpp" +#include "framework/python_parser.hpp" #include "framework/results/pybind_result.hpp" +#include "framework/types.hpp" #include "controllers/state_controller.hpp" @@ -46,50 +46,57 @@ void bind_aer_state(MODULE m) { aer_state.def("__repr__", [](const AerState &state) { std::stringstream ss; ss << "AerStateWrapper(" - << "initialized=" << state.is_initialized() - << ", num_of_qubits=" << state.num_of_qubits(); + << "initialized=" << state.is_initialized() + << ", num_of_qubits=" << state.num_of_qubits(); ss << ")"; return ss.str(); }); - aer_state.def("configure", &AerState::configure); - aer_state.def("allocate_qubits", &AerState::allocate_qubits); - aer_state.def("reallocate_qubits", &AerState::reallocate_qubits); - aer_state.def("set_random_seed", &AerState::set_random_seed); - aer_state.def("set_seed", &AerState::set_seed); - aer_state.def("clear", &AerState::clear); - aer_state.def("num_of_qubits", &AerState::num_of_qubits); - - aer_state.def("initialize", &AerState::initialize); - aer_state.def("initialize_statevector", [aer_state](AerState &state, - int num_of_qubits, - py::array_t> &values, - bool copy) { - auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); - auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); - if (!c_contiguous && !f_contiguous) - return false; - std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); - state.configure("method", "statevector"); - state.initialize_statevector(num_of_qubits, data_ptr, copy); - return true; - }); - - aer_state.def("initialize_density_matrix", [aer_state](AER::AerState &state, - int num_of_qubits, - py::array_t> &values, - bool copy) { - auto c_contiguous = values.attr("flags").attr("c_contiguous").template cast(); - auto f_contiguous = values.attr("flags").attr("f_contiguous").template cast(); - if (!c_contiguous && !f_contiguous) - return false; - std::complex* data_ptr = reinterpret_cast*>(values.mutable_data(0)); - state.configure("method", "density_matrix"); - state.initialize_density_matrix(num_of_qubits, data_ptr, f_contiguous, copy); - return true; - }); - - aer_state.def("move_to_buffer", [aer_state](AER::AerState &state) { + aer_state.def("configure", &AerState::configure); + aer_state.def("allocate_qubits", &AerState::allocate_qubits); + aer_state.def("reallocate_qubits", &AerState::reallocate_qubits); + aer_state.def("set_random_seed", &AerState::set_random_seed); + aer_state.def("set_seed", &AerState::set_seed); + aer_state.def("clear", &AerState::clear); + aer_state.def("num_of_qubits", &AerState::num_of_qubits); + + aer_state.def("initialize", &AerState::initialize); + aer_state.def( + "initialize_statevector", + [aer_state](AerState &state, int num_of_qubits, + py::array_t> &values, bool copy) { + auto c_contiguous = + values.attr("flags").attr("c_contiguous").template cast(); + auto f_contiguous = + values.attr("flags").attr("f_contiguous").template cast(); + if (!c_contiguous && !f_contiguous) + return false; + std::complex *data_ptr = + reinterpret_cast *>(values.mutable_data(0)); + state.configure("method", "statevector"); + state.initialize_statevector(num_of_qubits, data_ptr, copy); + return true; + }); + + aer_state.def( + "initialize_density_matrix", + [aer_state](AER::AerState &state, int num_of_qubits, + py::array_t> &values, bool copy) { + auto c_contiguous = + values.attr("flags").attr("c_contiguous").template cast(); + auto f_contiguous = + values.attr("flags").attr("f_contiguous").template cast(); + if (!c_contiguous && !f_contiguous) + return false; + std::complex *data_ptr = + reinterpret_cast *>(values.mutable_data(0)); + state.configure("method", "density_matrix"); + state.initialize_density_matrix(num_of_qubits, data_ptr, f_contiguous, + copy); + return true; + }); + + aer_state.def("move_to_buffer", [aer_state](AER::AerState &state) { return state.move_to_vector().move_to_buffer(); }); @@ -99,85 +106,85 @@ void bind_aer_state(MODULE m) { auto ret = AerToPy::to_numpy(std::move(vec)); return ret; }); - + aer_state.def("move_to_matrix", [aer_state](AER::AerState &state) { auto mat = state.move_to_matrix(); auto ret = AerToPy::to_numpy(std::move(mat)); return ret; }); - aer_state.def("flush", &AerState::flush_ops); + aer_state.def("flush", &AerState::flush_ops); - aer_state.def("last_result", [aer_state](AerState &state) { + aer_state.def("last_result", [aer_state](AerState &state) { return AerToPy::to_python(state.last_result().to_json()); }); - - aer_state.def("apply_initialize", &AerState::apply_initialize); - aer_state.def("set_statevector", &AER::AerState::set_statevector); - aer_state.def("set_density_matrix", &AER::AerState::set_density_matrix); - - aer_state.def("apply_global_phase", &AerState::apply_global_phase); - aer_state.def("apply_unitary", [aer_state](AerState &state, - const reg_t &qubits, - const py::array_t> &values) { - size_t mat_len = (1UL << qubits.size()); - auto ptr = values.unchecked<2>(); - cmatrix_t mat(mat_len, mat_len); - for (auto i = 0; i < mat_len; ++i) - for (auto j = 0; j < mat_len; ++j) - mat(i, j) = ptr(i, j); - state.apply_unitary(qubits, mat); - }); - - aer_state.def("apply_multiplexer", [aer_state](AerState &state, - const reg_t &control_qubits, - const reg_t &target_qubits, - const py::array_t> &values) { - size_t mat_len = (1UL << target_qubits.size()); - size_t mat_size = (1UL << control_qubits.size()); - auto ptr = values.unchecked<3>(); - std::vector mats; - for (auto i = 0; i < mat_size; ++i) { - cmatrix_t mat(mat_len, mat_len); - for (auto j = 0; j < mat_len; ++j) - for (auto k = 0; k < mat_len; ++k) - mat(j, k) = ptr(i, j, k); - mats.push_back(mat); - } - state.apply_multiplexer(control_qubits, target_qubits, mats); - }); - - aer_state.def("apply_diagonal", &AerState::apply_diagonal_matrix); - aer_state.def("apply_x", &AerState::apply_x); - aer_state.def("apply_cx", &AerState::apply_cx); - aer_state.def("apply_mcx", &AerState::apply_mcx); - aer_state.def("apply_y", &AerState::apply_y); - aer_state.def("apply_cy", &AerState::apply_cy); - aer_state.def("apply_mcy", &AerState::apply_mcy); - aer_state.def("apply_z", &AerState::apply_z); - aer_state.def("apply_cz", &AerState::apply_cz); - aer_state.def("apply_mcz", &AerState::apply_mcz); - aer_state.def("apply_mcphase", &AerState::apply_mcphase); - aer_state.def("apply_h", &AerState::apply_h); - aer_state.def("apply_u", &AerState::apply_u); - aer_state.def("apply_cu", &AerState::apply_cu); - aer_state.def("apply_mcu", &AerState::apply_mcu); - aer_state.def("apply_mcswap", &AerState::apply_mcswap); - aer_state.def("apply_measure", &AerState::apply_measure); - aer_state.def("apply_reset", &AerState::apply_reset); - aer_state.def("apply_kraus", &AER::AerState::apply_kraus); - aer_state.def("probability", &AerState::probability); - aer_state.def("probabilities", [aer_state](AerState &state, - const reg_t qubits) { - if (qubits.empty()) - return state.probabilities(); - else - return state.probabilities(qubits); - }, py::arg("qubits") = reg_t()); - aer_state.def("sample_memory", &AerState::sample_memory); - aer_state.def("sample_counts", &AerState::sample_counts); - + aer_state.def("apply_initialize", &AerState::apply_initialize); + aer_state.def("set_statevector", &AER::AerState::set_statevector); + aer_state.def("set_density_matrix", &AER::AerState::set_density_matrix); + + aer_state.def("apply_global_phase", &AerState::apply_global_phase); + aer_state.def("apply_unitary", + [aer_state](AerState &state, const reg_t &qubits, + const py::array_t> &values) { + size_t mat_len = (1UL << qubits.size()); + auto ptr = values.unchecked<2>(); + cmatrix_t mat(mat_len, mat_len); + for (auto i = 0; i < mat_len; ++i) + for (auto j = 0; j < mat_len; ++j) + mat(i, j) = ptr(i, j); + state.apply_unitary(qubits, mat); + }); + + aer_state.def("apply_multiplexer", + [aer_state](AerState &state, const reg_t &control_qubits, + const reg_t &target_qubits, + const py::array_t> &values) { + size_t mat_len = (1UL << target_qubits.size()); + size_t mat_size = (1UL << control_qubits.size()); + auto ptr = values.unchecked<3>(); + std::vector mats; + for (auto i = 0; i < mat_size; ++i) { + cmatrix_t mat(mat_len, mat_len); + for (auto j = 0; j < mat_len; ++j) + for (auto k = 0; k < mat_len; ++k) + mat(j, k) = ptr(i, j, k); + mats.push_back(mat); + } + state.apply_multiplexer(control_qubits, target_qubits, mats); + }); + + aer_state.def("apply_diagonal", &AerState::apply_diagonal_matrix); + aer_state.def("apply_x", &AerState::apply_x); + aer_state.def("apply_cx", &AerState::apply_cx); + aer_state.def("apply_mcx", &AerState::apply_mcx); + aer_state.def("apply_y", &AerState::apply_y); + aer_state.def("apply_cy", &AerState::apply_cy); + aer_state.def("apply_mcy", &AerState::apply_mcy); + aer_state.def("apply_z", &AerState::apply_z); + aer_state.def("apply_cz", &AerState::apply_cz); + aer_state.def("apply_mcz", &AerState::apply_mcz); + aer_state.def("apply_mcphase", &AerState::apply_mcphase); + aer_state.def("apply_h", &AerState::apply_h); + aer_state.def("apply_u", &AerState::apply_u); + aer_state.def("apply_cu", &AerState::apply_cu); + aer_state.def("apply_mcu", &AerState::apply_mcu); + aer_state.def("apply_mcswap", &AerState::apply_mcswap); + aer_state.def("apply_measure", &AerState::apply_measure); + aer_state.def("apply_reset", &AerState::apply_reset); + aer_state.def("apply_kraus", &AER::AerState::apply_kraus); + aer_state.def("probability", &AerState::probability); + aer_state.def( + "probabilities", + [aer_state](AerState &state, const reg_t qubits) { + if (qubits.empty()) + return state.probabilities(); + else + return state.probabilities(qubits); + }, + py::arg("qubits") = reg_t()); + aer_state.def("sample_memory", &AerState::sample_memory); + aer_state.def("sample_counts", &AerState::sample_counts); } #endif \ No newline at end of file diff --git a/qiskit_aer/jobs/aerjob.py b/qiskit_aer/jobs/aerjob.py index 0bd0124264..545fbe773e 100644 --- a/qiskit_aer/jobs/aerjob.py +++ b/qiskit_aer/jobs/aerjob.py @@ -27,11 +27,18 @@ class AerJob(Job): """AerJob class for Qiskit Aer Simulators.""" - def __init__(self, backend, job_id, fn, - qobj=None, - circuits=None, noise_model=None, config=None, - executor=None): - """ Initializes the asynchronous job. + def __init__( + self, + backend, + job_id, + fn, + qobj=None, + circuits=None, + noise_model=None, + config=None, + executor=None, + ): + """Initializes the asynchronous job. Args: backend(AerBackend): the backend used to run the job. @@ -65,7 +72,7 @@ def __init__(self, backend, job_id, fn, self._noise_model = noise_model self._config = config else: - raise JobError('AerJob needs a qobj or circuits') + raise JobError("AerJob needs a qobj or circuits") self._executor = executor or DEFAULT_EXECUTOR self._future = None @@ -82,8 +89,9 @@ def submit(self): if self._qobj: self._future = self._executor.submit(self._fn, self._qobj, self._job_id) else: - self._future = self._executor.submit(self._fn, self._circuits, self._noise_model, - self._config, self._job_id) + self._future = self._executor.submit( + self._fn, self._circuits, self._noise_model, self._config, self._job_id + ) @requires_submit def result(self, timeout=None): @@ -147,11 +155,13 @@ def qobj(self): Qobj: the Qobj submitted for this job. """ warnings.warn( - '`AerJob.qobj() is deprecated as of qiskit-aer 0.12.0`. ' - 'Using a qobj for `backend.run()` is deprecated as of qiskit-aer 0.9.0' - ' and will be removed no sooner than 3 months from that release' - ' date. Once it is removed, this `qobj()` returns always `None`.', - DeprecationWarning, stacklevel=2) + "`AerJob.qobj() is deprecated as of qiskit-aer 0.12.0`. " + "Using a qobj for `backend.run()` is deprecated as of qiskit-aer 0.9.0" + " and will be removed no sooner than 3 months from that release" + " date. Once it is removed, this `qobj()` returns always `None`.", + DeprecationWarning, + stacklevel=2, + ) return self._qobj def circuits(self): diff --git a/qiskit_aer/jobs/aerjobset.py b/qiskit_aer/jobs/aerjobset.py index 5b32625371..ce3d3d6229 100644 --- a/qiskit_aer/jobs/aerjobset.py +++ b/qiskit_aer/jobs/aerjobset.py @@ -79,8 +79,7 @@ def submit(self): RuntimeError: If the jobs were already submitted. """ if self._futures: - raise RuntimeError( - 'The jobs for this managed job set have already been submitted.') + raise RuntimeError("The jobs for this managed job set have already been submitted.") self._future = True worker_id = 0 @@ -99,8 +98,7 @@ def submit(self): self._combined_result.append(_worker_id_list) @requires_submit - def status(self, worker: Union[None, int, Iterable[int]] - ) -> Union[JobStatus, List[JobStatus]]: + def status(self, worker: Union[None, int, Iterable[int]]) -> Union[JobStatus, List[JobStatus]]: """Return the status of each job in this set. Args @@ -122,9 +120,10 @@ def status(self, worker: Union[None, int, Iterable[int]] return [aer.status() for aer in self._futures] @requires_submit - def result(self, - timeout: Optional[float] = None, - ) -> Result: + def result( + self, + timeout: Optional[float] = None, + ) -> Result: """Return the results of the jobs as a single Result object. This call will block until all job results become available or @@ -145,10 +144,11 @@ def result(self, return res @requires_submit - def worker_results(self, - worker: Union[None, int, Iterable[int]], - timeout: Optional[float] = None, - ) -> Union[Result, List[Result]]: + def worker_results( + self, + worker: Union[None, int, Iterable[int]], + timeout: Optional[float] = None, + ) -> Union[Result, List[Result]]: """Return the result of the jobs specified with worker_id. When the worker is None, this call return all worker's result. @@ -216,19 +216,18 @@ def _get_worker_result(self, worker: int, timeout: Optional[float] = None): result = aer_job.result(timeout=timeout) if result is None or not result.success: if result: - logger.warning('AerJobSet %s Error: %s', aer_job.name(), result.header) + logger.warning("AerJobSet %s Error: %s", aer_job.name(), result.header) else: - logger.warning('AerJobSet %s did not return a result', aer_job.name()) + logger.warning("AerJobSet %s did not return a result", aer_job.name()) except JobError: raise JobError( - 'Timeout while waiting for the results of experiment {}'.format( - aer_job.name())) + "Timeout while waiting for the results of experiment {}".format(aer_job.name()) + ) if timeout: timeout = original_timeout - (time.time() - start_time) if timeout <= 0: - raise JobError( - "Timeout while waiting for JobSet results") + raise JobError("Timeout while waiting for JobSet results") return result def _combine_job_results(self, result_list: List[Result]): @@ -239,7 +238,7 @@ def _combine_job_results(self, result_list: List[Result]): _merge_result_list = [] for _result in result_list[1:]: - for (_master_result, _sub_result) in zip(master_result.results, _result.results): + for _master_result, _sub_result in zip(master_result.results, _result.results): _merge_result_list.append(self._merge_exp(_master_result, _sub_result)) master_result.results = _merge_result_list return master_result @@ -268,8 +267,7 @@ def _accumulate_experiment_results(self, results: List[Result]): for _result in each_result.results: if not hasattr(_result.data, "counts") and not hasattr(_result.data, "memory"): - raise JobError( - "Results do not include counts or memory data") + raise JobError("Results do not include counts or memory data") meta_data = getattr(_result.header, "metadata", None) if meta_data and "id" in meta_data: _id = meta_data["id"] @@ -295,9 +293,7 @@ def _merge_exp(self, master: Result, sub: Result): return master - def _combine_results(self, - results: List[Union[Result, None]] = None - ) -> Result: + def _combine_results(self, results: List[Union[Result, None]] = None) -> Result: """Combine results from all jobs into a single `Result`. Note: @@ -313,8 +309,7 @@ def _combine_results(self, JobError: If results cannot be combined because some jobs failed. """ if not results: - raise JobError( - "Results cannot be combined - no results.") + raise JobError("Results cannot be combined - no results.") # find first non-null result and copy it's config _result = next((r for r in results if r is not None), None) @@ -334,8 +329,7 @@ def _combine_results(self, combined_result["header"] = _result.header.to_dict() combined_result.update(_result._metadata) else: - raise JobError( - "Results cannot be combined - no results.") + raise JobError("Results cannot be combined - no results.") for each_result in results: if each_result is not None: @@ -381,9 +375,7 @@ def job(self, experiment: Union[str, QuantumCircuit, Schedule]) -> Tuple[AerJob, return self.worker_job(worker_index) @requires_submit - def worker(self, - experiment: Union[str, QuantumCircuit, Schedule] - ) -> Union[int, List[int]]: + def worker(self, experiment: Union[str, QuantumCircuit, Schedule]) -> Union[int, List[int]]: """Retrieve the index of job. Args: @@ -406,7 +398,7 @@ def worker(self, job_list = [] for job in self._futures: for i, exp in enumerate(job.qobj().experiments): - if hasattr(exp.header, 'name') and exp.header.name == experiment: + if hasattr(exp.header, "name") and exp.header.name == experiment: job_list.append(i) if len(job_list) == 1: @@ -414,13 +406,10 @@ def worker(self, elif len(job_list) > 1: return job_list - raise JobError( - 'Unable to find the job for experiment {}.'.format(experiment)) + raise JobError("Unable to find the job for experiment {}.".format(experiment)) @requires_submit - def worker_job(self, - worker: Union[None, int, Iterable[int]] - ) -> Union[AerJob, List[AerJob]]: + def worker_job(self, worker: Union[None, int, Iterable[int]]) -> Union[AerJob, List[AerJob]]: """Retrieve the job specified with job's id Args: diff --git a/qiskit_aer/jobs/utils.py b/qiskit_aer/jobs/utils.py index b778496a54..ad016d1f0c 100644 --- a/qiskit_aer/jobs/utils.py +++ b/qiskit_aer/jobs/utils.py @@ -34,11 +34,13 @@ def requires_submit(func): Returns: callable: the decorated function. """ + @wraps(func) def _wrapper(self, *args, **kwargs): if self._future is None: raise JobError("Job not submitted yet!. You have to .submit() first!") return func(self, *args, **kwargs) + return _wrapper @@ -51,13 +53,13 @@ def methdispatch(func): def wrapper(*args, **kw): return dispatcher.dispatch(args[2].__class__)(*args, **kw) + wrapper.register = dispatcher.register update_wrapper(wrapper, func) return wrapper def _copy_qobj_for_noise(qobj, max_shot_size, qobj_id): - num_shot_jobs, shot_mod = divmod(qobj.config.shots, max_shot_size) qobj_list = [] @@ -99,7 +101,7 @@ def _split_qobj(qobj, max_size, qobj_id, seed): qobjs = [] # Check for parameterizations - params = getattr(qobj.config, 'parameterizations', None) + params = getattr(qobj.config, "parameterizations", None) for i in range(num_jobs): sub_id = qobj_id or str(uuid.uuid4()) @@ -125,20 +127,13 @@ def _check_custom_instruction(experiments, optypes=None): # Check via optype list if available if optypes is not None: # Optypes store class names as strings - return any( - {"SaveData"}.intersection(optype) - for optype in optypes - ) + return any({"SaveData"}.intersection(optype) for optype in optypes) # Otherwise iterate over instruction names - return any( - "save_" in inst.name - for exp in experiments for inst in exp.instructions - ) + return any("save_" in inst.name for exp in experiments for inst in exp.instructions) def _set_seed(qobj_list, seed): - # set seed number to each qobj seed_shift = 256 @@ -169,19 +164,22 @@ def split_qobj(qobj, max_size=None, max_shot_size=None, qobj_id=None): Returns: List: A list of qobjs. """ - optypes = getattr(qobj.config, 'optypes', None) + optypes = getattr(qobj.config, "optypes", None) split_qobj_list = [] - if (max_shot_size is not None and max_shot_size > 0): + if max_shot_size is not None and max_shot_size > 0: if _check_custom_instruction(qobj.experiments, optypes): raise JobError( "`max_shot_size` option cannot be used with circuits" - " containing save instructions.") + " containing save instructions." + ) _seed = getattr(qobj.config, "seed_simulator", 0) if hasattr(qobj.config, "noise_model"): if _seed and max_size is not None and max_size > 1: - raise JobError("cannot support max_job_size > 1 for noise simulation, " - "when seed_simulator is set.") + raise JobError( + "cannot support max_job_size > 1 for noise simulation, " + "when seed_simulator is set." + ) if max_shot_size is not None and max_shot_size > 0: _qobj = _copy_qobj_for_noise(qobj, max_shot_size, qobj_id) diff --git a/qiskit_aer/library/__init__.py b/qiskit_aer/library/__init__.py index da05a836aa..965099e41c 100644 --- a/qiskit_aer/library/__init__.py +++ b/qiskit_aer/library/__init__.py @@ -177,27 +177,27 @@ """ __all__ = [ - 'SaveAmplitudes', - 'SaveAmplitudesSquared', - 'SaveClifford', - 'SaveDensityMatrix', - 'SaveExpectationValue', - 'SaveExpectationValueVariance', - 'SaveMatrixProductState', - 'SaveProbabilities', - 'SaveProbabilitiesDict', - 'SaveStabilizer', - 'SaveState', - 'SaveStatevector', - 'SaveStatevectorDict', - 'SaveSuperOp', - 'SaveUnitary', - 'SetDensityMatrix', - 'SetStabilizer', - 'SetStatevector', - 'SetSuperOp', - 'SetUnitary', - 'SetMatrixProductState' + "SaveAmplitudes", + "SaveAmplitudesSquared", + "SaveClifford", + "SaveDensityMatrix", + "SaveExpectationValue", + "SaveExpectationValueVariance", + "SaveMatrixProductState", + "SaveProbabilities", + "SaveProbabilitiesDict", + "SaveStabilizer", + "SaveState", + "SaveStatevector", + "SaveStatevectorDict", + "SaveSuperOp", + "SaveUnitary", + "SetDensityMatrix", + "SetStabilizer", + "SetStatevector", + "SetSuperOp", + "SetUnitary", + "SetMatrixProductState", ] from .save_instructions import * diff --git a/qiskit_aer/library/default_qubits.py b/qiskit_aer/library/default_qubits.py index 2e81fdc5a3..df251d158d 100644 --- a/qiskit_aer/library/default_qubits.py +++ b/qiskit_aer/library/default_qubits.py @@ -43,7 +43,7 @@ def default_qubits(circuit, qubits=None): for register in circuit.qregs: tuples.append(register) if not tuples: - raise ExtensionError('no qubits for snapshot') + raise ExtensionError("no qubits for snapshot") qubits = [] for tuple_element in tuples: if isinstance(tuple_element, QuantumRegister): diff --git a/qiskit_aer/library/save_instructions/__init__.py b/qiskit_aer/library/save_instructions/__init__.py index 1c5b990676..cb18acd870 100644 --- a/qiskit_aer/library/save_instructions/__init__.py +++ b/qiskit_aer/library/save_instructions/__init__.py @@ -11,22 +11,34 @@ # that they have been altered from the originals. """Save directive instructions for the Aer simulator""" -from .save_state import (SaveState, save_state) -from .save_expectation_value import (SaveExpectationValue, - save_expectation_value, - SaveExpectationValueVariance, - save_expectation_value_variance) -from .save_probabilities import (SaveProbabilities, save_probabilities, - SaveProbabilitiesDict, - save_probabilities_dict) -from .save_statevector import (SaveStatevector, save_statevector, - SaveStatevectorDict, save_statevector_dict) +from .save_state import SaveState, save_state +from .save_expectation_value import ( + SaveExpectationValue, + save_expectation_value, + SaveExpectationValueVariance, + save_expectation_value_variance, +) +from .save_probabilities import ( + SaveProbabilities, + save_probabilities, + SaveProbabilitiesDict, + save_probabilities_dict, +) +from .save_statevector import ( + SaveStatevector, + save_statevector, + SaveStatevectorDict, + save_statevector_dict, +) from .save_density_matrix import SaveDensityMatrix, save_density_matrix -from .save_amplitudes import (SaveAmplitudes, save_amplitudes, - SaveAmplitudesSquared, save_amplitudes_squared) -from .save_stabilizer import (SaveStabilizer, save_stabilizer) -from .save_clifford import (SaveClifford, save_clifford) -from .save_unitary import (SaveUnitary, save_unitary) -from .save_matrix_product_state import ( - SaveMatrixProductState, save_matrix_product_state) -from .save_superop import (SaveSuperOp, save_superop) +from .save_amplitudes import ( + SaveAmplitudes, + save_amplitudes, + SaveAmplitudesSquared, + save_amplitudes_squared, +) +from .save_stabilizer import SaveStabilizer, save_stabilizer +from .save_clifford import SaveClifford, save_clifford +from .save_unitary import SaveUnitary, save_unitary +from .save_matrix_product_state import SaveMatrixProductState, save_matrix_product_state +from .save_superop import SaveSuperOp, save_superop diff --git a/qiskit_aer/library/save_instructions/save_amplitudes.py b/qiskit_aer/library/save_instructions/save_amplitudes.py index 4daa0d3488..bcc12383d1 100644 --- a/qiskit_aer/library/save_instructions/save_amplitudes.py +++ b/qiskit_aer/library/save_instructions/save_amplitudes.py @@ -21,12 +21,8 @@ class SaveAmplitudes(SaveSingleData): """Save complex statevector amplitudes.""" - def __init__(self, - num_qubits, - params, - label="amplitudes", - pershot=False, - conditional=False): + + def __init__(self, num_qubits, params, label="amplitudes", pershot=False, conditional=False): """Instruction to save complex statevector amplitudes. Args: @@ -44,21 +40,28 @@ def __init__(self, ExtensionError: if params is invalid for the specified number of qubits. """ params = _format_amplitude_params(params, num_qubits) - super().__init__("save_amplitudes", num_qubits, label, - pershot=pershot, - conditional=conditional, - params=params) + super().__init__( + "save_amplitudes", + num_qubits, + label, + pershot=pershot, + conditional=conditional, + params=params, + ) class SaveAmplitudesSquared(SaveAverageData): """Save squared statevector amplitudes (probabilities).""" - def __init__(self, - num_qubits, - params, - label="amplitudes_squared", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + num_qubits, + params, + label="amplitudes_squared", + unnormalized=False, + pershot=False, + conditional=False, + ): """Instruction to save squared statevector amplitudes (probabilities). Args: @@ -78,13 +81,15 @@ def __init__(self, ExtensionError: if params is invalid for the specified number of qubits. """ params = _format_amplitude_params(params, num_qubits) - super().__init__("save_amplitudes_sq", - num_qubits, - label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional, - params=params) + super().__init__( + "save_amplitudes_sq", + num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params, + ) def save_amplitudes(self, params, label="amplitudes", pershot=False, conditional=False): @@ -107,15 +112,15 @@ def save_amplitudes(self, params, label="amplitudes", pershot=False, conditional ExtensionError: if params is invalid for the specified number of qubits. """ qubits = default_qubits(self) - instr = SaveAmplitudes(len(qubits), params, label=label, - pershot=pershot, conditional=conditional) + instr = SaveAmplitudes( + len(qubits), params, label=label, pershot=pershot, conditional=conditional + ) return self.append(instr, qubits) -def save_amplitudes_squared(self, params, label="amplitudes_squared", - unnormalized=False, - pershot=False, - conditional=False): +def save_amplitudes_squared( + self, params, label="amplitudes_squared", unnormalized=False, pershot=False, conditional=False +): """Save squared statevector amplitudes (probabilities). Args: @@ -137,23 +142,26 @@ def save_amplitudes_squared(self, params, label="amplitudes_squared", ExtensionError: if params is invalid for the specified number of qubits. """ qubits = default_qubits(self) - instr = SaveAmplitudesSquared(len(qubits), params, label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveAmplitudesSquared( + len(qubits), + params, + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) return self.append(instr, qubits) def _format_amplitude_params(params, num_qubits=None): """Format amplitude params as a interger list.""" if isinstance(params[0], str): - if params[0].find('0x') == 0: + if params[0].find("0x") == 0: params = [int(i, 16) for i in params] else: params = [int(i, 2) for i in params] - if num_qubits and max(params) >= 2 ** num_qubits: - raise ExtensionError( - "Param values contain a state larger than the number of qubits") + if num_qubits and max(params) >= 2**num_qubits: + raise ExtensionError("Param values contain a state larger than the number of qubits") return params diff --git a/qiskit_aer/library/save_instructions/save_clifford.py b/qiskit_aer/library/save_instructions/save_clifford.py index 866d1954d5..9088d1e9c2 100644 --- a/qiskit_aer/library/save_instructions/save_clifford.py +++ b/qiskit_aer/library/save_instructions/save_clifford.py @@ -20,6 +20,7 @@ class SaveClifford(SaveSingleData): """Save Clifford instruction""" + def __init__(self, num_qubits, label="clifford", pershot=False): """Create new instruction to save the stabilizer simulator state as a Clifford. @@ -36,7 +37,7 @@ def __init__(self, num_qubits, label="clifford", pershot=False): qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_clifford', num_qubits, label, pershot=pershot) + super().__init__("save_clifford", num_qubits, label, pershot=pershot) def save_clifford(self, label="clifford", pershot=False): diff --git a/qiskit_aer/library/save_instructions/save_data.py b/qiskit_aer/library/save_instructions/save_data.py index c3803ff152..cb86edd94e 100644 --- a/qiskit_aer/library/save_instructions/save_data.py +++ b/qiskit_aer/library/save_instructions/save_data.py @@ -23,12 +23,11 @@ class SaveData(Instruction): """Pragma Instruction to save simulator data.""" _directive = True - _allowed_subtypes = set([ - 'single', 'c_single', 'list', 'c_list', - 'average', 'c_average', 'accum', 'c_accum' - ]) + _allowed_subtypes = set( + ["single", "c_single", "list", "c_list", "average", "c_average", "accum", "c_accum"] + ) - def __init__(self, name, num_qubits, label, subtype='single', params=None): + def __init__(self, name, num_qubits, label, subtype="single", params=None): """Create new save data instruction. Args: @@ -47,12 +46,12 @@ def __init__(self, name, num_qubits, label, subtype='single', params=None): 'c_average', 'accum', 'c_accum'. """ if subtype not in self._allowed_subtypes: - raise ExtensionError( - "Invalid data subtype for SaveData instruction.") + raise ExtensionError("Invalid data subtype for SaveData instruction.") if not isinstance(label, str): raise ExtensionError( - f"Invalid label for save data instruction, {label} must be a string.") + f"Invalid label for save data instruction, {label} must be a string." + ) if params is None: params = {} @@ -78,14 +77,17 @@ def inverse(self): class SaveAverageData(SaveData): """Save averageble data""" - def __init__(self, - name, - num_qubits, - label, - unnormalized=False, - pershot=False, - conditional=False, - params=None): + + def __init__( + self, + name, + num_qubits, + label, + unnormalized=False, + pershot=False, + conditional=False, + params=None, + ): """Create new save data instruction. Args: @@ -105,26 +107,20 @@ def __init__(self, [Default: None]. """ if pershot: - subtype = 'list' + subtype = "list" elif unnormalized: - subtype = 'accum' + subtype = "accum" else: - subtype = 'average' + subtype = "average" if conditional: - subtype = 'c_' + subtype + subtype = "c_" + subtype super().__init__(name, num_qubits, label, subtype=subtype, params=params) class SaveSingleData(SaveData): """Save non-averagable single data type.""" - def __init__(self, - name, - num_qubits, - label, - pershot=False, - conditional=False, - params=None): + def __init__(self, name, num_qubits, label, pershot=False, conditional=False, params=None): """Create new save data instruction. Args: @@ -139,7 +135,7 @@ def __init__(self, params (list or None): Optional, the parameters for instruction [Default: None]. """ - subtype = 'list' if pershot else 'single' + subtype = "list" if pershot else "single" if conditional: - subtype = 'c_' + subtype + subtype = "c_" + subtype super().__init__(name, num_qubits, label, subtype=subtype, params=params) diff --git a/qiskit_aer/library/save_instructions/save_density_matrix.py b/qiskit_aer/library/save_instructions/save_density_matrix.py index 289c050788..4532fef2f3 100644 --- a/qiskit_aer/library/save_instructions/save_density_matrix.py +++ b/qiskit_aer/library/save_instructions/save_density_matrix.py @@ -20,12 +20,15 @@ class SaveDensityMatrix(SaveAverageData): """Save a reduced density matrix.""" - def __init__(self, - num_qubits, - label="density_matrix", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + num_qubits, + label="density_matrix", + unnormalized=False, + pershot=False, + conditional=False, + ): """Create new instruction to save the simulator reduced density matrix. Args: @@ -41,18 +44,19 @@ def __init__(self, conditional on the current classical register values [Default: False]. """ - super().__init__("save_density_matrix", num_qubits, label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + super().__init__( + "save_density_matrix", + num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) -def save_density_matrix(self, - qubits=None, - label="density_matrix", - unnormalized=False, - pershot=False, - conditional=False): +def save_density_matrix( + self, qubits=None, label="density_matrix", unnormalized=False, pershot=False, conditional=False +): """Save the current simulator quantum state as a density matrix. Args: @@ -74,11 +78,13 @@ def save_density_matrix(self, QuantumCircuit: with attached instruction. """ qubits = default_qubits(self, qubits=qubits) - instr = SaveDensityMatrix(len(qubits), - label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveDensityMatrix( + len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_expectation_value.py b/qiskit_aer/library/save_instructions/save_expectation_value.py index 71fd85bd8a..27bcad9da0 100644 --- a/qiskit_aer/library/save_instructions/save_expectation_value.py +++ b/qiskit_aer/library/save_instructions/save_expectation_value.py @@ -22,12 +22,15 @@ class SaveExpectationValue(SaveAverageData): """Save expectation value of an operator.""" - def __init__(self, - operator, - label="expectation_value", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + operator, + label="expectation_value", + unnormalized=False, + pershot=False, + conditional=False, + ): r"""Instruction to save the expectation value of a Hermitian operator. The expectation value of a Hermitian operator :math:`H` for a simulator @@ -63,21 +66,28 @@ def __init__(self, if not allclose(operator.coeffs.imag, 0): raise ExtensionError("Input operator is not Hermitian.") params = _expval_params(operator, variance=False) - super().__init__('save_expval', operator.num_qubits, label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional, - params=params) + super().__init__( + "save_expval", + operator.num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params, + ) class SaveExpectationValueVariance(SaveAverageData): """Save expectation value and variance of an operator.""" - def __init__(self, - operator, - label="expectation_value_variance", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + operator, + label="expectation_value_variance", + unnormalized=False, + pershot=False, + conditional=False, + ): r"""Instruction to save the expectation value and variance of a Hermitian operator. The expectation value of a Hermitian operator :math:`H` for a @@ -114,15 +124,18 @@ def __init__(self, if not allclose(operator.coeffs.imag, 0): raise ExtensionError("Input operator is not Hermitian.") params = _expval_params(operator, variance=True) - super().__init__('save_expval_var', operator.num_qubits, label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional, - params=params) + super().__init__( + "save_expval_var", + operator.num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params, + ) def _expval_params(operator, variance=False): - # Convert O to SparsePauliOp representation if isinstance(operator, Pauli): operator = SparsePauliOp(operator) @@ -154,13 +167,15 @@ def _expval_params(operator, variance=False): return list(params.items()) -def save_expectation_value(self, - operator, - qubits, - label="expectation_value", - unnormalized=False, - pershot=False, - conditional=False): +def save_expectation_value( + self, + operator, + qubits, + label="expectation_value", + unnormalized=False, + pershot=False, + conditional=False, +): r"""Save the expectation value of a Hermitian operator. Args: @@ -188,21 +203,21 @@ def save_expectation_value(self, This method appends a :class:`SaveExpectationValue` instruction to the quantum circuit. """ - instr = SaveExpectationValue(operator, - label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveExpectationValue( + operator, label=label, unnormalized=unnormalized, pershot=pershot, conditional=conditional + ) return self.append(instr, qubits) -def save_expectation_value_variance(self, - operator, - qubits, - label="expectation_value_variance", - unnormalized=False, - pershot=False, - conditional=False): +def save_expectation_value_variance( + self, + operator, + qubits, + label="expectation_value_variance", + unnormalized=False, + pershot=False, + conditional=False, +): r"""Save the expectation value of a Hermitian operator. Args: @@ -229,11 +244,9 @@ def save_expectation_value_variance(self, This method appends a :class:`SaveExpectationValueVariance` instruction to the quantum circuit. """ - instr = SaveExpectationValueVariance(operator, - label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveExpectationValueVariance( + operator, label=label, unnormalized=unnormalized, pershot=pershot, conditional=conditional + ) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_matrix_product_state.py b/qiskit_aer/library/save_instructions/save_matrix_product_state.py index 12f6ed51ee..31163beeb3 100644 --- a/qiskit_aer/library/save_instructions/save_matrix_product_state.py +++ b/qiskit_aer/library/save_instructions/save_matrix_product_state.py @@ -20,10 +20,8 @@ class SaveMatrixProductState(SaveSingleData): """Save matrix product state instruction""" - def __init__(self, num_qubits, - label="matrix_product_state", - pershot=False, - conditional=False): + + def __init__(self, num_qubits, label="matrix_product_state", pershot=False, conditional=False): """Create new instruction to save the matrix product state. Args: @@ -40,11 +38,9 @@ def __init__(self, num_qubits, qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_matrix_product_state', - num_qubits, - label, - pershot=pershot, - conditional=conditional) + super().__init__( + "save_matrix_product_state", num_qubits, label, pershot=pershot, conditional=conditional + ) def save_matrix_product_state(self, label="matrix_product_state", pershot=False, conditional=False): @@ -66,10 +62,9 @@ def save_matrix_product_state(self, label="matrix_product_state", pershot=False, This instruction is always defined across all qubits in a circuit. """ qubits = default_qubits(self) - instr = SaveMatrixProductState(len(qubits), - label=label, - pershot=pershot, - conditional=conditional) + instr = SaveMatrixProductState( + len(qubits), label=label, pershot=pershot, conditional=conditional + ) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_probabilities.py b/qiskit_aer/library/save_instructions/save_probabilities.py index caabbe53c6..cb3fcec678 100644 --- a/qiskit_aer/library/save_instructions/save_probabilities.py +++ b/qiskit_aer/library/save_instructions/save_probabilities.py @@ -20,12 +20,15 @@ class SaveProbabilities(SaveAverageData): """Save measurement outcome probabilities vector.""" - def __init__(self, - num_qubits, - label="probabilities", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + num_qubits, + label="probabilities", + unnormalized=False, + pershot=False, + conditional=False, + ): """Instruction to save measurement probabilities vector. Args: @@ -40,20 +43,27 @@ def __init__(self, on the current classical register values [Default: False]. """ - super().__init__("save_probabilities", num_qubits, label, - conditional=conditional, - pershot=pershot, - unnormalized=unnormalized) + super().__init__( + "save_probabilities", + num_qubits, + label, + conditional=conditional, + pershot=pershot, + unnormalized=unnormalized, + ) class SaveProbabilitiesDict(SaveAverageData): """Save measurement outcome probabilities dict.""" - def __init__(self, - num_qubits, - label="probabilities_dict", - unnormalized=False, - pershot=False, - conditional=False): + + def __init__( + self, + num_qubits, + label="probabilities_dict", + unnormalized=False, + pershot=False, + conditional=False, + ): """Instruction to save measurement probabilities dict. Args: @@ -68,18 +78,19 @@ def __init__(self, on the current classical register values [Default: False]. """ - super().__init__("save_probabilities_dict", num_qubits, label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) - - -def save_probabilities(self, - qubits=None, - label="probabilities", - unnormalized=False, - pershot=False, - conditional=False): + super().__init__( + "save_probabilities_dict", + num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) + + +def save_probabilities( + self, qubits=None, label="probabilities", unnormalized=False, pershot=False, conditional=False +): """Save measurement outcome probabilities vector. Args: @@ -99,20 +110,19 @@ def save_probabilities(self, QuantumCircuit: with attached instruction. """ qubits = default_qubits(self, qubits=qubits) - instr = SaveProbabilities(len(qubits), - label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveProbabilities( + len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) return self.append(instr, qubits) -def save_probabilities_dict(self, - qubits=None, - label="probabilities", - unnormalized=False, - pershot=False, - conditional=False): +def save_probabilities_dict( + self, qubits=None, label="probabilities", unnormalized=False, pershot=False, conditional=False +): """Save measurement outcome probabilities vector. Args: @@ -132,11 +142,13 @@ def save_probabilities_dict(self, QuantumCircuit: with attached instruction. """ qubits = default_qubits(self, qubits=qubits) - instr = SaveProbabilitiesDict(len(qubits), - label=label, - unnormalized=unnormalized, - pershot=pershot, - conditional=conditional) + instr = SaveProbabilitiesDict( + len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + ) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_stabilizer.py b/qiskit_aer/library/save_instructions/save_stabilizer.py index 4a1ca163f1..9ad04e3724 100644 --- a/qiskit_aer/library/save_instructions/save_stabilizer.py +++ b/qiskit_aer/library/save_instructions/save_stabilizer.py @@ -20,8 +20,8 @@ class SaveStabilizer(SaveSingleData): """Save Stabilizer instruction""" - def __init__(self, num_qubits, label="stabilizer", - pershot=False, conditional=False): + + def __init__(self, num_qubits, label="stabilizer", pershot=False, conditional=False): """Create new instruction to save the stabilizer simulator state as a StabilizerState. Args: @@ -39,9 +39,9 @@ def __init__(self, num_qubits, label="stabilizer", qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_stabilizer', num_qubits, label, - pershot=pershot, - conditional=conditional) + super().__init__( + "save_stabilizer", num_qubits, label, pershot=pershot, conditional=conditional + ) def save_stabilizer(self, label="stabilizer", pershot=False, conditional=False): @@ -63,10 +63,7 @@ def save_stabilizer(self, label="stabilizer", pershot=False, conditional=False): This instruction is always defined across all qubits in a circuit. """ qubits = default_qubits(self) - instr = SaveStabilizer(len(qubits), - label=label, - pershot=pershot, - conditional=conditional) + instr = SaveStabilizer(len(qubits), label=label, pershot=pershot, conditional=conditional) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_state.py b/qiskit_aer/library/save_instructions/save_state.py index 3e320d86ea..ab0d43ef4b 100644 --- a/qiskit_aer/library/save_instructions/save_state.py +++ b/qiskit_aer/library/save_instructions/save_state.py @@ -23,10 +23,8 @@ class SaveState(SaveSingleData): The format of the saved state depends on the simulation method used. """ - def __init__(self, num_qubits, - label=None, - pershot=False, - conditional=False): + + def __init__(self, num_qubits, label=None, pershot=False, conditional=False): """Create new instruction to save the simualtor state. The format of the saved state depends on the simulation method used. @@ -49,10 +47,8 @@ def __init__(self, num_qubits, simulation. """ if label is None: - label = '_method_' - super().__init__('save_state', num_qubits, label, - pershot=pershot, - conditional=conditional) + label = "_method_" + super().__init__("save_state", num_qubits, label, pershot=pershot, conditional=conditional) def save_state(self, label=None, pershot=False, conditional=False): @@ -76,10 +72,7 @@ def save_state(self, label=None, pershot=False, conditional=False): This instruction is always defined across all qubits in a circuit. """ qubits = default_qubits(self) - instr = SaveState(len(qubits), - label=label, - pershot=pershot, - conditional=conditional) + instr = SaveState(len(qubits), label=label, pershot=pershot, conditional=conditional) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_statevector.py b/qiskit_aer/library/save_instructions/save_statevector.py index 2358f86642..5bb112c32d 100644 --- a/qiskit_aer/library/save_instructions/save_statevector.py +++ b/qiskit_aer/library/save_instructions/save_statevector.py @@ -20,10 +20,8 @@ class SaveStatevector(SaveSingleData): """Save statevector""" - def __init__(self, num_qubits, - label="statevector", - pershot=False, - conditional=False): + + def __init__(self, num_qubits, label="statevector", pershot=False, conditional=False): """Create new instruction to save the simulator statevector. Args: @@ -41,17 +39,15 @@ def __init__(self, num_qubits, qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_statevector', num_qubits, label, - pershot=pershot, - conditional=conditional) + super().__init__( + "save_statevector", num_qubits, label, pershot=pershot, conditional=conditional + ) class SaveStatevectorDict(SaveSingleData): """Save statevector as ket-form dictionary.""" - def __init__(self, num_qubits, - label="statevector_dict", - pershot=False, - conditional=False): + + def __init__(self, num_qubits, label="statevector_dict", pershot=False, conditional=False): """Create new instruction to save the simulator statevector as a dict. Args: @@ -69,9 +65,9 @@ def __init__(self, num_qubits, qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_statevector_dict', num_qubits, label, - pershot=pershot, - conditional=conditional) + super().__init__( + "save_statevector_dict", num_qubits, label, pershot=pershot, conditional=conditional + ) def save_statevector(self, label="statevector", pershot=False, conditional=False): @@ -93,10 +89,7 @@ def save_statevector(self, label="statevector", pershot=False, conditional=False This instruction is always defined across all qubits in a circuit. """ qubits = default_qubits(self) - instr = SaveStatevector(len(qubits), - label=label, - pershot=pershot, - conditional=conditional) + instr = SaveStatevector(len(qubits), label=label, pershot=pershot, conditional=conditional) return self.append(instr, qubits) @@ -119,10 +112,7 @@ def save_statevector_dict(self, label="statevector", pershot=False, conditional= This instruction is always defined across all qubits in a circuit. """ qubits = default_qubits(self) - instr = SaveStatevectorDict(len(qubits), - label=label, - pershot=pershot, - conditional=conditional) + instr = SaveStatevectorDict(len(qubits), label=label, pershot=pershot, conditional=conditional) return self.append(instr, qubits) diff --git a/qiskit_aer/library/save_instructions/save_superop.py b/qiskit_aer/library/save_instructions/save_superop.py index d8ebca6103..d2ea183481 100644 --- a/qiskit_aer/library/save_instructions/save_superop.py +++ b/qiskit_aer/library/save_instructions/save_superop.py @@ -20,6 +20,7 @@ class SaveSuperOp(SaveSingleData): """Save a SuperOp matrix.""" + def __init__(self, num_qubits, label="superop", pershot=False): """Create new instruction to save the superop simulator state. diff --git a/qiskit_aer/library/save_instructions/save_unitary.py b/qiskit_aer/library/save_instructions/save_unitary.py index 8766852cbc..542992ca57 100644 --- a/qiskit_aer/library/save_instructions/save_unitary.py +++ b/qiskit_aer/library/save_instructions/save_unitary.py @@ -20,6 +20,7 @@ class SaveUnitary(SaveSingleData): """Save Unitary""" + def __init__(self, num_qubits, label="unitary", pershot=False): """Create new instruction to save the unitary simulator state. @@ -36,7 +37,7 @@ def __init__(self, num_qubits, label="unitary", pershot=False): qubits in a circuit, otherwise an exception will be raised during simulation. """ - super().__init__('save_unitary', num_qubits, label, pershot=pershot) + super().__init__("save_unitary", num_qubits, label, pershot=pershot) def save_unitary(self, label="unitary", pershot=False): diff --git a/qiskit_aer/library/set_instructions/set_density_matrix.py b/qiskit_aer/library/set_instructions/set_density_matrix.py index e228288440..08b013e1aa 100644 --- a/qiskit_aer/library/set_instructions/set_density_matrix.py +++ b/qiskit_aer/library/set_instructions/set_density_matrix.py @@ -43,9 +43,7 @@ def __init__(self, state): state = DensityMatrix(state) if not state.num_qubits or not state.is_valid(): raise ExtensionError("The input state is not valid") - super().__init__('set_density_matrix', - state.num_qubits, 0, - [state.data]) + super().__init__("set_density_matrix", state.num_qubits, 0, [state.data]) def set_density_matrix(self, state): @@ -73,7 +71,8 @@ def set_density_matrix(self, state): "The size of the density matrix for the set state" " instruction must be equal to the number of qubits" f" in the circuit (state.num_qubits ({state.num_qubits})" - f" != QuantumCircuit.num_qubits ({self.num_qubits})).") + f" != QuantumCircuit.num_qubits ({self.num_qubits}))." + ) return self.append(SetDensityMatrix(state), qubits) diff --git a/qiskit_aer/library/set_instructions/set_matrix_product_state.py b/qiskit_aer/library/set_instructions/set_matrix_product_state.py index 302057bc70..1546c842f1 100644 --- a/qiskit_aer/library/set_instructions/set_matrix_product_state.py +++ b/qiskit_aer/library/set_instructions/set_matrix_product_state.py @@ -38,7 +38,7 @@ def __init__(self, state): vector of pairs of matrices of complex numbers. The second is a vector of vectors of double. """ - super().__init__('set_matrix_product_state', len(state[0]), 0, [state]) + super().__init__("set_matrix_product_state", len(state[0]), 0, [state]) def set_matrix_product_state(self, state): @@ -61,19 +61,23 @@ def set_matrix_product_state(self, state): qubits = default_qubits(self) if not isinstance(state, tuple) or len(state) != 2: raise ExtensionError( - "The input matrix product state is not valid. Should be a list of 2 elements") + "The input matrix product state is not valid. Should be a list of 2 elements" + ) if not isinstance(state[0], list) or not isinstance(state[1], list): raise ExtensionError( - "The first element of the input matrix product state is not valid. Should be a list.") + "The first element of the input matrix product state is not valid. Should be a list." + ) if len(state[0]) != len(state[1]) + 1: raise ExtensionError( "The input matrix product state is not valid. " - "Length of q_reg vector should be 1 more than length of lambda_reg") + "Length of q_reg vector should be 1 more than length of lambda_reg" + ) for elem in state[0]: if not isinstance(elem, tuple) or len(elem) != 2: raise ExtensionError( "The input matrix product state is not valid." - "The first element should be a list of length 2") + "The first element should be a list of length 2" + ) return self.append(SetMatrixProductState(state), qubits) diff --git a/qiskit_aer/library/set_instructions/set_stabilizer.py b/qiskit_aer/library/set_instructions/set_stabilizer.py index bbafbc8703..7767560f41 100644 --- a/qiskit_aer/library/set_instructions/set_stabilizer.py +++ b/qiskit_aer/library/set_instructions/set_stabilizer.py @@ -40,7 +40,7 @@ def __init__(self, state): state = state.clifford elif not isinstance(state, Clifford): state = Clifford(state) - super().__init__('set_stabilizer', state.num_qubits, 0, [state.to_dict()]) + super().__init__("set_stabilizer", state.num_qubits, 0, [state.to_dict()]) def set_stabilizer(self, state): @@ -70,7 +70,8 @@ def set_stabilizer(self, state): "The size of the Clifford for the set_stabilizer" " instruction must be equal to the number of qubits" f" in the circuit (state.num_qubits ({state.num_qubits})" - f" != QuantumCircuit.num_qubits ({self.num_qubits})).") + f" != QuantumCircuit.num_qubits ({self.num_qubits}))." + ) return self.append(SetStabilizer(state), qubits) diff --git a/qiskit_aer/library/set_instructions/set_statevector.py b/qiskit_aer/library/set_instructions/set_statevector.py index a2fb6ab8d9..25603cecc5 100644 --- a/qiskit_aer/library/set_instructions/set_statevector.py +++ b/qiskit_aer/library/set_instructions/set_statevector.py @@ -43,7 +43,7 @@ def __init__(self, state): state = Statevector(state) if not state.num_qubits or not state.is_valid(): raise ExtensionError("The input statevector is not valid") - super().__init__('set_statevector', state.num_qubits, 0, [state.data]) + super().__init__("set_statevector", state.num_qubits, 0, [state.data]) def set_statevector(self, state): @@ -71,7 +71,8 @@ def set_statevector(self, state): "The size of the statevector for the set_statevector" " instruction must be equal to the number of qubits" f" in the circuit (state.num_qubits ({state.num_qubits})" - f" != QuantumCircuit.num_qubits ({self.num_qubits})).") + f" != QuantumCircuit.num_qubits ({self.num_qubits}))." + ) return self.append(SetStatevector(state), qubits) diff --git a/qiskit_aer/library/set_instructions/set_superop.py b/qiskit_aer/library/set_instructions/set_superop.py index e176834c99..0538e5f2dd 100644 --- a/qiskit_aer/library/set_instructions/set_superop.py +++ b/qiskit_aer/library/set_instructions/set_superop.py @@ -43,8 +43,7 @@ def __init__(self, state): state = SuperOp(state) if not state.num_qubits or not state.is_cptp(): raise ExtensionError("The input quantum channel is not CPTP") - super().__init__('set_superop', state.num_qubits, 0, - [state.data]) + super().__init__("set_superop", state.num_qubits, 0, [state.data]) def set_superop(self, state): @@ -73,7 +72,8 @@ def set_superop(self, state): "The size of the quantum channel for the set_superop" " instruction must be equal to the number of qubits" f" in the circuit (state.num_qubits ({state.num_qubits})" - f" != QuantumCircuit.num_qubits ({self.num_qubits})).") + f" != QuantumCircuit.num_qubits ({self.num_qubits}))." + ) return self.append(SetSuperOp(state), qubits) diff --git a/qiskit_aer/library/set_instructions/set_unitary.py b/qiskit_aer/library/set_instructions/set_unitary.py index 744923d5ac..ab8e261cdc 100644 --- a/qiskit_aer/library/set_instructions/set_unitary.py +++ b/qiskit_aer/library/set_instructions/set_unitary.py @@ -43,8 +43,7 @@ def __init__(self, state): state = Operator(state) if not state.num_qubits or not state.is_unitary(): raise ExtensionError("The input matrix is not unitary") - super().__init__('set_unitary', state.num_qubits, 0, - [state.data]) + super().__init__("set_unitary", state.num_qubits, 0, [state.data]) def set_unitary(self, state): @@ -73,7 +72,8 @@ def set_unitary(self, state): "The size of the unitary matrix for the set_unitary" " instruction must be equal to the number of qubits" f" in the circuit (state.num_qubits ({state.num_qubits})" - f" != QuantumCircuit.num_qubits ({self.num_qubits})).") + f" != QuantumCircuit.num_qubits ({self.num_qubits}))." + ) return self.append(SetUnitary(state), qubits) diff --git a/qiskit_aer/noise/__init__.py b/qiskit_aer/noise/__init__.py index dfe18526ad..b03ac45f4e 100644 --- a/qiskit_aer/noise/__init__.py +++ b/qiskit_aer/noise/__init__.py @@ -26,30 +26,72 @@ The :class:`NoiseModel` class is used to represent noise model for the :class:`~qiskit_aer.QasmSimulator`. It can be used to construct -custom noise models for simulator, to to automatically generate a basic -device noise model for an IBMQ backend. +custom noise models for simulator, to automatically generate a basic +device noise model for an IBMQ or fake backend. Device Noise Models ------------------- A simplified approximate :class:`NoiseModel` can be generated automatically -from the properties of real device backends from the IBMQ provider using the -:meth:`NoiseModel.from_backend` method. See the method documentation for -details. +from the properties of real device backends from the IBMQ provider or +fake backends of the `fake_provider` using the :meth:`NoiseModel.from_backend` +method. See the method documentation for details. + **Example: Basic device noise model** .. code-block:: python - from qiskit import QuantumCircuit, execute - from qiskit import IBMQ, Aer + from qiskit import IBMQ + from qiskit.providers.aer.noise import NoiseModel + from qiskit import QuantumCircuit, transpile + from qiskit_aer import AerSimulator from qiskit.visualization import plot_histogram from qiskit_aer.noise import NoiseModel - # Build noise model from backend properties + # Make a circuit + circ = QuantumCircuit(3, 3) + circ.h(0) + circ.cx(0, 1) + circ.cx(1, 2) + circ.measure([0, 1, 2], [0, 1, 2]) + + # Get the noise model of ibmq_lima provider = IBMQ.load_account() - backend = provider.get_backend('ibmq_vigo') + provider = IBMQ.get_provider(hub='ibm-q', group='open', project='main') + backend_lima = provider.get_backend('ibmq_lima') + noise_model = NoiseModel.from_backend(backend_lima) + + # Get coupling map from backend + coupling_map = backend_lima.configuration().coupling_map + + # Get basis gates from noise model + basis_gates = noise_model.basis_gates + + # Perform a noise simulation + backend = AerSimulator(noise_model=noise_model, + coupling_map=coupling_map, + basis_gates=basis_gates) + transpiled_circuit = transpile(circ, backend) + result = backend.run(transpiled_circuit).result() + + counts = result.get_counts(0) + plot_histogram(counts) + + +**Example: Basic device noise model using a `fake_provider` backend** + +.. code-block:: python + + from qiskit import QuantumCircuit, transpile + from qiskit_aer import AerSimulator + from qiskit.visualization import plot_histogram + from qiskit_aer.noise import NoiseModel + from qiskit.providers.fake_provider import FakeVigo + + # Build noise model from backend properties + backend = FakeVigo() noise_model = NoiseModel.from_backend(backend) # Get coupling map from backend @@ -66,10 +108,12 @@ circ.measure([0, 1, 2], [0, 1, 2]) # Perform a noise simulation - result = execute(circ, Aer.get_backend('qasm_simulator'), - coupling_map=coupling_map, - basis_gates=basis_gates, - noise_model=noise_model).result() + backend = AerSimulator(noise_model=noise_model, + coupling_map=coupling_map, + basis_gates=basis_gates) + transpiled_circuit = transpile(circ, backend) + result = backend.run(transpiled_circuit).result() + counts = result.get_counts(0) plot_histogram(counts) @@ -88,8 +132,9 @@ .. code-block:: python - from qiskit import QuantumCircuit, execute, Aer + from qiskit import QuantumCircuit, transpile, Aer from qiskit.visualization import plot_histogram + from qiskit_aer import AerSimulator import qiskit_aer.noise as noise # Error probabilities @@ -116,12 +161,16 @@ circ.measure([0, 1, 2], [0, 1, 2]) # Perform a noise simulation - result = execute(circ, Aer.get_backend('qasm_simulator'), - basis_gates=basis_gates, - noise_model=noise_model).result() + backend = AerSimulator(noise_model=noise_model, + coupling_map=coupling_map, + basis_gates=basis_gates) + transpiled_circuit = transpile(circ, backend) + result = backend.run(transpiled_circuit).result() + counts = result.get_counts(0) plot_histogram(counts) + Classes ======= diff --git a/qiskit_aer/noise/device/models.py b/qiskit_aer/noise/device/models.py index 657e74c8ec..528a9ffa1c 100644 --- a/qiskit_aer/noise/device/models.py +++ b/qiskit_aer/noise/device/models.py @@ -79,14 +79,16 @@ def basic_device_readout_errors(properties=None, target=None): return errors -def basic_device_gate_errors(properties=None, - gate_error=True, - thermal_relaxation=True, - gate_lengths=None, - gate_length_units='ns', - temperature=0, - warnings=None, - target=None): +def basic_device_gate_errors( + properties=None, + gate_error=True, + thermal_relaxation=True, + gate_lengths=None, + gate_length_units="ns", + temperature=0, + warnings=None, + target=None, +): """ Return QuantumErrors derived from either of a devices BackendProperties or Target. @@ -104,21 +106,26 @@ def basic_device_gate_errors(properties=None, Args: properties (BackendProperties): device backend properties. gate_error (bool): Include depolarizing gate errors (Default: True). - thermal_relaxation (Bool): Include thermal relaxation errors - (Default: True). + thermal_relaxation (Bool): Include thermal relaxation errors (Default: True). + If no ``t1`` and ``t2`` values are provided (i.e. None) in ``target`` for a qubit, + an identity ``QuantumError` (i.e. effectively no thermal relaxation error) + will be added to the qubit even if this flag is set to True. + If no ``frequency`` is not defined (i.e. None) in ``target`` for a qubit, + no excitation is considered in the thermal relaxation error on the qubit + even with non-zero ``temperature``. gate_lengths (list): Override device gate times with custom values. If None use gate times from backend properties. (Default: None). - gate_length_units (str): Time units for gate length values in gate_lengths. + gate_length_units (str): Time units for gate length values in ``gate_lengths``. Can be 'ns', 'ms', 'us', or 's' (Default: 'ns'). temperature (double): qubit temperature in milli-Kelvin (mK) (Default: 0). warnings (bool): DEPRECATED, Display warnings (Default: None). target (Target): device backend target (Default: None). When this is supplied, several options are disabled: - `properties`, `gate_lengths` and `gate_length_units` are not used + ``properties``, ``gate_lengths`` and ``gate_length_units`` are not used during the construction of gate errors. - Default values are always used for `warnings`. + Default values are always used for ``warnings``. Returns: list: A list of tuples ``(label, qubits, QuantumError)``, for gates @@ -134,26 +141,33 @@ def basic_device_gate_errors(properties=None, if warnings is not None: warn( '"warnings" argument has been deprecated as of qiskit-aer 0.12.0 ' - 'and will be removed no earlier than 3 months from that release date. ' - 'Use the warnings filter in Python standard library instead.', - DeprecationWarning, stacklevel=2) + "and will be removed no earlier than 3 months from that release date. " + "Use the warnings filter in Python standard library instead.", + DeprecationWarning, + stacklevel=2, + ) else: warnings = True if target is not None: if not warnings: - warn("When `target` is supplied, `warnings` are ignored," - " and they are always set to true.", UserWarning) + warn( + "When `target` is supplied, `warnings` are ignored," + " and they are always set to true.", + UserWarning, + ) if gate_lengths: - raise NoiseError("When `target` is supplied, `gate_lengths` option is not allowed." - "Use `duration` property in target's InstructionProperties instead.") + raise NoiseError( + "When `target` is supplied, `gate_lengths` option is not allowed." + "Use `duration` property in target's InstructionProperties instead." + ) return _basic_device_target_gate_errors( target=target, gate_error=gate_error, thermal_relaxation=thermal_relaxation, - temperature=temperature + temperature=temperature, ) # Generate custom gate time dict @@ -186,23 +200,19 @@ def basic_device_gate_errors(properties=None, relax_time = gate_length # Override with custom value if name in custom_times: - filtered = [ - val for q, val in custom_times[name] - if q is None or q == qubits - ] + filtered = [val for q, val in custom_times[name] if q is None or q == qubits] if filtered: # get first value relax_time = filtered[0] # Get relaxation error if thermal_relaxation: relax_error = _device_thermal_relaxation_error( - qubits, relax_time, relax_params, temperature, - thermal_relaxation) + qubits, relax_time, relax_params, temperature, thermal_relaxation + ) # Get depolarizing error channel if gate_error: - depol_error = _device_depolarizing_error( - qubits, error_param, relax_error) + depol_error = _device_depolarizing_error(qubits, error_param, relax_error) # Combine errors combined_error = _combine_depol_and_relax_error(depol_error, relax_error) @@ -222,10 +232,9 @@ def _combine_depol_and_relax_error(depol_error, relax_error): return None -def _basic_device_target_gate_errors(target, - gate_error=True, - thermal_relaxation=True, - temperature=0): +def _basic_device_target_gate_errors( + target, gate_error=True, thermal_relaxation=True, temperature=0 +): """Return QuantumErrors derived from a devices Target. Note that, in the resulting error list, non-Gate instructions (e.g. Reset) will have no gate errors while they may have thermal relaxation errors. Exceptionally, @@ -245,10 +254,14 @@ def _basic_device_target_gate_errors(target, relax_error = None # Get relaxation error if thermal_relaxation and inst_prop.duration: - relax_params = {q: (target.qubit_properties[q].t1, - target.qubit_properties[q].t2, - target.qubit_properties[q].frequency) - for q in qubits} + relax_params = { + q: ( + target.qubit_properties[q].t1, + target.qubit_properties[q].t2, + target.qubit_properties[q].frequency, + ) + for q in qubits + } relax_error = _device_thermal_relaxation_error( qubits=qubits, gate_time=inst_prop.duration, @@ -270,9 +283,7 @@ def _basic_device_target_gate_errors(target, return errors -def _device_depolarizing_error(qubits, - error_param, - relax_error=None): +def _device_depolarizing_error(qubits, error_param, relax_error=None): """Construct a depolarizing_error for device. If un-physical parameters are supplied, they are truncated to the theoretical bound values.""" @@ -301,7 +312,7 @@ def _device_depolarizing_error(qubits, relax_infid = 0 if error_param is not None and error_param > relax_infid: num_qubits = len(qubits) - dim = 2 ** num_qubits + dim = 2**num_qubits error_max = dim / (dim + 1) # Check if reported error param is un-physical # The minimum average gate fidelity is F_min = 1 / (dim + 1) @@ -309,7 +320,7 @@ def _device_depolarizing_error(qubits, error_param = min(error_param, error_max) # Model gate error entirely as depolarizing error num_qubits = len(qubits) - dim = 2 ** num_qubits + dim = 2**num_qubits depol_param = dim * (error_param - relax_infid) / (dim * relax_fid - 1) max_param = 4**num_qubits / (4**num_qubits - 1) if depol_param > max_param: @@ -318,11 +329,9 @@ def _device_depolarizing_error(qubits, return None -def _device_thermal_relaxation_error(qubits, - gate_time, - relax_params, - temperature, - thermal_relaxation=True): +def _device_thermal_relaxation_error( + qubits, gate_time, relax_params, temperature, thermal_relaxation=True +): """Construct a thermal_relaxation_error for device""" # Check trivial case if not thermal_relaxation or gate_time is None or gate_time == 0: @@ -335,6 +344,10 @@ def _device_thermal_relaxation_error(qubits, for qubit in qubits: t1, t2, freq = relax_params[qubit] t2 = _truncate_t2_value(t1, t2) + if t1 is None: + t1 = inf + if t2 is None: + t2 = inf population = _excited_population(freq, temperature) if first: error = thermal_relaxation_error(t1, t2, gate_time, population) @@ -347,14 +360,15 @@ def _device_thermal_relaxation_error(qubits, def _truncate_t2_value(t1, t2): """Return t2 value truncated to 2 * t1 (for t2 > 2 * t1)""" - new_t2 = t2 - if t2 > 2 * t1: - new_t2 = 2 * t1 - return new_t2 + if t1 is None or t2 is None: + return t2 + return min(t2, 2 * t1) def _excited_population(freq, temperature): """Return excited state population from freq [GHz] and temperature [mK].""" + if freq is None or temperature is None: + return 0 population = 0 if freq != inf and temperature != 0: # Compute the excited state population from qubit frequency and temperature diff --git a/qiskit_aer/noise/device/parameters.py b/qiskit_aer/noise/device/parameters.py index 88e8fcb473..04cf0df41a 100644 --- a/qiskit_aer/noise/device/parameters.py +++ b/qiskit_aer/noise/device/parameters.py @@ -17,8 +17,8 @@ from numpy import inf # Time and frequency unit conversions -_NANOSECOND_UNITS = {'s': 1e9, 'ms': 1e6, 'µs': 1e3, 'us': 1e3, 'ns': 1} -_GHZ_UNITS = {'Hz': 1e-9, 'KHz': 1e-6, 'MHz': 1e-3, 'GHz': 1, 'THz': 1e3} +_NANOSECOND_UNITS = {"s": 1e9, "ms": 1e6, "µs": 1e3, "us": 1e3, "ns": 1} +_GHZ_UNITS = {"Hz": 1e-9, "KHz": 1e-6, "MHz": 1e-3, "GHz": 1, "THz": 1e3} def gate_param_values(properties): @@ -39,16 +39,16 @@ def gate_param_values(properties): qubits = gate.qubits # Check for gate time information gate_length = None # default value - time_param = _check_for_item(gate.parameters, 'gate_length') - if hasattr(time_param, 'value'): + time_param = _check_for_item(gate.parameters, "gate_length") + if hasattr(time_param, "value"): gate_length = time_param.value - if hasattr(time_param, 'unit'): + if hasattr(time_param, "unit"): # Convert gate time to ns gate_length *= _NANOSECOND_UNITS.get(time_param.unit, 1) # Check for gate error information gate_error = None # default value - error_param = _check_for_item(gate.parameters, 'gate_error') - if hasattr(error_param, 'value'): + error_param = _check_for_item(gate.parameters, "gate_error") + if hasattr(error_param, "value"): gate_error = error_param.value values.append((name, qubits, gate_length, gate_error)) @@ -72,8 +72,8 @@ def gate_error_values(properties): name = gate.gate qubits = gate.qubits value = None # default value - params = _check_for_item(gate.parameters, 'gate_error') - if hasattr(params, 'value'): + params = _check_for_item(gate.parameters, "gate_error") + if hasattr(params, "value"): value = params.value values.append((name, qubits, value)) return values @@ -98,10 +98,10 @@ def gate_length_values(properties): name = gate.gate qubits = gate.qubits value = None # default value - params = _check_for_item(gate.parameters, 'gate_length') - if hasattr(params, 'value'): + params = _check_for_item(gate.parameters, "gate_length") + if hasattr(params, "value"): value = params.value - if hasattr(params, 'unit'): + if hasattr(params, "unit"): # Convert gate time to ns value *= _NANOSECOND_UNITS.get(params.unit, 1) values.append((name, qubits, value)) @@ -124,13 +124,13 @@ def readout_error_values(properties): for qubit_props in properties.qubits: value = None # default value - params_roerror = _check_for_item(qubit_props, 'readout_error') - params_m1p0 = _check_for_item(qubit_props, 'prob_meas1_prep0') - params_m0p1 = _check_for_item(qubit_props, 'prob_meas0_prep1') + params_roerror = _check_for_item(qubit_props, "readout_error") + params_m1p0 = _check_for_item(qubit_props, "prob_meas1_prep0") + params_m0p1 = _check_for_item(qubit_props, "prob_meas0_prep1") - if hasattr(params_m1p0, 'value') and hasattr(params_m0p1, 'value'): + if hasattr(params_m1p0, "value") and hasattr(params_m0p1, "value"): value = [params_m1p0.value, params_m0p1.value] - elif hasattr(params_roerror, 'value'): + elif hasattr(params_roerror, "value"): value = [params_roerror.value, params_roerror.value] values.append(value) return values @@ -161,24 +161,24 @@ def thermal_relaxation_values(properties): t1, t2, freq = inf, inf, inf # Get the readout error value - t1_params = _check_for_item(qubit_props, 'T1') - t2_params = _check_for_item(qubit_props, 'T2') - freq_params = _check_for_item(qubit_props, 'frequency') + t1_params = _check_for_item(qubit_props, "T1") + t2_params = _check_for_item(qubit_props, "T2") + freq_params = _check_for_item(qubit_props, "frequency") # Load values from parameters - if hasattr(t1_params, 'value'): + if hasattr(t1_params, "value"): t1 = t1_params.value - if hasattr(t1_params, 'unit'): + if hasattr(t1_params, "unit"): # Convert to nanoseconds t1 *= _NANOSECOND_UNITS.get(t1_params.unit, 1) - if hasattr(t2_params, 'value'): + if hasattr(t2_params, "value"): t2 = t2_params.value - if hasattr(t2_params, 'unit'): + if hasattr(t2_params, "unit"): # Convert to nanoseconds t2 *= _NANOSECOND_UNITS.get(t2_params.unit, 1) - if hasattr(freq_params, 'value'): + if hasattr(freq_params, "value"): freq = freq_params.value - if hasattr(freq_params, 'unit'): + if hasattr(freq_params, "unit"): # Convert to Gigahertz freq *= _GHZ_UNITS.get(freq_params.unit, 1) diff --git a/qiskit_aer/noise/errors/quantum_error.py b/qiskit_aer/noise/errors/quantum_error.py index 5d8efd335c..59d65c469f 100644 --- a/qiskit_aer/noise/errors/quantum_error.py +++ b/qiskit_aer/noise/errors/quantum_error.py @@ -44,8 +44,7 @@ class QuantumError(BaseOperator, TolerancesMixin): module. """ - def __init__(self, - noise_ops): + def __init__(self, noise_ops): """ Create a quantum error for a noise model. @@ -102,8 +101,9 @@ def __init__(self, return # Single circuit case - if not isinstance(noise_ops, Iterable) or \ - (isinstance(noise_ops, tuple) and isinstance(noise_ops[0], Instruction)): + if not isinstance(noise_ops, Iterable) or ( + isinstance(noise_ops, tuple) and isinstance(noise_ops[0], Instruction) + ): noise_ops = [(noise_ops, 1.0)] # Convert zipped object to list (to enable multiple iteration over it) @@ -180,7 +180,7 @@ def _to_circuit(cls, op): f"Fail to convert {op.__class__.__name__} to Instruction." ) from err if isinstance(op, BaseOperator): - if hasattr(op, 'to_instruction'): + if hasattr(op, "to_instruction"): try: return cls._to_circuit(op.to_instruction()) except QiskitError as err: @@ -231,7 +231,7 @@ def __hash__(self): return hash(self._id) @property - def id(self): # pylint: disable=invalid-name + def id(self): # pylint: disable=invalid-name """Return unique ID string for error""" return self._id @@ -273,15 +273,16 @@ def ideal(self): if isinstance(op, IGate): continue if isinstance(op, PauliGate): - if op.params[0].replace('I', ''): + if op.params[0].replace("I", ""): return False else: # Convert to Kraus and check if identity kmats = Kraus(op).data if len(kmats) > 1: return False - if not is_identity_matrix(kmats[0], ignore_phase=True, - atol=self.atol, rtol=self.rtol): + if not is_identity_matrix( + kmats[0], ignore_phase=True, atol=self.atol, rtol=self.rtol + ): return False return True @@ -289,7 +290,7 @@ def to_quantumchannel(self): """Convert the QuantumError to a SuperOp quantum channel. Required to enable SuperOp(QuantumError).""" # Initialize as an empty superoperator of the correct size - dim = 2 ** self.num_qubits + dim = 2**self.num_qubits ret = SuperOp(np.zeros([dim * dim, dim * dim])) for circ, prob in zip(self.circuits, self.probabilities): component = prob * SuperOp(circ) @@ -340,7 +341,7 @@ def to_dict(self): "id": self.id, "operations": [], "instructions": instructions, - "probabilities": list(self.probabilities) + "probabilities": list(self.probabilities), } return error @@ -349,22 +350,26 @@ def compose(self, other, qargs=None, front=False): other = QuantumError(other) if qargs is not None: if self.num_qubits < other.num_qubits: - raise QiskitError("Number of qubits of this error must be less than" - " that of the error to be composed if using 'qargs' argument.") + raise QiskitError( + "Number of qubits of this error must be less than" + " that of the error to be composed if using 'qargs' argument." + ) if len(qargs) != other.num_qubits: - raise QiskitError("Number of items in 'qargs' argument must be the same as" - " number of qubits of the error to be composed.") + raise QiskitError( + "Number of items in 'qargs' argument must be the same as" + " number of qubits of the error to be composed." + ) if front: raise QiskitError( "QuantumError.compose does not support 'qargs' when 'front=True'." ) - circs = [self._compose_circ(lqc, rqc, qubits=qargs, front=front) - for lqc in self.circuits - for rqc in other.circuits] - probs = [lpr * rpr - for lpr in self.probabilities - for rpr in other.probabilities] + circs = [ + self._compose_circ(lqc, rqc, qubits=qargs, front=front) + for lqc in self.circuits + for rqc in other.circuits + ] + probs = [lpr * rpr for lpr in self.probabilities for rpr in other.probabilities] return QuantumError(zip(circs, probs)) @staticmethod @@ -389,12 +394,8 @@ def tensor(self, other): if not isinstance(other, QuantumError): other = QuantumError(other) - circs = [lqc.tensor(rqc) - for lqc in self.circuits - for rqc in other.circuits] - probs = [lpr * rpr - for lpr in self.probabilities - for rpr in other.probabilities] + circs = [lqc.tensor(rqc) for lqc in self.circuits for rqc in other.circuits] + probs = [lpr * rpr for lpr in self.probabilities for rpr in other.probabilities] return QuantumError(zip(circs, probs)) def expand(self, other): diff --git a/qiskit_aer/noise/errors/readout_error.py b/qiskit_aer/noise/errors/readout_error.py index f84661d494..8374e6736c 100644 --- a/qiskit_aer/noise/errors/readout_error.py +++ b/qiskit_aer/noise/errors/readout_error.py @@ -27,6 +27,7 @@ class ReadoutError: """ Readout error class for Qiskit Aer noise model. """ + # pylint: disable=invalid-name _ATOL_DEFAULT = ATOL_DEFAULT _RTOL_DEFAULT = RTOL_DEFAULT @@ -82,8 +83,10 @@ def __repr__(self): def __str__(self): """Print error information.""" - output = "ReadoutError on {} qubits.".format(self._number_of_qubits) + \ - " Assignment probabilities:" + output = ( + "ReadoutError on {} qubits.".format(self._number_of_qubits) + + " Assignment probabilities:" + ) for j, vec in enumerate(self._probabilities): output += "\n P(j|{0}) = {1}".format(j, vec) return output @@ -94,8 +97,9 @@ def __eq__(self, other): return False if self.number_of_qubits != other.number_of_qubits: return False - return np.allclose(self._probabilities, other._probabilities, - atol=self.atol, rtol=self.rtol) + return np.allclose( + self._probabilities, other._probabilities, atol=self.atol, rtol=self.rtol + ) def copy(self): """Make a copy of current ReadoutError.""" @@ -127,24 +131,18 @@ def rtol(self): def set_atol(cls, value): """Set the class default absolute tolerance parameter for float comparisons.""" if value < 0: - raise NoiseError( - "Invalid atol ({}) must be non-negative.".format(value)) + raise NoiseError("Invalid atol ({}) must be non-negative.".format(value)) if value > cls._MAX_TOL: - raise NoiseError( - "Invalid atol ({}) must be less than {}.".format( - value, cls._MAX_TOL)) + raise NoiseError("Invalid atol ({}) must be less than {}.".format(value, cls._MAX_TOL)) cls._ATOL_DEFAULT = value @classmethod def set_rtol(cls, value): """Set the class default relative tolerance parameter for float comparisons.""" if value < 0: - raise NoiseError( - "Invalid rtol ({}) must be non-negative.".format(value)) + raise NoiseError("Invalid rtol ({}) must be non-negative.".format(value)) if value > cls._MAX_TOL: - raise NoiseError( - "Invalid rtol ({}) must be less than {}.".format( - value, cls._MAX_TOL)) + raise NoiseError("Invalid rtol ({}) must be less than {}.".format(value, cls._MAX_TOL)) cls._RTOL_DEFAULT = value def ideal(self): @@ -164,7 +162,7 @@ def to_dict(self): error = { "type": "roerror", "operations": ["measure"], - "probabilities": self._probabilities.tolist() + "probabilities": self._probabilities.tolist(), } return error @@ -256,28 +254,30 @@ def expand(self, other): def _check_probabilities(probabilities, threshold): """Check probabilities are valid.""" # 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): + 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)) if 2**num_qubits != num_outcomes: - raise NoiseError("Invalid probabilities: length " - "{} != 2**{}".format(num_outcomes, num_qubits)) + raise NoiseError( + "Invalid probabilities: length " "{} != 2**{}".format(num_outcomes, num_qubits) + ) if len(probabilities) != num_outcomes: raise NoiseError("Invalid probabilities.") for vec in probabilities: arr = np.array(vec) if len(arr) != num_outcomes: - raise NoiseError( - "Invalid probabilities: vectors are different lengths.") + raise NoiseError("Invalid probabilities: vectors are different lengths.") if abs(sum(arr) - 1) > threshold: - raise NoiseError("Invalid probabilities: sum({})= {} " - "is not 1.".format(vec, sum(arr))) + raise NoiseError( + "Invalid probabilities: sum({})= {} " "is not 1.".format(vec, sum(arr)) + ) if arr[arr < 0].size > 0: raise NoiseError( - "Invalid probabilities: {} " - "contains a negative probability.".format(vec)) + "Invalid probabilities: {} " "contains a negative probability.".format(vec) + ) def _matmul(self, other, left_multiply=False): """Return the composition readout error. @@ -335,8 +335,7 @@ def __xor__(self, other): return self.tensor(other) def __rmul__(self, other): - raise NotImplementedError( - "'ReadoutError' does not support scalar multiplication.") + raise NotImplementedError("'ReadoutError' does not support scalar multiplication.") def __truediv__(self, other): raise NotImplementedError("'ReadoutError' does not support division.") @@ -345,8 +344,7 @@ def __add__(self, other): raise NotImplementedError("'ReadoutError' does not support addition.") def __sub__(self, other): - raise NotImplementedError( - "'ReadoutError' does not support subtraction.") + raise NotImplementedError("'ReadoutError' does not support subtraction.") def __neg__(self): raise NotImplementedError("'ReadoutError' does not support negation.") diff --git a/qiskit_aer/noise/errors/standard_errors.py b/qiskit_aer/noise/errors/standard_errors.py index cbb01e8667..c9d85eddf4 100644 --- a/qiskit_aer/noise/errors/standard_errors.py +++ b/qiskit_aer/noise/errors/standard_errors.py @@ -83,7 +83,7 @@ def mixed_unitary_error(noise_ops): raise NoiseError("Input noise list is empty.") # Check for identity unitaries - prob_identity = 0. + prob_identity = 0.0 instructions = [] instructions_probs = [] num_qubits = int(np.log2(noise_ops[0][0].shape[0])) @@ -179,7 +179,7 @@ def depolarizing_error(param, num_qubits): with :math:`0 \le λ \le 4^n / (4^n - 1)` - where :math:`λ` is the depolarizing error param and :math`n` is the + where :math:`λ` is the depolarizing error param and :math:`n` is the number of qubits. * If :math:`λ = 0` this is the identity channel :math:`E(ρ) = ρ` @@ -202,11 +202,10 @@ def depolarizing_error(param, num_qubits): 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 + 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)) + raise NoiseError("Depolarizing parameter must be in between 0 " "and {}.".format(max_param)) # Rescale completely depolarizing channel error probs # with the identity component removed @@ -216,7 +215,7 @@ def depolarizing_error(param, num_qubits): # Generate pauli strings. The order doesn't matter as long # as the all identity string is first. - paulis = [Pauli("".join(tup)) for tup in it.product(['I', 'X', 'Y', 'Z'], repeat=num_qubits)] + paulis = [Pauli("".join(tup)) for tup in it.product(["I", "X", "Y", "Z"], repeat=num_qubits)] return QuantumError(zip(paulis, probs)) @@ -249,7 +248,7 @@ def reset_error(prob0, prob1=0): noise_ops = [ ([(IGate(), [0])], 1 - prob0 - prob1), ([(Reset(), [0])], prob0), - ([(Reset(), [0]), (XGate(), [0])], prob1) + ([(Reset(), [0]), (XGate(), [0])], prob1), ] return QuantumError(noise_ops) @@ -283,11 +282,13 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): general non-unitary Kraus error channel. """ if excited_state_population < 0: - raise NoiseError("Invalid excited state population " - "({} < 0).".format(excited_state_population)) + raise NoiseError( + "Invalid excited state population " "({} < 0).".format(excited_state_population) + ) if excited_state_population > 1: - raise NoiseError("Invalid excited state population " - "({} > 1).".format(excited_state_population)) + raise NoiseError( + "Invalid excited state population " "({} > 1).".format(excited_state_population) + ) if time < 0: raise NoiseError("Invalid gate_time ({} < 0)".format(time)) if t1 <= 0: @@ -295,8 +296,7 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): if t2 <= 0: raise NoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.") if t2 - 2 * t1 > 0: - raise NoiseError( - "Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.") + raise NoiseError("Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.") # T1 relaxation rate if t1 == np.inf: @@ -320,9 +320,15 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): # If T_2 > T_1 we must express this as a Kraus channel # We start with the Choi-matrix representation: chan = Choi( - np.array([[1 - p1 * p_reset, 0, 0, exp_t2], - [0, p1 * p_reset, 0, 0], [0, 0, p0 * p_reset, 0], - [exp_t2, 0, 0, 1 - p0 * p_reset]])) + np.array( + [ + [1 - p1 * p_reset, 0, 0, exp_t2], + [0, p1 * p_reset, 0, 0], + [0, 0, p0 * p_reset, 0], + [exp_t2, 0, 0, 1 - p0 * p_reset], + ] + ) + ) return QuantumError(Kraus(chan)) else: # If T_2 < T_1 we can express this channel as a probabilistic @@ -331,7 +337,7 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): [(IGate(), [0])], [(ZGate(), [0])], [(Reset(), [0])], - [(Reset(), [0]), (XGate(), [0])] + [(Reset(), [0]), (XGate(), [0])], ] # Probability p_reset0 = p_reset * p0 @@ -342,10 +348,9 @@ def thermal_relaxation_error(t1, t2, time, excited_state_population=0): return QuantumError(zip(circuits, probabilities)) -def phase_amplitude_damping_error(param_amp, - param_phase, - excited_state_population=0, - canonical_kraus=True): +def phase_amplitude_damping_error( + param_amp, param_phase, excited_state_population=0, canonical_kraus=True +): r""" Return a single-qubit combined phase and amplitude damping quantum error channel. @@ -385,20 +390,22 @@ def phase_amplitude_damping_error(param_amp, """ if param_amp < 0: - raise NoiseError("Invalid amplitude damping to |0> parameter " - "({} < 0)".format(param_amp)) + raise NoiseError("Invalid amplitude damping to |0> parameter " "({} < 0)".format(param_amp)) if param_phase < 0: - raise NoiseError("Invalid phase damping parameter " - "({} < 0)".format(param_phase)) + raise NoiseError("Invalid phase damping parameter " "({} < 0)".format(param_phase)) if param_phase + param_amp > 1: - raise NoiseError("Invalid amplitude and phase damping parameters " - "({} + {} > 1)".format(param_phase, param_amp)) + raise NoiseError( + "Invalid amplitude and phase damping parameters " + "({} + {} > 1)".format(param_phase, param_amp) + ) if excited_state_population < 0: - raise NoiseError("Invalid excited state population " - "({} < 0).".format(excited_state_population)) + raise NoiseError( + "Invalid excited state population " "({} < 0).".format(excited_state_population) + ) if excited_state_population > 1: - raise NoiseError("Invalid excited state population " - "({} > 1).".format(excited_state_population)) + raise NoiseError( + "Invalid excited state population " "({} > 1).".format(excited_state_population) + ) c0 = np.sqrt(1 - excited_state_population) c1 = np.sqrt(excited_state_population) param = 1 - param_amp - param_phase @@ -411,15 +418,11 @@ def phase_amplitude_damping_error(param_amp, B1 = c1 * np.array([[0, 0], [np.sqrt(param_amp), 0]], dtype=complex) B2 = c1 * np.array([[np.sqrt(param_phase), 0], [0, 0]], dtype=complex) # Select non-zero ops - noise_ops = [ - a for a in [A0, A1, A2, B0, B1, B2] if np.linalg.norm(a) > 1e-10 - ] + noise_ops = [a for a in [A0, A1, A2, B0, B1, B2] if np.linalg.norm(a) > 1e-10] return kraus_error(noise_ops, canonical_kraus=canonical_kraus) -def amplitude_damping_error(param_amp, - excited_state_population=0, - canonical_kraus=True): +def amplitude_damping_error(param_amp, excited_state_population=0, canonical_kraus=True): r""" Return a single-qubit generalized amplitude damping quantum error channel. @@ -455,7 +458,8 @@ def amplitude_damping_error(param_amp, param_amp, 0, excited_state_population=excited_state_population, - canonical_kraus=canonical_kraus) + canonical_kraus=canonical_kraus, + ) def phase_damping_error(param_phase, canonical_kraus=True): @@ -490,7 +494,5 @@ def phase_damping_error(param_phase, canonical_kraus=True): """ return phase_amplitude_damping_error( - 0, - param_phase, - excited_state_population=0, - canonical_kraus=canonical_kraus) + 0, param_phase, excited_state_population=0, canonical_kraus=canonical_kraus + ) diff --git a/qiskit_aer/noise/noise_model.py b/qiskit_aer/noise/noise_model.py index fa2aa6f11d..a32fcc2020 100644 --- a/qiskit_aer/noise/noise_model.py +++ b/qiskit_aer/noise/noise_model.py @@ -129,14 +129,52 @@ class NoiseModel: print(noise_model) """ + # Checks for standard 1-3 qubit instructions - _1qubit_instructions = set([ - 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', - 'y', 'z', 'h', 's', 'sdg', 'sx', 'sxdg', 't', 'tdg']) - _2qubit_instructions = set([ - 'swap', 'cx', 'cy', 'cz', 'csx', 'cp', 'cu', 'cu1', 'cu2', 'cu3', 'rxx', - 'ryy', 'rzz', 'rzx', 'ecr']) - _3qubit_instructions = set(['ccx', 'cswap']) + _1qubit_instructions = set( + [ + "u1", + "u2", + "u3", + "u", + "p", + "r", + "rx", + "ry", + "rz", + "id", + "x", + "y", + "z", + "h", + "s", + "sdg", + "sx", + "sxdg", + "t", + "tdg", + ] + ) + _2qubit_instructions = set( + [ + "swap", + "cx", + "cy", + "cz", + "csx", + "cp", + "cu", + "cu1", + "cu2", + "cu3", + "rxx", + "ryy", + "rzz", + "rzx", + "ecr", + ] + ) + _3qubit_instructions = set(["ccx", "cswap"]) def __init__(self, basis_gates=None): """Initialize an empty noise model. @@ -160,10 +198,9 @@ def __init__(self, basis_gates=None): # Default basis gates is id, rz, sx, cx so that all standard # non-identity instructions can be unrolled to rz, sx, cx, # and identities won't be unrolled - self._basis_gates = set(['id', 'rz', 'sx', 'cx']) + self._basis_gates = set(["id", "rz", "sx", "cx"]) else: - self._basis_gates = set( - name for name, _ in self._instruction_names_labels(basis_gates)) + self._basis_gates = set(name for name, _ in self._instruction_names_labels(basis_gates)) # Store gates with a noise model defined self._noise_instructions = set() # Store qubits referenced in noise model. @@ -205,14 +242,17 @@ def noise_qubits(self): return sorted(self._noise_qubits) @classmethod - def from_backend(cls, backend, - gate_error=True, - readout_error=True, - thermal_relaxation=True, - temperature=0, - gate_lengths=None, - gate_length_units='ns', - warnings=None): + def from_backend( + cls, + backend, + gate_error=True, + readout_error=True, + thermal_relaxation=True, + temperature=0, + gate_lengths=None, + gate_length_units="ns", + warnings=None, + ): """Return a noise model derived from a devices backend properties. This function generates a noise model based on: @@ -305,9 +345,11 @@ def from_backend(cls, backend, if warnings is not None: warn( '"warnings" argument has been deprecated as of qiskit-aer 0.12.0 ' - 'and will be removed no earlier than 3 months from that release date. ' - 'Use the warnings filter in Python standard library instead.', - DeprecationWarning, stacklevel=2) + "and will be removed no earlier than 3 months from that release date. " + "Use the warnings filter in Python standard library instead.", + DeprecationWarning, + stacklevel=2, + ) else: warnings = True @@ -318,8 +360,11 @@ def from_backend(cls, backend, target = None if backend_interface_version == 2: if not warnings: - warn("When a BackendV2 is supplied, `warnings`" - " are ignored, and their default values are used.", UserWarning) + warn( + "When a BackendV2 is supplied, `warnings`" + " are ignored, and their default values are used.", + UserWarning, + ) properties = None basis_gates = backend.operation_names target = backend.target @@ -332,22 +377,27 @@ def from_backend(cls, backend, target.update_instruction_properties(op_name, qubits, prop) all_qubit_properties = backend.target.qubit_properties if not all_qubit_properties: - warn(f"Qiskit backend {backend} has no QubitProperties, so the resulting" - " noise model will not include any thermal relaxation errors.", UserWarning) + warn( + f"Qiskit backend {backend} has no QubitProperties, so the resulting" + " noise model will not include any thermal relaxation errors.", + UserWarning, + ) dt = backend.dt elif backend_interface_version <= 1: properties = backend.properties() configuration = backend.configuration() basis_gates = configuration.basis_gates - all_qubit_properties = [QubitProperties(t1=properties.t1(q), - t2=properties.t2(q), - frequency=properties.frequency(q)) - for q in range(configuration.num_qubits)] + all_qubit_properties = [ + QubitProperties( + t1=properties.t1(q), t2=properties.t2(q), frequency=properties.frequency(q) + ) + for q in range(configuration.num_qubits) + ] dt = getattr(configuration, "dt", 0) if not properties: - raise NoiseError(f'Qiskit backend {backend} does not have a BackendProperties') + raise NoiseError(f"Qiskit backend {backend} does not have a BackendProperties") else: - raise NoiseError(f'{backend} is not a Qiskit backend') + raise NoiseError(f"{backend} is not a Qiskit backend") noise_model = NoiseModel(basis_gates=basis_gates) @@ -358,11 +408,7 @@ def from_backend(cls, backend, # Add gate errors with catch_warnings(): - filterwarnings( - "ignore", - category=DeprecationWarning, - module="qiskit_aer.noise" - ) + filterwarnings("ignore", category=DeprecationWarning, module="qiskit_aer.noise") gate_errors = basic_device_gate_errors( properties, gate_error=gate_error, @@ -380,20 +426,20 @@ def from_backend(cls, backend, # Add delay errors via RelaxationNiose pass try: excited_state_populations = [ - _excited_population( - freq=q.frequency, temperature=temperature - ) for q in all_qubit_properties] + _excited_population(freq=q.frequency, temperature=temperature) + for q in all_qubit_properties + ] except BackendPropertyError: excited_state_populations = None try: t1s = [prop.t1 for prop in all_qubit_properties] t2s = [_truncate_t2_value(prop.t1, prop.t2) for prop in all_qubit_properties] delay_pass = RelaxationNoisePass( - t1s=t1s, - t2s=t2s, + t1s=[np.inf if x is None else x for x in t1s], # replace None with np.inf + t2s=[np.inf if x is None else x for x in t2s], # replace None with np.inf dt=dt, op_types=Delay, - excited_state_populations=excited_state_populations + excited_state_populations=excited_state_populations, ) noise_model._custom_noise_passes.append(delay_pass) except BackendPropertyError: @@ -428,10 +474,14 @@ def from_backend_properties( Args: backend_properties (BackendProperties): The property of backend. gate_error (Bool): Include depolarizing gate errors (Default: True). - readout_error (Bool): Include readout errors in model - (Default: True). - thermal_relaxation (Bool): Include thermal relaxation errors - (Default: True). + readout_error (Bool): Include readout errors in model (Default: True). + thermal_relaxation (Bool): Include thermal relaxation errors (Default: True). + If no ``t1`` and ``t2`` values are provided (i.e. None) in ``target`` for a qubit, + an identity ``QuantumError` (i.e. effectively no thermal relaxation error) + will be added to the qubit even if this flag is set to True. + If no ``frequency`` is not defined (i.e. None) in ``target`` for a qubit, + no excitation is considered in the thermal relaxation error on the qubit + even with non-zero ``temperature``. temperature (double): qubit temperature in milli-Kelvin (mK) for thermal relaxation errors (Default: 0). gate_lengths (Optional[list]): Custom gate times for thermal relaxation errors. @@ -452,8 +502,7 @@ def from_backend_properties( """ if not isinstance(backend_properties, BackendProperties): raise NoiseError( - "{} is not a Qiskit backend or" - " BackendProperties".format(backend_properties) + "{} is not a Qiskit backend or" " BackendProperties".format(backend_properties) ) basis_gates = set() for prop in backend_properties.gates: @@ -492,8 +541,10 @@ def from_backend_properties( try: delay_pass = RelaxationNoisePass( t1s=[backend_properties.t1(q) for q in range(num_qubits)], - t2s=[_truncate_t2_value(backend_properties.t1( - q), backend_properties.t2(q)) for q in range(num_qubits)], + t2s=[ + _truncate_t2_value(backend_properties.t1(q), backend_properties.t2(q)) + for q in range(num_qubits) + ], dt=dt, op_types=Delay, excited_state_populations=excited_state_populations, @@ -535,10 +586,10 @@ def __str__(self): # Get default errors default_error_ops = [] for inst in self._default_quantum_errors: - default_error_ops.append('{}'.format(inst)) + default_error_ops.append("{}".format(inst)) if self._default_readout_error is not None: - if 'measure' not in default_error_ops: - default_error_ops.append('measure') + if "measure" not in default_error_ops: + default_error_ops.append("measure") # Get local errors local_error_ops = [] @@ -546,23 +597,20 @@ def __str__(self): for qubits in dic.keys(): local_error_ops.append((inst, qubits)) for qubits in self._local_readout_errors: - tmp = ('measure', qubits) + tmp = ("measure", qubits) if tmp not in local_error_ops: local_error_ops.append(tmp) output = "NoiseModel:" output += "\n Basis gates: {}".format(self.basis_gates) if self._noise_instructions: - output += "\n Instructions with noise: {}".format( - list(self._noise_instructions)) + output += "\n Instructions with noise: {}".format(list(self._noise_instructions)) if self._noise_qubits: - output += "\n Qubits with noise: {}".format( - list(self._noise_qubits)) + output += "\n Qubits with noise: {}".format(list(self._noise_qubits)) if default_error_ops: output += "\n All-qubits errors: {}".format(default_error_ops) if local_error_ops: - output += "\n Specific qubit errors: {}".format( - local_error_ops) + output += "\n Specific qubit errors: {}".format(local_error_ops) return output def __eq__(self, other): @@ -571,10 +619,12 @@ 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): @@ -604,9 +654,8 @@ def add_basis_gates(self, instructions): for name, _ in self._instruction_names_labels(instructions): # If the instruction is in the default basis gates for the # AerSimulator we add it to the basis gates. - if name in BASIS_GATES['automatic']: - if name not in ['measure', 'reset', 'initialize', - 'kraus', 'superop', 'roerror']: + if name in BASIS_GATES["automatic"]: + if name not in ["measure", "reset", "initialize", "kraus", "superop", "roerror"]: self._basis_gates.add(name) def add_all_qubit_quantum_error(self, error, instructions, warnings=True): @@ -646,20 +695,23 @@ def add_all_qubit_quantum_error(self, error, instructions, warnings=True): if warnings: logger.warning( "WARNING: all-qubit error already exists for " - "instruction \"%s\", " - "composing with additional error.", label) + 'instruction "%s", ' + "composing with additional error.", + label, + ) else: self._default_quantum_errors[label] = error # Check if a specific qubit error has been applied for this instruction if label in self._local_quantum_errors: - local_qubits = self._keys2str( - self._local_quantum_errors[label].keys()) + local_qubits = self._keys2str(self._local_quantum_errors[label].keys()) if warnings: logger.warning( "WARNING: all-qubit error for instruction " - "\"%s\" will not apply to qubits: " - "%s as specific error already exists.", label, - local_qubits) + '"%s" will not apply to qubits: ' + "%s as specific error already exists.", + label, + local_qubits, + ) self._noise_instructions.add(label) self.add_basis_gates(name) @@ -712,17 +764,21 @@ def add_quantum_error(self, error, instructions, qubits, warnings=True): # Convert qubits list to hashable string if error.num_qubits != len(qubits): - raise NoiseError("Number of qubits ({}) does not match " - " the error size ({})".format( - len(qubits), error.num_qubits)) + raise NoiseError( + "Number of qubits ({}) does not match " + " the error size ({})".format(len(qubits), error.num_qubits) + ) if qubits in qubit_dict: new_error = qubit_dict[qubits].compose(error) qubit_dict[qubits] = new_error if warnings: logger.warning( "WARNING: quantum error already exists for " - "instruction \"%s\" on qubits %s " - ", appending additional error.", label, qubits) + 'instruction "%s" on qubits %s ' + ", appending additional error.", + label, + qubits, + ) else: qubit_dict[qubits] = error # Add updated dictionary @@ -732,9 +788,12 @@ def add_quantum_error(self, error, instructions, qubits, warnings=True): if label in self._default_quantum_errors: if warnings: logger.warning( - "WARNING: Specific error for instruction \"%s\" " + 'WARNING: Specific error for instruction "%s" ' "on qubits %s overrides previously defined " - "all-qubit error for these qubits.", label, qubits) + "all-qubit error for these qubits.", + label, + qubits, + ) self._noise_instructions.add(label) self.add_basis_gates(name) @@ -767,14 +826,13 @@ def add_all_qubit_readout_error(self, error, warnings=True): # Check number of qubits is correct for standard instructions if error.number_of_qubits != 1: - raise NoiseError( - "All-qubit readout errors must defined as single-qubit errors." - ) + raise NoiseError("All-qubit readout errors must defined as single-qubit errors.") if self._default_readout_error is not None: if warnings: logger.warning( "WARNING: all-qubit readout error already exists, " - "overriding with new readout error.") + "overriding with new readout error." + ) self._default_readout_error = error # Check if a specific qubit error has been applied for this instruction @@ -784,7 +842,9 @@ def add_all_qubit_readout_error(self, error, warnings=True): logger.warning( "WARNING: The all-qubit readout error will not " "apply to measure of qubits qubits: %s " - "as specific readout errors already exist.", local_qubits) + "as specific readout errors already exist.", + local_qubits, + ) self._noise_instructions.add("measure") def add_readout_error(self, error, qubits, warnings=True): @@ -827,13 +887,16 @@ def add_readout_error(self, error, qubits, warnings=True): if error.number_of_qubits != len(qubits): raise NoiseError( "Number of qubits ({}) does not match the readout " - "error size ({})".format(len(qubits), error.number_of_qubits)) + "error size ({})".format(len(qubits), error.number_of_qubits) + ) # Check if we are overriding a previous error if qubits in self._local_readout_errors: if warnings: logger.warning( "WARNING: readout error already exists for qubits " - "%s, overriding with new readout error.", qubits) + "%s, overriding with new readout error.", + qubits, + ) self._local_readout_errors[qubits] = error # Check if all-qubit readout error is already defined @@ -842,7 +905,9 @@ def add_readout_error(self, error, qubits, warnings=True): logger.warning( "WARNING: Specific readout error on qubits " "%s overrides previously defined " - "all-qubit readout error for these qubits.", qubits) + "all-qubit readout error for these qubits.", + qubits, + ) self._noise_instructions.add("measure") def to_dict(self, serializable=False): @@ -903,84 +968,89 @@ def from_dict(noise_dict): Raises: NoiseError: if dict cannot be converted to NoiseModel. """ - warn('from_dict has been deprecated as of qiskit-aer 0.10.0' - ' and will be removed no earlier than 3 months from that release date.', - DeprecationWarning, stacklevel=2) + warn( + "from_dict has been deprecated as of qiskit-aer 0.10.0" + " and will be removed no earlier than 3 months from that release date.", + DeprecationWarning, + stacklevel=2, + ) def inst_dic_list_to_circuit(dic_list): - num_qubits = max([max(dic['qubits']) for dic in dic_list]) + 1 + num_qubits = max([max(dic["qubits"]) for dic in dic_list]) + 1 circ = QuantumCircuit(num_qubits) for dic in dic_list: - if dic['name'] == 'reset': - circ.append(Reset(), qargs=dic['qubits']) - elif dic['name'] == 'kraus': - circ.append(Instruction(name='kraus', - num_qubits=len(dic['qubits']), - num_clbits=0, - params=dic['params']), - qargs=dic['qubits']) - elif dic['name'] == 'unitary': - circ.append(UnitaryGate(data=dic['params'][0]), - qargs=dic['qubits']) - elif dic['name'] == 'pauli': - circ.append(PauliGate(dic['params'][0]), - qargs=dic['qubits']) + if dic["name"] == "reset": + circ.append(Reset(), qargs=dic["qubits"]) + elif dic["name"] == "kraus": + circ.append( + Instruction( + name="kraus", + num_qubits=len(dic["qubits"]), + num_clbits=0, + params=dic["params"], + ), + qargs=dic["qubits"], + ) + elif dic["name"] == "unitary": + circ.append(UnitaryGate(data=dic["params"][0]), qargs=dic["qubits"]) + elif dic["name"] == "pauli": + circ.append(PauliGate(dic["params"][0]), qargs=dic["qubits"]) else: with catch_warnings(): filterwarnings( "ignore", category=DeprecationWarning, - module="qiskit_aer.noise.errors.errorutils" + module="qiskit_aer.noise.errors.errorutils", + ) + circ.append( + UnitaryGate( + label=dic["name"], data=_standard_gate_unitary(dic["name"]) + ), + qargs=dic["qubits"], ) - circ.append(UnitaryGate(label=dic['name'], - data=_standard_gate_unitary(dic['name'])), - qargs=dic['qubits']) return circ # Return noise model noise_model = NoiseModel() # Get error terms - errors = noise_dict.get('errors', []) + errors = noise_dict.get("errors", []) for error in errors: - error_type = error['type'] + error_type = error["type"] # Add QuantumError - if error_type == 'qerror': - circuits = [inst_dic_list_to_circuit(dics) for dics in error['instructions']] - noise_ops = tuple(zip(circuits, error['probabilities'])) + if error_type == "qerror": + circuits = [inst_dic_list_to_circuit(dics) for dics in error["instructions"]] + noise_ops = tuple(zip(circuits, error["probabilities"])) qerror = QuantumError(noise_ops) - qerror._id = error.get('id', None) or qerror.id - instruction_names = error['operations'] - all_gate_qubits = error.get('gate_qubits', None) + qerror._id = error.get("id", None) or qerror.id + instruction_names = error["operations"] + all_gate_qubits = error.get("gate_qubits", None) if all_gate_qubits is not None: for gate_qubits in all_gate_qubits: # Add local quantum error noise_model.add_quantum_error( - qerror, - instruction_names, - gate_qubits, - warnings=False) + qerror, instruction_names, gate_qubits, warnings=False + ) else: # Add all-qubit quantum error noise_model.add_all_qubit_quantum_error( - qerror, instruction_names, warnings=False) + qerror, instruction_names, warnings=False + ) # Add ReadoutError - elif error_type == 'roerror': - probabilities = error['probabilities'] - all_gate_qubits = error.get('gate_qubits', None) + elif error_type == "roerror": + probabilities = error["probabilities"] + all_gate_qubits = error.get("gate_qubits", None) roerror = ReadoutError(probabilities) # Add local readout error if all_gate_qubits is not None: for gate_qubits in all_gate_qubits: - noise_model.add_readout_error( - roerror, gate_qubits, warnings=False) + noise_model.add_readout_error(roerror, gate_qubits, warnings=False) # Add all-qubit readout error else: - noise_model.add_all_qubit_readout_error( - roerror, warnings=False) + noise_model.add_all_qubit_readout_error(roerror, warnings=False) # Invalid error type else: raise NoiseError("Invalid error type: {}".format(error_type)) @@ -996,12 +1066,12 @@ def _instruction_names_labels(self, instructions): # as the label if isinstance(inst, Instruction): name = inst.name - label = getattr(inst, 'label', inst.name) + label = getattr(inst, "label", inst.name) names_labels.append((name, label)) elif isinstance(inst, str): names_labels.append((inst, inst)) else: - raise NoiseError('Invalid instruction type {}'.format(inst)) + raise NoiseError("Invalid instruction type {}".format(inst)) return names_labels def _check_number_of_qubits(self, error, name): @@ -1017,9 +1087,11 @@ def _check_number_of_qubits(self, error, name): """ def error_message(gate_qubits): - msg = "{} qubit QuantumError".format(error.num_qubits) + \ - " cannot be applied to {} qubit".format(gate_qubits) + \ - " instruction \"{}\".".format(name) + msg = ( + "{} qubit QuantumError".format(error.num_qubits) + + " cannot be applied to {} qubit".format(gate_qubits) + + ' instruction "{}".'.format(name) + ) return msg if name in self._1qubit_instructions and error.num_qubits != 1: @@ -1040,30 +1112,27 @@ def _readout_errors_equal(self, other): if self._default_readout_error != other._default_readout_error: return False # Check local readout errors are equal - if sorted(self._local_readout_errors.keys()) != sorted( - other._local_readout_errors.keys()): + if sorted(self._local_readout_errors.keys()) != sorted(other._local_readout_errors.keys()): return False for key in self._local_readout_errors: - if self._local_readout_errors[key] != other._local_readout_errors[ - key]: + if self._local_readout_errors[key] != other._local_readout_errors[key]: return False return True def _all_qubit_quantum_errors_equal(self, other): """Check two noise models have equal local quantum errors""" if sorted(self._default_quantum_errors.keys()) != sorted( - other._default_quantum_errors.keys()): + other._default_quantum_errors.keys() + ): return False for key in self._default_quantum_errors: - if self._default_quantum_errors[ - key] != other._default_quantum_errors[key]: + if self._default_quantum_errors[key] != other._default_quantum_errors[key]: return False return True def _local_quantum_errors_equal(self, other): """Check two noise models have equal local quantum errors""" - if sorted(self._local_quantum_errors.keys()) != sorted( - other._local_quantum_errors.keys()): + if sorted(self._local_quantum_errors.keys()) != sorted(other._local_quantum_errors.keys()): return False for key in self._local_quantum_errors: inner_dict1 = self._local_quantum_errors[key] @@ -1073,8 +1142,7 @@ def _local_quantum_errors_equal(self, other): for inner_key in inner_dict1: if inner_dict1[inner_key] != inner_dict2[inner_key]: return False - if self._local_quantum_errors[key] != other._local_quantum_errors[ - key]: + if self._local_quantum_errors[key] != other._local_quantum_errors[key]: return False return True @@ -1094,50 +1162,66 @@ def _pass_manager(self) -> Optional[PassManager]: def _standard_gate_unitary(name): # To be removed with from_dict 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) + ("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) diff --git a/qiskit_aer/noise/noiseerror.py b/qiskit_aer/noise/noiseerror.py index 32edb0febf..cc3e5244c6 100644 --- a/qiskit_aer/noise/noiseerror.py +++ b/qiskit_aer/noise/noiseerror.py @@ -23,7 +23,7 @@ class NoiseError(QiskitError): def __init__(self, *message): """Set the error message.""" super().__init__(*message) - self.message = ' '.join(message) + self.message = " ".join(message) def __str__(self): """Return the message.""" diff --git a/qiskit_aer/noise/passes/local_noise_pass.py b/qiskit_aer/noise/passes/local_noise_pass.py index e141d6f685..8713ed494d 100644 --- a/qiskit_aer/noise/passes/local_noise_pass.py +++ b/qiskit_aer/noise/passes/local_noise_pass.py @@ -53,10 +53,10 @@ def func( """ def __init__( - self, - func: Callable[[Instruction, Sequence[int]], Optional[InstructionLike]], - op_types: Optional[Union[type, Iterable[type]]] = None, - method: str = 'append' + self, + func: Callable[[Instruction, Sequence[int]], Optional[InstructionLike]], + op_types: Optional[Union[type, Iterable[type]]] = None, + method: str = "append", ): """Initialize noise pass. diff --git a/qiskit_aer/noise/passes/relaxation_noise_pass.py b/qiskit_aer/noise/passes/relaxation_noise_pass.py index b441ce6a3b..d2bf6159bc 100644 --- a/qiskit_aer/noise/passes/relaxation_noise_pass.py +++ b/qiskit_aer/noise/passes/relaxation_noise_pass.py @@ -28,12 +28,12 @@ class RelaxationNoisePass(LocalNoisePass): """Add duration dependent thermal relaxation noise after instructions.""" def __init__( - self, - t1s: List[float], - t2s: List[float], - dt: Optional[float] = None, - op_types: Optional[Union[type, Sequence[type]]] = None, - excited_state_populations: Optional[List[float]] = None, + self, + t1s: List[float], + t2s: List[float], + dt: Optional[float] = None, + op_types: Optional[Union[type, Sequence[type]]] = None, + excited_state_populations: Optional[List[float]] = None, ): """Initialize RelaxationNoisePass. @@ -57,24 +57,24 @@ def __init__( self._dt = dt super().__init__(self._thermal_relaxation_error, op_types=op_types, method="append") - def _thermal_relaxation_error( - self, - op: Instruction, - qubits: Sequence[int] - ): + def _thermal_relaxation_error(self, op: Instruction, qubits: Sequence[int]): """Return thermal relaxation error on each operand qubit""" if not op.duration: if op.duration is None: - warnings.warn("RelaxationNoisePass ignores instructions without duration," - " you may need to schedule circuit in advance.", UserWarning) + warnings.warn( + "RelaxationNoisePass ignores instructions without duration," + " you may need to schedule circuit in advance.", + UserWarning, + ) return None # Convert op duration to seconds - if op.unit == 'dt': + if op.unit == "dt": if self._dt is None: raise NoiseError( "RelaxationNoisePass cannot apply noise to a 'dt' unit duration" - " without a dt time set.") + " without a dt time set." + ) duration = op.duration * self._dt else: duration = apply_prefix(op.duration, op.unit) diff --git a/qiskit_aer/primitives/estimator.py b/qiskit_aer/primitives/estimator.py index 1917d145e9..6e34dda648 100644 --- a/qiskit_aer/primitives/estimator.py +++ b/qiskit_aer/primitives/estimator.py @@ -19,9 +19,10 @@ from collections import defaultdict from collections.abc import Sequence from copy import copy +from warnings import warn import numpy as np -from qiskit.circuit import QuantumCircuit +from qiskit.circuit import ParameterExpression, QuantumCircuit from qiskit.compiler import transpile from qiskit.opflow import PauliSumOp from qiskit.primitives import BaseEstimator, EstimatorResult @@ -50,12 +51,21 @@ class Estimator(BaseEstimator): .. note:: Precedence of seeding for ``seed_simulator`` is as follows: - 1. ``seed_simulator`` in runtime (i.e. in :meth:`__call__`) - 2. ``seed`` in runtime (i.e. in :meth:`__call__`) + 1. ``seed_simulator`` in runtime (i.e. in :meth:`run`) + 2. ``seed`` in runtime (i.e. in :meth:`run`) 3. ``seed_simulator`` of ``backend_options``. 4. default. ``seed`` is also used for sampling from a normal distribution when approximation is True. + + When combined with the approximation option, we get the expectation values as follows: + + * shots is None and approximation=False: Return an expectation value with sampling-noise w/ + warning. + * shots is int and approximation=False: Return an expectation value with sampling-noise. + * shots is None and approximation=True: Return an exact expectation value. + * shots is int and approximation=True: Return expectation value with sampling-noise using a + normal distribution approximation. """ def __init__( @@ -106,7 +116,6 @@ def _call( parameter_values: Sequence[Sequence[float]], **run_options, ) -> EstimatorResult: - seed = run_options.pop("seed", None) if seed is not None: run_options.setdefault("seed_simulator", seed) @@ -125,7 +134,6 @@ def _run( parameter_values: Sequence[Sequence[float]], **run_options, ) -> PrimitiveJob: - circuit_indices: list = [] for circuit in circuits: index = self._circuit_ids.get(_circuit_key(circuit)) @@ -147,12 +155,24 @@ def _run( self._observable_ids[_observable_key(observable)] = len(self._observables) self._observables.append(observable) job = PrimitiveJob( - self._call, circuit_indices, observable_indices, parameter_values, **run_options + self._call, + circuit_indices, + observable_indices, + parameter_values, + **run_options, ) job.submit() return job def _compute(self, circuits, observables, parameter_values, run_options): + if "shots" in run_options and run_options["shots"] is None: + warn( + "If `shots` is None and `approximation` is False, " + "the number of shots is automatically set to backend options' " + f"shots={self._backend.options.shots}.", + RuntimeWarning, + ) + # Key for cache key = (tuple(circuits), tuple(observables), self.approximation) @@ -301,14 +321,12 @@ def _calculate_result_index(circ_ind, obs_ind, term_ind, param_val, obs_maps, ex result_index = 0 for _circ_ind, basis_map in exp_map.items(): - for _basis_ind, (_, param_vals) in basis_map.items(): + for _basis_ind, (_, (_, param_vals)) in enumerate(basis_map.items()): if circ_ind == _circ_ind and basis_ind == _basis_ind: result_index += param_vals.index(param_val) return result_index result_index += len(param_vals) - raise AerError( - "Bug. Please report from isssue: https://github.com/Qiskit/qiskit-aer/issues" - ) + raise AerError("Bug. Please report from issue: https://github.com/Qiskit/qiskit-aer/issues") def _create_post_processing( self, circuits, observables, parameter_values, obs_maps, exp_map @@ -346,55 +364,80 @@ def _compute_with_approximation( ): # Key for cache key = (tuple(circuits), tuple(observables), self.approximation) - parameter_binds = [] shots = run_options.pop("shots", None) + # Create expectation value experiments. if key in self._cache: # Use a cache - experiments, experiment_data = self._cache[key] + parameter_binds = defaultdict(dict) for i, j, value in zip(circuits, observables, parameter_values): self._validate_parameter_length(value, i) - parameter_binds.append({k: [v] for k, v in zip(self._parameters[i], value)}) + for k, v in zip(self._parameters[i], value): + if k in parameter_binds[(i, j)]: + parameter_binds[(i, j)][k].append(v) + else: + parameter_binds[(i, j)][k] = [v] + experiment_manager = self._cache[key] + experiment_manager.parameter_binds = list(parameter_binds.values()) else: self._transpile_circuits(circuits) - experiments = [] - experiment_data = [] + experiment_manager = _ExperimentManager() for i, j, value in zip(circuits, observables, parameter_values): - self._validate_parameter_length(value, i) - circuit = ( - self._circuits[i].copy() - if self._skip_transpilation - else self._transpiled_circuits[i].copy() - ) - observable = self._observables[j] - experiment_data.append(observable) - if shots is None: - circuit.save_expectation_value(observable, self._layouts[i]) + if (i, j) in experiment_manager.keys: + self._validate_parameter_length(value, i) + experiment_manager.append( + key=(i, j), + parameter_bind=dict(zip(self._parameters[i], value)), + ) else: - for term_ind, pauli in enumerate(observable.paulis): - circuit.save_expectation_value(pauli, self._layouts[i], label=str(term_ind)) - experiments.append(circuit) - parameter_binds.append({k: [v] for k, v in zip(self._parameters[i], value)}) - self._cache[key] = (experiments, experiment_data) - parameter_binds = parameter_binds if any(parameter_binds) else None + self._validate_parameter_length(value, i) + circuit = ( + self._circuits[i].copy() + if self._skip_transpilation + else self._transpiled_circuits[i].copy() + ) + + observable = self._observables[j] + if shots is None: + circuit.save_expectation_value(observable, self._layouts[i]) + else: + for term_ind, pauli in enumerate(observable.paulis): + circuit.save_expectation_value( + pauli, self._layouts[i], label=str(term_ind) + ) + experiment_manager.append( + key=(i, j), + parameter_bind=dict(zip(self._parameters[i], value)), + experiment_circuit=circuit, + ) + + self._cache[key] = experiment_manager result = self._backend.run( - experiments, parameter_binds=parameter_binds, **run_options + experiment_manager.experiment_circuits, + parameter_binds=experiment_manager.parameter_binds, + **run_options, ).result() # Post processing (calculate expectation values) if shots is None: - expectation_values = [result.data(i)["expectation_value"] for i in range(len(circuits))] + expectation_values = [ + result.data(i)["expectation_value"] for i in experiment_manager.experiment_indices + ] metadata = [ - {"simulator_metadata": result.results[i].metadata} for i in range(len(experiments)) + {"simulator_metadata": result.results[i].metadata} + for i in experiment_manager.experiment_indices ] else: expectation_values = [] rng = np.random.default_rng(seed) metadata = [] - for i in range(len(experiments)): + experiment_indices = experiment_manager.experiment_indices + for i in range(len(experiment_manager)): combined_expval = 0.0 combined_var = 0.0 - coeffs = np.real_if_close(experiment_data[i].coeffs) - for term_ind, expval in result.data(i).items(): + result_index = experiment_indices[i] + observable_key = experiment_manager.get_observable_key(i) + coeffs = np.real_if_close(self._observables[observable_key].coeffs) + for term_ind, expval in result.data(result_index).items(): var = 1 - expval**2 coeff = coeffs[int(term_ind)] combined_expval += expval * coeff @@ -404,9 +447,9 @@ def _compute_with_approximation( expectation_values.append(rng.normal(combined_expval, standard_error)) metadata.append( { - "variance": combined_var, + "variance": np.real_if_close(combined_var).item(), "shots": shots, - "simulator_metadata": result.results[i].metadata, + "simulator_metadata": result.results[result_index].metadata, } ) @@ -459,7 +502,10 @@ def _expval_with_variance(counts) -> tuple[float, float]: class _PostProcessing: def __init__( - self, result_indices: list[int], paulis: list[PauliList], coeffs: list[list[float]] + self, + result_indices: list[int], + paulis: list[PauliList], + coeffs: list[list[float]], ): self._result_indices = result_indices self._paulis = paulis @@ -497,7 +543,7 @@ def run(self, results: list[ExperimentResult]) -> tuple[float, dict]: combined_var += np.dot(variances, coeffs**2) metadata = { "shots": shots, - "variance": combined_var, + "variance": np.real_if_close(combined_var).item(), "simulator_metadata": simulator_metadata, } return combined_expval, metadata @@ -554,3 +600,49 @@ def _paulis2basis(paulis: PauliList) -> Pauli: np.logical_or.reduce(paulis.x), # pylint:disable=no-member ) ) + + +class _ExperimentManager: + def __init__(self): + self.keys: list[tuple[int, int]] = [] + self.experiment_circuits: list[QuantumCircuit] = [] + self.parameter_binds: list[dict[ParameterExpression, list[float]]] = [] + self._input_indices: list[list[int]] = [] + self._num_experiment: int = 0 + + def __len__(self): + return self._num_experiment + + @property + def experiment_indices(self): + """indices of experiments""" + return sum(self._input_indices, []) + + def append( + self, + key: tuple[int, int], + parameter_bind: dict[ParameterExpression, float], + experiment_circuit: QuantumCircuit | None = None, + ): + """append experiments""" + if experiment_circuit is not None: + self.experiment_circuits.append(experiment_circuit) + + if key in self.keys: + key_index = self.keys.index(key) + for k, vs in self.parameter_binds[key_index].items(): + vs.append(parameter_bind[k]) + self._input_indices[key_index].append(self._num_experiment) + else: + self.keys.append(key) + self.parameter_binds.append({k: [v] for k, v in parameter_bind.items()}) + self._input_indices.append([self._num_experiment]) + + self._num_experiment += 1 + + def get_observable_key(self, index): + """return key of observables""" + for i, inputs in enumerate(self._input_indices): + if index in inputs: + return self.keys[i][1] + raise AerError("Unexpected behavior.") diff --git a/qiskit_aer/primitives/sampler.py b/qiskit_aer/primitives/sampler.py index e1b75cef39..286d7df0fe 100644 --- a/qiskit_aer/primitives/sampler.py +++ b/qiskit_aer/primitives/sampler.py @@ -66,7 +66,6 @@ def __init__( skip_transpilation: if True, transpilation is skipped. """ super().__init__(options=run_options) - self._is_closed = False self._backend = AerSimulator() backend_options = {} if backend_options is None else backend_options self._backend.set_options(**backend_options) @@ -82,9 +81,6 @@ def _call( parameter_values: Sequence[Sequence[float]], **run_options, ) -> SamplerResult: - if self._is_closed: - raise QiskitError("The primitive has been closed.") - seed = run_options.pop("seed", None) if seed is not None: run_options.setdefault("seed_simulator", seed) @@ -158,9 +154,6 @@ def _run( job.submit() return job - def close(self): - self._is_closed = True - @staticmethod def _preprocess_circuit(circuit: QuantumCircuit): circuit = init_circuit(circuit) diff --git a/qiskit_aer/pulse/controllers/digest_pulse_qobj.py b/qiskit_aer/pulse/controllers/digest_pulse_qobj.py index a64d032423..ea278a2b84 100644 --- a/qiskit_aer/pulse/controllers/digest_pulse_qobj.py +++ b/qiskit_aer/pulse/controllers/digest_pulse_qobj.py @@ -21,6 +21,7 @@ from qiskit.pulse import DriveChannel from ...aererror import AerError + # pylint: disable=no-name-in-module from .pulse_utils import oplist_to_array @@ -29,7 +30,6 @@ class DigestedPulseQobj: """Container class for information extracted from PulseQobj.""" def __init__(self): - # #################################### # Some "Simulation description" # #################################### @@ -66,7 +66,7 @@ def __init__(self): def digest_pulse_qobj(qobj, channels, dt, qubit_list): - """ Given a PulseQobj (and other parameters), returns a DigestedPulseQobj + """Given a PulseQobj (and other parameters), returns a DigestedPulseQobj containing relevant extracted information Parameters: @@ -86,47 +86,50 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list): digested_qobj = DigestedPulseQobj() qobj_dict = qobj.to_dict() - qobj_config = qobj_dict['config'] + qobj_config = qobj_dict["config"] # extract schedule_los - if qobj_config.get('schedule_los') is not None: - for exp, schedule_lo in zip(qobj_dict['experiments'], qobj_config['schedule_los']): - if exp.get('config') is None: - exp['config'] = {} + if qobj_config.get("schedule_los") is not None: + for exp, schedule_lo in zip(qobj_dict["experiments"], qobj_config["schedule_los"]): + if exp.get("config") is None: + exp["config"] = {} schedule_lo_list = [] for idx in qubit_list: freq = schedule_lo.get(DriveChannel(idx), None) if freq is None: - raise ValueError('''A qubit in the simulation is missing an entry in - schedule_los.''') + raise ValueError( + """A qubit in the simulation is missing an entry in + schedule_los.""" + ) schedule_lo_list.append(freq * 1e-9) - exp['config']['qubit_lo_freq'] = schedule_lo_list + exp["config"]["qubit_lo_freq"] = schedule_lo_list # raises errors for unsupported features _unsupported_errors(qobj_dict) - if 'memory_slots' not in qobj_config: - raise ValueError('Number of memory_slots must be specific in Qobj config') + if "memory_slots" not in qobj_config: + raise ValueError("Number of memory_slots must be specific in Qobj config") # set memory and measurement details - digested_qobj.shots = int(qobj_config.get('shots', 1024)) - digested_qobj.meas_level = int(qobj_config.get('meas_level', 2)) - digested_qobj.meas_return = qobj_config.get('meas_return', 'avg') - digested_qobj.memory_slots = qobj_config.get('memory_slots', 0) - digested_qobj.memory = qobj_config.get('memory', False) - digested_qobj.n_registers = qobj_config.get('n_registers', 0) + digested_qobj.shots = int(qobj_config.get("shots", 1024)) + digested_qobj.meas_level = int(qobj_config.get("meas_level", 2)) + digested_qobj.meas_return = qobj_config.get("meas_return", "avg") + digested_qobj.memory_slots = qobj_config.get("memory_slots", 0) + digested_qobj.memory = qobj_config.get("memory", False) + digested_qobj.n_registers = qobj_config.get("n_registers", 0) # set qubit_lo_freq as given in qobj - if 'qubit_lo_freq' in qobj_config and qobj_config['qubit_lo_freq'] != [np.inf]: + if "qubit_lo_freq" in qobj_config and qobj_config["qubit_lo_freq"] != [np.inf]: # qobj frequencies are divided by 1e9, so multiply back - digested_qobj.qubit_lo_freq = [freq * 1e9 for freq in qobj_config['qubit_lo_freq']] + digested_qobj.qubit_lo_freq = [freq * 1e9 for freq in qobj_config["qubit_lo_freq"]] # build pulse arrays from qobj - pulses, pulses_idx, pulse_dict = build_pulse_arrays(qobj_dict['experiments'], - qobj_config['pulse_library']) + pulses, pulses_idx, pulse_dict = build_pulse_arrays( + qobj_dict["experiments"], qobj_config["pulse_library"] + ) digested_qobj.pulse_array = pulses digested_qobj.pulse_indices = pulses_idx @@ -134,13 +137,8 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list): experiments = [] - for exp in qobj_dict['experiments']: - exp_struct = experiment_to_structs(exp, - channels, - pulses_idx, - pulse_dict, - dt, - qubit_list) + for exp in qobj_dict["experiments"]: + exp_struct = experiment_to_structs(exp, channels, pulses_idx, pulse_dict, dt, qubit_list) experiments.append(exp_struct) digested_qobj.experiments = experiments @@ -149,7 +147,7 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list): def _unsupported_errors(qobj_dict): - """ Raises errors for untested/unsupported features. + """Raises errors for untested/unsupported features. Parameters: qobj_dict (dict): qobj in dictionary form @@ -159,26 +157,26 @@ def _unsupported_errors(qobj_dict): """ # Warnings that don't stop execution - warning_str = '{} are an untested feature, and therefore may not behave as expected.' - if _contains_pv_instruction(qobj_dict['experiments']): - raise AerError(warning_str.format('PersistentValue instructions')) + warning_str = "{} are an untested feature, and therefore may not behave as expected." + if _contains_pv_instruction(qobj_dict["experiments"]): + raise AerError(warning_str.format("PersistentValue instructions")) - error_str = '''{} are not directly supported by PulseSimulator. Convert to - explicit WaveForms to simulate.''' - if _contains_parametric_pulse(qobj_dict['experiments']): - raise AerError(error_str.format('Parametric Pulses')) + error_str = """{} are not directly supported by PulseSimulator. Convert to + explicit WaveForms to simulate.""" + if _contains_parametric_pulse(qobj_dict["experiments"]): + raise AerError(error_str.format("Parametric Pulses")) - error_str = '''Schedules contain {}, are not supported by PulseSimulator.''' - if _contains_frequency_instruction(qobj_dict['experiments']): - raise AerError(error_str.format('shift frequency and/or set frequency instructions')) + error_str = """Schedules contain {}, are not supported by PulseSimulator.""" + if _contains_frequency_instruction(qobj_dict["experiments"]): + raise AerError(error_str.format("shift frequency and/or set frequency instructions")) - required_str = '{} are required for simulation, and none were specified.' - if not _contains_acquire_instruction(qobj_dict['experiments']): - raise AerError(required_str.format('Acquire instructions')) + required_str = "{} are required for simulation, and none were specified." + if not _contains_acquire_instruction(qobj_dict["experiments"]): + raise AerError(required_str.format("Acquire instructions")) def _contains_acquire_instruction(experiments): - """ Return True if the list of experiments contains an Acquire instruction + """Return True if the list of experiments contains an Acquire instruction Parameters: experiments (list): list of schedules Returns: @@ -187,14 +185,14 @@ def _contains_acquire_instruction(experiments): """ for exp in experiments: - for inst in exp['instructions']: - if inst['name'] == 'acquire': + for inst in exp["instructions"]: + if inst["name"] == "acquire": return True return False def _contains_pv_instruction(experiments): - """ Return True if the list of experiments contains a PersistentValue instruction. + """Return True if the list of experiments contains a PersistentValue instruction. Parameters: experiments (list): list of schedules @@ -203,14 +201,14 @@ def _contains_pv_instruction(experiments): Raises: """ for exp in experiments: - for inst in exp['instructions']: - if inst['name'] == 'pv': + for inst in exp["instructions"]: + if inst["name"] == "pv": return True return False def _contains_frequency_instruction(experiments): - """ Return True if the list of experiments contains either a set fruquency or shift + """Return True if the list of experiments contains either a set fruquency or shift frequency instruction. Parameters: @@ -220,8 +218,8 @@ def _contains_frequency_instruction(experiments): Raises: """ for exp in experiments: - for inst in exp['instructions']: - if inst['name'] == 'setf' or inst['name'] == 'shiftf': + for inst in exp["instructions"]: + if inst["name"] == "setf" or inst["name"] == "shiftf": return True return False @@ -236,14 +234,14 @@ def _contains_parametric_pulse(experiments): Raises: """ for exp in experiments: - for inst in exp['instructions']: - if inst['name'] == 'parametric_pulse': + for inst in exp["instructions"]: + if inst["name"] == "parametric_pulse": return True return False def build_pulse_arrays(experiments, pulse_library): - """ Build pulses and pulse_idx arrays, and a pulse_dict + """Build pulses and pulse_idx arrays, and a pulse_dict used in simulations and mapping of experimental pulse sequencies to pulse_idx sequencies and timings. @@ -261,22 +259,22 @@ def build_pulse_arrays(experiments, pulse_library): num_pulse = 0 for pulse in pulse_library: - pulse_dict[pulse['name']] = num_pulse - total_pulse_length += len(pulse['samples']) + pulse_dict[pulse["name"]] = num_pulse + total_pulse_length += len(pulse["samples"]) num_pulse += 1 idx = num_pulse + 1 # now go through experiments looking for PV gates pv_pulses = [] for exp in experiments: - for pulse in exp['instructions']: - if pulse['name'] == 'pv': - if pulse['val'] not in [pval[1] for pval in pv_pulses] and pulse['val'] != 0: - pv_pulses.append((pulse['val'], idx)) + for pulse in exp["instructions"]: + if pulse["name"] == "pv": + if pulse["val"] not in [pval[1] for pval in pv_pulses] and pulse["val"] != 0: + pv_pulses.append((pulse["val"], idx)) idx += 1 total_pulse_length += 1 - pulse_dict['pv'] = pv_pulses + pulse_dict["pv"] = pv_pulses pulses = np.empty(total_pulse_length, dtype=complex) pulses_idx = np.zeros(idx + 1, dtype=np.uint32) @@ -284,9 +282,9 @@ def build_pulse_arrays(experiments, pulse_library): stop = 0 ind = 1 for _, pulse in enumerate(pulse_library): - stop = pulses_idx[ind - 1] + len(pulse['samples']) + stop = pulses_idx[ind - 1] + len(pulse["samples"]) pulses_idx[ind] = stop - oplist_to_array(format_pulse_samples(pulse['samples']), pulses, pulses_idx[ind - 1]) + oplist_to_array(format_pulse_samples(pulse["samples"]), pulses, pulses_idx[ind - 1]) ind += 1 for pv in pv_pulses: @@ -341,171 +339,173 @@ def experiment_to_structs(experiment, ham_chans, pulse_inds, pulse_to_int, dt, q # TO DO: Error check that operations are restricted to qubit list max_time = 0 structs = {} - structs['header'] = experiment['header'] - structs['channels'] = OrderedDict() + structs["header"] = experiment["header"] + structs["channels"] = OrderedDict() for chan_name in ham_chans: - structs['channels'][chan_name] = [[], []] - structs['acquire'] = [] - structs['cond'] = [] - structs['snapshot'] = [] - structs['tlist'] = [] - structs['can_sample'] = True + structs["channels"][chan_name] = [[], []] + structs["acquire"] = [] + structs["cond"] = [] + structs["snapshot"] = [] + structs["tlist"] = [] + structs["can_sample"] = True # set an experiment qubit_lo_freq if present in experiment - structs['qubit_lo_freq'] = None - if 'config' in experiment: - if ('qubit_lo_freq' in experiment['config'] and - experiment['config']['qubit_lo_freq'] is not None): - freq_list = experiment['config']['qubit_lo_freq'] + structs["qubit_lo_freq"] = None + if "config" in experiment: + if ( + "qubit_lo_freq" in experiment["config"] + and experiment["config"]["qubit_lo_freq"] is not None + ): + freq_list = experiment["config"]["qubit_lo_freq"] freq_list = [freq * 1e9 for freq in freq_list] - structs['qubit_lo_freq'] = freq_list + structs["qubit_lo_freq"] = freq_list # This is a list that tells us whether # the last PV pulse on a channel needs to # be assigned a final time based on the next pulse on that channel pv_needs_tf = [0] * len(ham_chans) # The instructions are time-ordered so just loop through them. - for inst in experiment['instructions']: + for inst in experiment["instructions"]: # Do D and U channels - if 'ch' in inst.keys() and inst['ch'][0] in ['d', 'u']: - chan_name = inst['ch'].upper() + if "ch" in inst.keys() and inst["ch"][0] in ["d", "u"]: + chan_name = inst["ch"].upper() if chan_name not in ham_chans.keys(): - raise ValueError('Channel {} is not in Hamiltonian model'.format(inst['ch'])) + raise ValueError("Channel {} is not in Hamiltonian model".format(inst["ch"])) # If last pulse on channel was a PV then need to set # its final time to be start time of current pulse if pv_needs_tf[ham_chans[chan_name]]: - structs['channels'][chan_name][0][-3] = inst['t0'] * dt + structs["channels"][chan_name][0][-3] = inst["t0"] * dt pv_needs_tf[ham_chans[chan_name]] = 0 # Get condtional info - if 'conditional' in inst.keys(): - cond = inst['conditional'] + if "conditional" in inst.keys(): + cond = inst["conditional"] else: cond = -1 # PV's - if inst['name'] == 'pv': + if inst["name"] == "pv": # Get PV index - for pv in pulse_to_int['pv']: - if pv[0] == inst['val']: + for pv in pulse_to_int["pv"]: + if pv[0] == inst["val"]: index = pv[1] break - structs['channels'][chan_name][0].extend([inst['t0'] * dt, None, index, cond]) + structs["channels"][chan_name][0].extend([inst["t0"] * dt, None, index, cond]) pv_needs_tf[ham_chans[chan_name]] = 1 # ShiftPhase instructions - elif inst['name'] == 'fc': + elif inst["name"] == "fc": # get current phase value current_phase = 0 - if len(structs['channels'][chan_name][1]) > 0: - current_phase = structs['channels'][chan_name][1][-2] + if len(structs["channels"][chan_name][1]) > 0: + current_phase = structs["channels"][chan_name][1][-2] - structs['channels'][chan_name][1].extend([inst['t0'] * dt, - current_phase + inst['phase'], - cond]) + structs["channels"][chan_name][1].extend( + [inst["t0"] * dt, current_phase + inst["phase"], cond] + ) # SetPhase instruction - elif inst['name'] == 'setp': - structs['channels'][chan_name][1].extend([inst['t0'] * dt, - inst['phase'], - cond]) + elif inst["name"] == "setp": + structs["channels"][chan_name][1].extend([inst["t0"] * dt, inst["phase"], cond]) # Delay instruction - elif inst['name'] == 'delay': + elif inst["name"] == "delay": pass # nothing to be done in this case # A standard pulse else: - start = inst['t0'] * dt - pulse_int = pulse_to_int[inst['name']] + start = inst["t0"] * dt + pulse_int = pulse_to_int[inst["name"]] pulse_width = (pulse_inds[pulse_int + 1] - pulse_inds[pulse_int]) * dt stop = start + pulse_width - structs['channels'][chan_name][0].extend([start, stop, pulse_int, cond]) + structs["channels"][chan_name][0].extend([start, stop, pulse_int, cond]) max_time = max(max_time, stop) # Take care of acquires and snapshots (bfuncs added ) else: # measurements - if inst['name'] == 'acquire': - + if inst["name"] == "acquire": # Better way?? qlist2 = [] mlist2 = [] if qubit_list is None: - qlist2 = inst['qubits'] - mlist2 = inst['memory_slot'] + qlist2 = inst["qubits"] + mlist2 = inst["memory_slot"] else: - for qind, qb in enumerate(inst['qubits']): + for qind, qb in enumerate(inst["qubits"]): if qb in qubit_list: qlist2.append(qb) - mlist2.append(inst['memory_slot'][qind]) - - acq_vals = [inst['t0'] * dt, - np.asarray(qlist2, dtype=np.uint32), - np.asarray(mlist2, dtype=np.uint32) - ] - if 'register_slot' in inst.keys(): - acq_vals.append(np.asarray(inst['register_slot'], - dtype=np.uint32)) + mlist2.append(inst["memory_slot"][qind]) + + acq_vals = [ + inst["t0"] * dt, + np.asarray(qlist2, dtype=np.uint32), + np.asarray(mlist2, dtype=np.uint32), + ] + if "register_slot" in inst.keys(): + acq_vals.append(np.asarray(inst["register_slot"], dtype=np.uint32)) else: acq_vals.append(None) - structs['acquire'].append(acq_vals) + structs["acquire"].append(acq_vals) # update max_time - max_time = max(max_time, (inst['t0'] + inst['duration']) * dt) + max_time = max(max_time, (inst["t0"] + inst["duration"]) * dt) # Add time to tlist - if inst['t0'] * dt not in structs['tlist']: - structs['tlist'].append(inst['t0'] * dt) + if inst["t0"] * dt not in structs["tlist"]: + structs["tlist"].append(inst["t0"] * dt) # conditionals - elif inst['name'] == 'bfunc': - bfun_vals = [inst['t0'] * dt, inst['mask'], inst['relation'], - inst['val'], inst['register']] - if 'memory' in inst.keys(): - bfun_vals.append(inst['memory']) + elif inst["name"] == "bfunc": + bfun_vals = [ + inst["t0"] * dt, + inst["mask"], + inst["relation"], + inst["val"], + inst["register"], + ] + if "memory" in inst.keys(): + bfun_vals.append(inst["memory"]) else: bfun_vals.append(None) - structs['cond'].append(acq_vals) + structs["cond"].append(acq_vals) # update max_time - max_time = max(max_time, inst['t0'] * dt) + max_time = max(max_time, inst["t0"] * dt) # Add time to tlist - if inst['t0'] * dt not in structs['tlist']: - structs['tlist'].append(inst['t0'] * dt) + if inst["t0"] * dt not in structs["tlist"]: + structs["tlist"].append(inst["t0"] * dt) # snapshots - elif inst['name'] == 'snapshot': - if inst['type'] != 'state': + elif inst["name"] == "snapshot": + if inst["type"] != "state": raise TypeError("Snapshots must be of type 'state'") - structs['snapshot'].append([inst['t0'] * dt, inst['label']]) + structs["snapshot"].append([inst["t0"] * dt, inst["label"]]) # Add time to tlist - if inst['t0'] * dt not in structs['tlist']: - structs['tlist'].append(inst['t0'] * dt) + if inst["t0"] * dt not in structs["tlist"]: + structs["tlist"].append(inst["t0"] * dt) # update max_time - max_time = max(max_time, inst['t0'] * dt) + max_time = max(max_time, inst["t0"] * dt) # If any PVs still need time then they are at the end # and should just go til final time ham_keys = list(ham_chans.keys()) for idx, pp in enumerate(pv_needs_tf): if pp: - structs['channels'][ham_keys[idx]][0][-3] = max_time + structs["channels"][ham_keys[idx]][0][-3] = max_time pv_needs_tf[idx] = 0 # Convert lists to numpy arrays - for key in structs['channels'].keys(): - structs['channels'][key][0] = np.asarray(structs['channels'][key][0], - dtype=float) - structs['channels'][key][1] = np.asarray(structs['channels'][key][1], - dtype=float) + for key in structs["channels"].keys(): + structs["channels"][key][0] = np.asarray(structs["channels"][key][0], dtype=float) + structs["channels"][key][1] = np.asarray(structs["channels"][key][1], dtype=float) - structs['tlist'] = np.asarray([0] + structs['tlist'], dtype=float) + structs["tlist"] = np.asarray([0] + structs["tlist"], dtype=float) - if structs['tlist'][-1] > structs['acquire'][-1][0]: - structs['can_sample'] = False + if structs["tlist"][-1] > structs["acquire"][-1][0]: + structs["can_sample"] = False return structs diff --git a/qiskit_aer/pulse/controllers/mc_controller.py b/qiskit_aer/pulse/controllers/mc_controller.py index 03e93faf1d..00e14d7f84 100644 --- a/qiskit_aer/pulse/controllers/mc_controller.py +++ b/qiskit_aer/pulse/controllers/mc_controller.py @@ -29,13 +29,13 @@ from qiskit.tools.parallel import parallel_map, CPU_COUNT from .pulse_sim_options import PulseSimOptions from .pulse_de_solver import setup_de_solver -from .pulse_utils import (occ_probabilities, write_shots_memory, spmv, cy_expect_psi) +from .pulse_utils import occ_probabilities, write_shots_memory, spmv, cy_expect_psi dznrm2 = get_blas_funcs("znrm2", dtype=np.float64) def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=None): - """ Runs monte carlo experiments for a given op_system + """Runs monte carlo experiments for a given op_system Parameters: pulse_sim_desc (PulseSimDescription): description of pulse simulation @@ -64,9 +64,9 @@ def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=N seed = pulse_sim_desc.seed or np.random.randint(np.iinfo(np.int32).max - 1) prng = np.random.RandomState(seed) for exp in pulse_sim_desc.experiments: - exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) + exp["seed"] = prng.randint(np.iinfo(np.int32).max - 1) - map_kwargs = {'num_processes': solver_options.num_cpus} + map_kwargs = {"num_processes": solver_options.num_cpus} exp_results = [] exp_times = [] @@ -76,16 +76,20 @@ def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=N for exp in pulse_sim_desc.experiments: start = time.time() - rng = np.random.RandomState(exp['seed']) + rng = np.random.RandomState(exp["seed"]) seeds = rng.randint(np.iinfo(np.int32).max - 1, size=pulse_sim_desc.shots) - exp_res = parallel_map(monte_carlo_evolution, - seeds, - task_args=(exp, - y0, - pulse_sim_desc, - pulse_de_model, - solver_options, ), - **map_kwargs) + exp_res = parallel_map( + monte_carlo_evolution, + seeds, + task_args=( + exp, + y0, + pulse_sim_desc, + pulse_de_model, + solver_options, + ), + **map_kwargs, + ) # exp_results is a list for each shot # so transform back to an array of shots @@ -100,13 +104,8 @@ def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=N return exp_results, exp_times -def monte_carlo_evolution(seed, - exp, - y0, - pulse_sim_desc, - pulse_de_model, - solver_options=None): - """ Performs a single monte carlo run for the given op_system, experiment, and seed +def monte_carlo_evolution(seed, exp, y0, pulse_sim_desc, pulse_de_model, solver_options=None): + """Performs a single monte carlo run for the given op_system, experiment, and seed Parameters: seed (int): seed for random number generation @@ -126,12 +125,12 @@ def monte_carlo_evolution(seed, solver_options = PulseSimOptions() if solver_options is None else solver_options rng = np.random.RandomState(seed) - tlist = exp['tlist'] + tlist = exp["tlist"] # Init memory memory = np.zeros((1, pulse_sim_desc.memory_slots), dtype=np.uint8) # Get number of acquire - num_acq = len(exp['acquire']) + num_acq = len(exp["acquire"]) acq_idx = 0 collapse_times = [] @@ -167,18 +166,16 @@ def monte_carlo_evolution(seed, t_final = ODE.t while ii < solver_options.norm_steps: ii += 1 - t_guess = t_prev + \ - log(norm2_prev / rand_vals[0]) / \ - log(norm2_prev / norm2_psi) * (t_final - t_prev) + t_guess = t_prev + log(norm2_prev / rand_vals[0]) / log( + norm2_prev / norm2_psi + ) * (t_final - t_prev) ODE.y = y_prev ODE.t = t_prev ODE.integrate(t_guess, step=0) if not ODE.successful(): - raise Exception( - "Integration failed after adjusting step size!") - norm2_guess = dznrm2(ODE.y)**2 - if (abs(rand_vals[0] - norm2_guess) < - solver_options.norm_tol * rand_vals[0]): + raise Exception("Integration failed after adjusting step size!") + norm2_guess = dznrm2(ODE.y) ** 2 + if abs(rand_vals[0] - norm2_guess) < solver_options.norm_tol * rand_vals[0]: break if norm2_guess < rand_vals[0]: @@ -191,9 +188,11 @@ def monte_carlo_evolution(seed, y_prev = ODE.y norm2_prev = norm2_guess if ii > solver_options.norm_steps: - raise Exception("Norm tolerance not reached. " + - "Increase accuracy of ODE solver or " + - "Options.norm_steps.") + raise Exception( + "Norm tolerance not reached. " + + "Increase accuracy of ODE solver or " + + "Options.norm_steps." + ) collapse_times.append(ODE.t) # all constant collapse operators. @@ -214,8 +213,8 @@ def monte_carlo_evolution(seed, out_psi = ODE.y / dznrm2(ODE.y) for aind in range(acq_idx, num_acq): - if exp['acquire'][aind][0] == stop_time: - current_acq = exp['acquire'][aind] + if exp["acquire"][aind][0] == stop_time: + current_acq = exp["acquire"][aind] qubits = current_acq[1] memory_slots = current_acq[2] probs = occ_probabilities(qubits, out_psi, pulse_sim_desc.measurement_ops) diff --git a/qiskit_aer/pulse/controllers/pulse_controller.py b/qiskit_aer/pulse/controllers/pulse_controller.py index 0e06c48adc..74b4e8ff40 100644 --- a/qiskit_aer/pulse/controllers/pulse_controller.py +++ b/qiskit_aer/pulse/controllers/pulse_controller.py @@ -32,7 +32,7 @@ def pulse_controller(qobj): - """ Interprets PulseQobj input, runs simulations, and returns results + """Interprets PulseQobj input, runs simulations, and returns results Parameters: qobj (PulseQobj): pulse qobj containing a list of pulse schedules @@ -59,12 +59,12 @@ def pulse_controller(qobj): # Get qubit list and number qubit_list = system_model.subsystem_list if qubit_list is None: - raise ValueError('Model must have a qubit list to simulate.') + raise ValueError("Model must have a qubit list to simulate.") n_qubits = len(qubit_list) # get Hamiltonian if system_model.hamiltonian is None: - raise ValueError('Model must have a Hamiltonian to simulate.') + raise ValueError("Model must have a Hamiltonian to simulate.") ham_model = system_model.hamiltonian # Extract DE model information @@ -80,19 +80,19 @@ def pulse_controller(qobj): estates = [op_gen.state(state) for state in ham_model._estates.T[:]] # initial state set here - if getattr(config, 'initial_state', None) is not None: + if getattr(config, "initial_state", None) is not None: pulse_sim_desc.initial_state = op_gen.state(config.initial_state) else: pulse_sim_desc.initial_state = estates[0] # Get dt if system_model.dt is None: - raise ValueError('System model must have a dt value to simulate.') + raise ValueError("System model must have a dt value to simulate.") pulse_de_model.dt = system_model.dt # Parse noise - noise_model = getattr(config, 'noise_model', None) + noise_model = getattr(config, "noise_model", None) # post warnings for unsupported features _unsupported_warnings(noise_model) @@ -108,10 +108,7 @@ def pulse_controller(qobj): # ############################### # ### Parse qobj_config settings # ############################### - digested_qobj = digest_pulse_qobj(qobj, - pulse_de_model.channels, - system_model.dt, - qubit_list) + digested_qobj = digest_pulse_qobj(qobj, pulse_de_model.channels, system_model.dt, qubit_list) # extract simulation-description level qobj content pulse_sim_desc.shots = digested_qobj.shots @@ -133,16 +130,18 @@ def pulse_controller(qobj): # if it wasn't specified in the PulseQobj, draw from system_model if qubit_lo_freq is None: - default_freq = getattr(config, 'qubit_freq_est', [np.inf]) + default_freq = getattr(config, "qubit_freq_est", [np.inf]) if default_freq != [np.inf]: qubit_lo_freq = default_freq # if still None, or is the placeholder value draw from the Hamiltonian if qubit_lo_freq is None: qubit_lo_freq = system_model.hamiltonian.get_qubit_lo_from_drift() - if getattr(qobj.config, 'schedule_los', None) is None: - warn('Warning: qubit_lo_freq was not specified in PulseQobj and there is no default, ' - 'so it is being automatically determined from the drift Hamiltonian.') + if getattr(qobj.config, "schedule_los", None) is None: + warn( + "Warning: qubit_lo_freq was not specified in PulseQobj and there is no default, " + "so it is being automatically determined from the drift Hamiltonian." + ) pulse_de_model.freqs = system_model.calculate_channel_frequencies(qubit_lo_freq=qubit_lo_freq) pulse_de_model.calculate_channel_frequencies = system_model.calculate_channel_frequencies @@ -152,24 +151,31 @@ def pulse_controller(qobj): # # solver-specific information should be extracted in the solver # ############################### - pulse_sim_desc.seed = int(config.seed) if hasattr(config, 'seed') else None - pulse_sim_desc.q_level_meas = int(getattr(config, 'q_level_meas', 1)) + pulse_sim_desc.seed = int(config.seed) if hasattr(config, "seed") else None + pulse_sim_desc.q_level_meas = int(getattr(config, "q_level_meas", 1)) # solver options - allowed_solver_options = ['atol', 'rtol', 'nsteps', 'max_step', - 'num_cpus', 'norm_tol', 'norm_steps', - 'method'] - solver_options = getattr(config, 'solver_options', {}) + allowed_solver_options = [ + "atol", + "rtol", + "nsteps", + "max_step", + "num_cpus", + "norm_tol", + "norm_steps", + "method", + ] + solver_options = getattr(config, "solver_options", {}) for key in solver_options: if key not in allowed_solver_options: - raise Exception('Invalid solver_option: {}'.format(key)) + raise Exception("Invalid solver_option: {}".format(key)) solver_options = PulseSimOptions(**solver_options) # Set the ODE solver max step to be the half the # width of the smallest pulse min_width = np.iinfo(np.int32).max for key, val in pulse_de_model.pulse_to_int.items(): - if key != 'pv': + if key != "pv": stop = pulse_de_model.pulse_indices[val + 1] start = pulse_de_model.pulse_indices[val] min_width = min(min_width, stop - start) @@ -181,27 +187,24 @@ def pulse_controller(qobj): pulse_sim_desc.measurement_ops = [None] * n_qubits for exp in pulse_sim_desc.experiments: - # Add in measurement operators # Not sure if this will work for multiple measurements # Note: the extraction of multiple measurements works, but the simulation routines # themselves implicitly assume there is only one measurement at the end - if any(exp['acquire']): - for acq in exp['acquire']: + if any(exp["acquire"]): + for acq in exp["acquire"]: for jj in acq[1]: if jj > qubit_list[-1]: continue if not pulse_sim_desc.measurement_ops[qubit_list.index(jj)]: q_level_meas = pulse_sim_desc.q_level_meas - pulse_sim_desc.measurement_ops[qubit_list.index(jj)] = \ - op_gen.qubit_occ_oper_dressed(jj, - estates, - h_osc=dim_osc, - h_qub=dim_qub, - level=q_level_meas - ) - - if not exp['can_sample']: + pulse_sim_desc.measurement_ops[ + qubit_list.index(jj) + ] = op_gen.qubit_occ_oper_dressed( + jj, estates, h_osc=dim_osc, h_qub=dim_qub, level=q_level_meas + ) + + if not exp["can_sample"]: pulse_sim_desc.can_sample = False # trim measurement operators to relevant qubits once constructed @@ -211,20 +214,21 @@ def pulse_controller(qobj): meas_ops_reduced.append(op) pulse_sim_desc.measurement_ops = meas_ops_reduced - run_experiments = (run_unitary_experiments if pulse_sim_desc.can_sample - else run_monte_carlo_experiments) + run_experiments = ( + run_unitary_experiments if pulse_sim_desc.can_sample else run_monte_carlo_experiments + ) exp_results, exp_times = run_experiments(pulse_sim_desc, pulse_de_model, solver_options) output = { - 'results': format_exp_results(exp_results, exp_times, pulse_sim_desc), - 'success': True, - 'qobj_id': qobj.qobj_id + "results": format_exp_results(exp_results, exp_times, pulse_sim_desc), + "success": True, + "qobj_id": qobj.qobj_id, } return output def format_exp_results(exp_results, exp_times, pulse_sim_desc): - """ format simulation results + """format simulation results Parameters: exp_results (list): simulation results @@ -238,28 +242,28 @@ def format_exp_results(exp_results, exp_times, pulse_sim_desc): # format the data into the proper output all_results = [] for idx_exp, exp in enumerate(pulse_sim_desc.experiments): - m_lev = pulse_sim_desc.meas_level m_ret = pulse_sim_desc.meas_return # populate the results dictionary - results = {'seed_simulator': exp['seed'], - 'shots': pulse_sim_desc.shots, - 'status': 'DONE', - 'success': True, - 'time_taken': exp_times[idx_exp], - 'header': exp['header'], - 'meas_level': m_lev, - 'meas_return': m_ret, - 'data': {}} + results = { + "seed_simulator": exp["seed"], + "shots": pulse_sim_desc.shots, + "status": "DONE", + "success": True, + "time_taken": exp_times[idx_exp], + "header": exp["header"], + "meas_level": m_lev, + "meas_return": m_ret, + "data": {}, + } if pulse_sim_desc.can_sample: memory = exp_results[idx_exp][0] - results['data']['statevector'] = [] + results["data"]["statevector"] = [] for coef in exp_results[idx_exp][1]: - results['data']['statevector'].append([np.real(coef), - np.imag(coef)]) - results['header']['ode_t'] = exp_results[idx_exp][2] + results["data"]["statevector"].append([np.real(coef), np.imag(coef)]) + results["header"]["ode_t"] = exp_results[idx_exp][2] else: memory = exp_results[idx_exp] @@ -268,13 +272,12 @@ def format_exp_results(exp_results, exp_times, pulse_sim_desc): # convert the memory **array** into a n # integer # e.g. [1,0] -> 2 - int_mem = memory.dot(np.power(2.0, - np.arange(memory.shape[1]))).astype(int) + int_mem = memory.dot(np.power(2.0, np.arange(memory.shape[1]))).astype(int) # if the memory flag is set return each shot if pulse_sim_desc.memory: hex_mem = [hex(val) for val in int_mem] - results['data']['memory'] = hex_mem + results["data"]["memory"] = hex_mem # Get hex counts dict unique = np.unique(int_mem, return_counts=True) @@ -282,32 +285,29 @@ def format_exp_results(exp_results, exp_times, pulse_sim_desc): for kk in range(unique[0].shape[0]): key = hex(unique[0][kk]) hex_dict[key] = unique[1][kk] - results['data']['counts'] = hex_dict + results["data"]["counts"] = hex_dict # meas_level 1 returns the elif m_lev == 1: - - if m_ret == 'avg': - + if m_ret == "avg": memory = [np.mean(memory, 0)] # convert into the right [real, complex] pair form for json - results['data']['memory'] = [] + results["data"]["memory"] = [] for mem_shot in memory: - results['data']['memory'].append([]) + results["data"]["memory"].append([]) for mem_slot in mem_shot: - results['data']['memory'][-1].append( - [np.real(mem_slot), np.imag(mem_slot)]) + results["data"]["memory"][-1].append([np.real(mem_slot), np.imag(mem_slot)]) - if m_ret == 'avg': - results['data']['memory'] = results['data']['memory'][0] + if m_ret == "avg": + results["data"]["memory"] = results["data"]["memory"][0] all_results.append(results) return all_results def _unsupported_warnings(noise_model): - """ Warns the user about untested/unsupported features. + """Warns the user about untested/unsupported features. Parameters: noise_model (dict): backend_options for simulation @@ -317,14 +317,13 @@ def _unsupported_warnings(noise_model): """ # Warnings that don't stop execution - warning_str = '{} are an untested feature, and therefore may not behave as expected.' + warning_str = "{} are an untested feature, and therefore may not behave as expected." if noise_model is not None: - warn(warning_str.format('Noise models')) + warn(warning_str.format("Noise models")) class PulseInternalDEModel: - """Container of information required for de RHS construction - """ + """Container of information required for de RHS construction""" def __init__(self): # The system Hamiltonian in numerical format @@ -372,8 +371,7 @@ def __init__(self): self._rhs_dict = None def _config_internal_data(self): - """Preps internal data into format required by RHS function. - """ + """Preps internal data into format required by RHS function.""" self.vars = list(self.variables.values()) # Need this info for evaluating the hamiltonian vars in the c++ solver @@ -411,14 +409,16 @@ def _config_internal_data(self): # construct data sets self.h_ops_data = [-1.0j * hpart.data for hpart in H] - self._rhs_dict = {'freqs': list(self.freqs.values()), - 'pulse_array': self.pulse_array, - 'pulse_indices': self.pulse_indices, - 'vars': self.vars, - 'vars_names': self.vars_names, - 'num_h_terms': self.num_h_terms, - 'h_ops_data': self.h_ops_data, - 'h_diag_elems': self.h_diag_elems} + self._rhs_dict = { + "freqs": list(self.freqs.values()), + "pulse_array": self.pulse_array, + "pulse_indices": self.pulse_indices, + "vars": self.vars, + "vars_names": self.vars_names, + "num_h_terms": self.num_h_terms, + "h_ops_data": self.h_ops_data, + "h_diag_elems": self.h_diag_elems, + } def init_rhs(self, exp): """Set up and return rhs function corresponding to this model for a given @@ -443,9 +443,9 @@ def rhs(t, y): return rhs -def setup_rhs_dict_freqs(default_rhs_dict: dict, - exp: dict, - calculate_channel_frequencies: Callable): +def setup_rhs_dict_freqs( + default_rhs_dict: dict, exp: dict, calculate_channel_frequencies: Callable +): """Standalone function for overriding channel frequencies in a given experiment. Args: @@ -458,19 +458,20 @@ def setup_rhs_dict_freqs(default_rhs_dict: dict, dict: Dictionary with frequencies potentially overriden by those in exp. """ - if 'qubit_lo_freq' in exp and exp['qubit_lo_freq'] is not None: + if "qubit_lo_freq" in exp and exp["qubit_lo_freq"] is not None: # copy to not overwrite defaults default_rhs_dict = copy(default_rhs_dict) - freqs_dict = calculate_channel_frequencies(exp['qubit_lo_freq']) - default_rhs_dict['freqs'] = list(freqs_dict.values()) + freqs_dict = calculate_channel_frequencies(exp["qubit_lo_freq"]) + default_rhs_dict["freqs"] = list(freqs_dict.values()) return default_rhs_dict class PulseSimDescription: - """ Object for holding any/all information required for simulation. + """Object for holding any/all information required for simulation. Needs to be refactored into different pieces. """ + def __init__(self): self.initial_state = None # Channels in the Hamiltonian string diff --git a/qiskit_aer/pulse/controllers/pulse_de_solver.py b/qiskit_aer/pulse/controllers/pulse_de_solver.py index 16c93b790b..ab6081f06b 100644 --- a/qiskit_aer/pulse/controllers/pulse_de_solver.py +++ b/qiskit_aer/pulse/controllers/pulse_de_solver.py @@ -24,7 +24,7 @@ def setup_de_solver(exp, y0, pulse_de_model, de_options): - """ Constructs a scipy ODE solver for a given exp and op_system + """Constructs a scipy ODE solver for a given exp and op_system Parameters: exp (dict): dict containing experiment description diff --git a/qiskit_aer/pulse/controllers/pulse_sim_options.py b/qiskit_aer/pulse/controllers/pulse_sim_options.py index 9357078803..5ceefb7b9c 100644 --- a/qiskit_aer/pulse/controllers/pulse_sim_options.py +++ b/qiskit_aer/pulse/controllers/pulse_sim_options.py @@ -17,7 +17,7 @@ from ..de.DE_Options import DE_Options -class PulseSimOptions(): +class PulseSimOptions: """ Class of options for pulse solver routines. Options can be specified either as arguments to the constructor:: @@ -54,35 +54,38 @@ class PulseSimOptions(): of the evolution. """ - def __init__(self, - method='zvode-adams', - atol=1e-8, - rtol=1e-6, - order=12, - nsteps=10**6, - first_step=None, - max_step=None, - min_step=None, - max_dt=10**-3, - num_cpus=0, - norm_tol=1e-3, - norm_steps=5, - progress_bar=True, - shots=1024, - store_final_state=False, - seeds=None, - reuse_seeds=False): - + def __init__( + self, + method="zvode-adams", + atol=1e-8, + rtol=1e-6, + order=12, + nsteps=10**6, + first_step=None, + max_step=None, + min_step=None, + max_dt=10**-3, + num_cpus=0, + norm_tol=1e-3, + norm_steps=5, + progress_bar=True, + shots=1024, + store_final_state=False, + seeds=None, + reuse_seeds=False, + ): # set DE specific options - self.de_options = DE_Options(method=method, - atol=atol, - rtol=rtol, - order=order, - nsteps=nsteps, - first_step=first_step, - max_step=max_step, - min_step=min_step, - max_dt=max_dt) + self.de_options = DE_Options( + method=method, + atol=atol, + rtol=rtol, + order=order, + nsteps=nsteps, + first_step=first_step, + max_step=max_step, + min_step=min_step, + max_dt=max_dt, + ) self.shots = shots self.seeds = seeds @@ -95,23 +98,25 @@ def __init__(self, def copy(self): """Create a copy.""" - return PulseSimOptions(method=self.de_options.method, - atol=self.de_options.atol, - rtol=self.de_options.rtol, - order=self.de_options.order, - nsteps=self.de_options.nsteps, - first_step=self.de_options.first_step, - max_step=self.de_options.max_step, - min_step=self.de_options.min_step, - max_dt=self.de_options.max_dt, - num_cpus=self.num_cpus, - norm_tol=self.norm_tol, - norm_steps=self.norm_steps, - progress_bar=self.progress_bar, - shots=self.shots, - store_final_state=self.store_final_state, - seeds=self.seeds, - reuse_seeds=self.reuse_seeds) + return PulseSimOptions( + method=self.de_options.method, + atol=self.de_options.atol, + rtol=self.de_options.rtol, + order=self.de_options.order, + nsteps=self.de_options.nsteps, + first_step=self.de_options.first_step, + max_step=self.de_options.max_step, + min_step=self.de_options.min_step, + max_dt=self.de_options.max_dt, + num_cpus=self.num_cpus, + norm_tol=self.norm_tol, + norm_steps=self.norm_steps, + progress_bar=self.progress_bar, + shots=self.shots, + store_final_state=self.store_final_state, + seeds=self.seeds, + reuse_seeds=self.reuse_seeds, + ) def __str__(self): return str(vars(self)) diff --git a/qiskit_aer/pulse/controllers/unitary_controller.py b/qiskit_aer/pulse/controllers/unitary_controller.py index 0397e12f25..29a8afb8e5 100644 --- a/qiskit_aer/pulse/controllers/unitary_controller.py +++ b/qiskit_aer/pulse/controllers/unitary_controller.py @@ -42,7 +42,7 @@ def _full_simulation(exp, y0, pulse_sim_desc, pulse_de_model, solver_options=Non # ############### # do measurement # ############### - rng = np.random.RandomState(exp['seed']) + rng = np.random.RandomState(exp["seed"]) shots = pulse_sim_desc.shots # Init memory @@ -50,13 +50,13 @@ def _full_simulation(exp, y0, pulse_sim_desc, pulse_de_model, solver_options=Non qubits = [] memory_slots = [] - tlist = exp['tlist'] - for acq in exp['acquire']: + tlist = exp["tlist"] + for acq in exp["acquire"]: if acq[0] == tlist[-1]: qubits += list(acq[1]) memory_slots += list(acq[2]) - qubits = np.array(qubits, dtype='uint32') - memory_slots = np.array(memory_slots, dtype='uint32') + qubits = np.array(qubits, dtype="uint32") + memory_slots = np.array(memory_slots, dtype="uint32") probs = occ_probabilities(qubits, psi, pulse_sim_desc.measurement_ops) rand_vals = rng.rand(memory_slots.shape[0] * shots) @@ -66,7 +66,7 @@ def _full_simulation(exp, y0, pulse_sim_desc, pulse_de_model, solver_options=Non def run_unitary_experiments(pulse_sim_desc, pulse_de_model, solver_options=None): - """ Runs unitary experiments for a given op_system + """Runs unitary experiments for a given op_system Parameters: pulse_sim_desc (PulseSimDescription): description of pulse simulation @@ -95,20 +95,27 @@ def run_unitary_experiments(pulse_sim_desc, pulse_de_model, solver_options=None) seed = pulse_sim_desc.seed or np.random.randint(np.iinfo(np.int32).max - 1) prng = np.random.RandomState(seed) for exp in pulse_sim_desc.experiments: - exp['seed'] = prng.randint(np.iinfo(np.int32).max - 1) + exp["seed"] = prng.randint(np.iinfo(np.int32).max - 1) - map_kwargs = {'num_processes': solver_options.num_cpus} + map_kwargs = {"num_processes": solver_options.num_cpus} # run simulation on each experiment in parallel start = time.time() - exp_results = parallel_map(_full_simulation, - pulse_sim_desc.experiments, - task_args=(y0, pulse_sim_desc, pulse_de_model, solver_options, ), - **map_kwargs - ) + exp_results = parallel_map( + _full_simulation, + pulse_sim_desc.experiments, + task_args=( + y0, + pulse_sim_desc, + pulse_de_model, + solver_options, + ), + **map_kwargs, + ) end = time.time() - exp_times = (np.ones(len(pulse_sim_desc.experiments)) * - (end - start) / len(pulse_sim_desc.experiments)) + exp_times = ( + np.ones(len(pulse_sim_desc.experiments)) * (end - start) / len(pulse_sim_desc.experiments) + ) return exp_results, exp_times @@ -135,14 +142,14 @@ def unitary_evolution(exp, y0, pulse_de_model, solver_options=None): ODE = setup_de_solver(exp, y0, pulse_de_model, solver_options.de_options) - tlist = exp['tlist'] + tlist = exp["tlist"] for t in tlist[1:]: ODE.integrate(t) if ODE.successful(): psi = ODE.y / dznrm2(ODE.y) else: - err_msg = 'ODE method exited with status: %s' % ODE.return_code() + err_msg = "ODE method exited with status: %s" % ODE.return_code() raise Exception(err_msg) # apply final rotation to come out of rotating frame diff --git a/qiskit_aer/pulse/de/DE_Methods.py b/qiskit_aer/pulse/de/DE_Methods.py index f0994c4aaa..812cd53618 100644 --- a/qiskit_aer/pulse/de/DE_Methods.py +++ b/qiskit_aer/pulse/de/DE_Methods.py @@ -45,10 +45,9 @@ class ODE_Method(ABC): rhs (dict): rhs-related functions as values Currently supports key 'rhs'. """ - method_spec = {'inner_state_spec': {'type': 'array'}} + method_spec = {"inner_state_spec": {"type": "array"}} def __init__(self, t0=None, y0=None, rhs=None, options=None): - # set_options is first as options may influence the behaviour of other functions self.set_options(options) @@ -110,14 +109,14 @@ def y(self, new_y): self.set_y(new_y) def set_y(self, new_y, reset=True): - """Method for logic of setting internal state of solver with more control - """ + """Method for logic of setting internal state of solver with more control""" # instantiate internal StateTypeConverter based on the provided new_y and the # general type required internally by the solver - type_spec = self.method_spec.get('inner_state_spec') - self._state_type_converter = \ - StateTypeConverter.from_outer_instance_inner_type_spec(new_y, type_spec) + type_spec = self.method_spec.get("inner_state_spec") + self._state_type_converter = StateTypeConverter.from_outer_instance_inner_type_spec( + new_y, type_spec + ) # set internal state self._y = self._state_type_converter.outer_to_inner(new_y) @@ -138,13 +137,13 @@ def set_rhs(self, rhs=None, reset=True): """ if rhs is None: - rhs = {'rhs': None} + rhs = {"rhs": None} if callable(rhs): - rhs = {'rhs': rhs} + rhs = {"rhs": rhs} - if 'rhs' not in rhs: - raise Exception('ODE_Method requires at minimum a specification of an rhs function.') + if "rhs" not in rhs: + raise Exception("ODE_Method requires at minimum a specification of an rhs function.") # transform rhs function into a function that accepts/returns inner state type self.rhs = self._state_type_converter.transform_rhs_funcs(rhs) @@ -198,26 +197,29 @@ class ScipyODE(ODE_Method): do not handle complex types """ - method_spec = {'inner_state_spec': {'type': 'array', 'ndim': 1}} + method_spec = {"inner_state_spec": {"type": "array", "ndim": 1}} def integrate(self, tf, **kwargs): - """Integrate up to a time tf. - """ + """Integrate up to a time tf.""" t0 = self.t y0 = self._y - rhs = self.rhs.get('rhs') + rhs = self.rhs.get("rhs") # solve problem and silence warnings for options that don't apply to a given method kept_warnings = [] with warnings.catch_warnings(record=True) as ws: - results = solve_ivp(rhs, (t0, tf), y0, - method=self.options.method, - atol=self.options.atol, - rtol=self.options.rtol, - max_step=self.options.max_step, - min_step=self.options.min_step, - first_step=self.options.first_step, - **kwargs) + results = solve_ivp( + rhs, + (t0, tf), + y0, + method=self.options.method, + atol=self.options.atol, + rtol=self.options.rtol, + max_step=self.options.max_step, + min_step=self.options.min_step, + first_step=self.options.first_step, + **kwargs, + ) # update the internal state self._y = results.y[:, -1] @@ -225,7 +227,7 @@ def integrate(self, tf, **kwargs): # discard warnings for arguments with no effect for w in ws: - if 'The following arguments have no effect' not in str(w.message): + if "The following arguments have no effect" not in str(w.message): kept_warnings.append(w) # display warnings we don't want to silence @@ -236,10 +238,10 @@ def set_options(self, options): # establish method if options is None: options = DE_Options() - options.method = 'RK45' + options.method = "RK45" else: options = options.copy() - if 'scipy-' in options.method: + if "scipy-" in options.method: options.method = options.method[6:] self.options = options @@ -256,13 +258,12 @@ class QiskitZVODE(ODE_Method): - Internally this """ - method_spec = {'inner_state_spec': {'type': 'array', 'ndim': 1}} + method_spec = {"inner_state_spec": {"type": "array", "ndim": 1}} def __init__(self, t0=None, y0=None, rhs=None, options=None): - # all de specification arguments are necessary to instantiate scipy ode object if (t0 is None) or (y0 is None) or (rhs is None): - raise Exception('QiskitZVODE solver requires t0, y0, and rhs at instantiation.') + raise Exception("QiskitZVODE solver requires t0, y0, and rhs at instantiation.") # initialize internal attribute for storing scipy ode object self._ODE = None @@ -280,11 +281,11 @@ def t(self, new_t): self._reset_method() def set_y(self, new_y, reset=True): - """Method for logic of setting internal state of solver with more control - """ - type_spec = self.method_spec.get('inner_state_spec') - self._state_type_converter = \ - StateTypeConverter.from_outer_instance_inner_type_spec(new_y, type_spec) + """Method for logic of setting internal state of solver with more control""" + type_spec = self.method_spec.get("inner_state_spec") + self._state_type_converter = StateTypeConverter.from_outer_instance_inner_type_spec( + new_y, type_spec + ) self._y = self._state_type_converter.outer_to_inner(new_y) @@ -297,27 +298,28 @@ def set_rhs(self, rhs=None, reset=True): """This set_rhs function fully instantiates the scipy ode object behind the scenes.""" if rhs is None: - rhs = {'rhs': None} + rhs = {"rhs": None} if callable(rhs): - rhs = {'rhs': rhs} + rhs = {"rhs": rhs} - if 'rhs' not in rhs: - raise Exception('ODE_Method requires at minimum a specification of an rhs function.') + if "rhs" not in rhs: + raise Exception("ODE_Method requires at minimum a specification of an rhs function.") self.rhs = self._state_type_converter.transform_rhs_funcs(rhs) - self._ODE = ode(self.rhs['rhs']) + self._ODE = ode(self.rhs["rhs"]) - self._ODE._integrator = qiskit_zvode(method=self.options.method, - order=self.options.order, - atol=self.options.atol, - rtol=self.options.rtol, - nsteps=self.options.nsteps, - first_step=self.options.first_step, - min_step=self.options.min_step, - max_step=self.options.max_step - ) + self._ODE._integrator = qiskit_zvode( + method=self.options.method, + order=self.options.order, + atol=self.options.atol, + rtol=self.options.rtol, + nsteps=self.options.nsteps, + first_step=self.options.first_step, + min_step=self.options.min_step, + max_step=self.options.max_step, + ) # Forces complex ODE solving if not self._ODE._y: @@ -339,7 +341,7 @@ def integrate(self, tf, **kwargs): single step of the solver """ - step = kwargs.get('step', False) + step = kwargs.get("step", False) self._ODE.integrate(tf, step=step) @@ -359,10 +361,10 @@ def _reset_method(self, reset=True): def set_options(self, options): # establish method if options is None: - options = DE_Options(method='adams') + options = DE_Options(method="adams") else: options = options.copy() - if 'zvode-' in options.method: + if "zvode-" in options.method: options.method = options.method[6:] # handle None-type defaults @@ -383,6 +385,7 @@ class qiskit_zvode(zvode): it always stops at a given time in tlist; by default, it over shoots the time. """ + def step(self, *args): itask = self.call_args[2] self.rwork[0] = args[4] @@ -399,8 +402,7 @@ class RK4(ODE_Method): """ def integrate(self, tf, **kwargs): - """Integrate up to a time tf. - """ + """Integrate up to a time tf.""" delta_t = tf - self.t steps = int((delta_t // self._max_dt) + 1) @@ -409,11 +411,10 @@ def integrate(self, tf, **kwargs): self._integration_step(h) def _integration_step(self, h): - """Integration step for RK4 - """ + """Integration step for RK4""" y0 = self._y t0 = self._t - rhs = self.rhs.get('rhs') + rhs = self.rhs.get("rhs") k1 = rhs(t0, y0) t_mid = t0 + (h / 2) @@ -421,7 +422,7 @@ def _integration_step(self, h): k3 = rhs(t_mid, y0 + (h * k2 / 2)) t_end = t0 + h k4 = rhs(t_end, y0 + h * k3) - self._y = y0 + (1. / 6) * h * (k1 + (2 * k2) + (2 * k3) + k4) + self._y = y0 + (1.0 / 6) * h * (k1 + (2 * k2) + (2 * k3) + k4) self._t = t_end def set_options(self, options): @@ -438,14 +439,12 @@ def method_from_string(method_str): method: instance of an ODE_Method object """ - if 'scipy-' in method_str: + if "scipy-" in method_str: return ScipyODE - if 'zvode-' in method_str: + if "zvode-" in method_str: return QiskitZVODE - method_dict = {'RK4': RK4, - 'scipy': ScipyODE, - 'zvode': QiskitZVODE} + method_dict = {"RK4": RK4, "scipy": ScipyODE, "zvode": QiskitZVODE} return method_dict.get(method_str) diff --git a/qiskit_aer/pulse/de/DE_Options.py b/qiskit_aer/pulse/de/DE_Options.py index 2825000d99..5fe1081326 100644 --- a/qiskit_aer/pulse/de/DE_Options.py +++ b/qiskit_aer/pulse/de/DE_Options.py @@ -45,17 +45,18 @@ class DE_Options: max_dt (float, 1e-3): Max step size for fixed step solver. """ - def __init__(self, - method='zvode-adams', - atol=1e-8, - rtol=1e-6, - order=12, - nsteps=10**6, - first_step=None, - max_step=None, - min_step=None, - max_dt=10**-3): - + def __init__( + self, + method="zvode-adams", + atol=1e-8, + rtol=1e-6, + order=12, + nsteps=10**6, + first_step=None, + max_step=None, + min_step=None, + max_dt=10**-3, + ): self.method = method self.atol = atol self.rtol = rtol @@ -68,15 +69,17 @@ def __init__(self, def copy(self): """Create a copy of the object.""" - return DE_Options(method=self.method, - atol=self.atol, - rtol=self.rtol, - order=self.order, - nsteps=self.nsteps, - first_step=self.first_step, - max_step=self.max_step, - min_step=self.min_step, - max_dt=self.max_dt) + return DE_Options( + method=self.method, + atol=self.atol, + rtol=self.rtol, + order=self.order, + nsteps=self.nsteps, + first_step=self.first_step, + max_step=self.max_step, + min_step=self.min_step, + max_dt=self.max_dt, + ) def __str__(self): return str(vars(self)) diff --git a/qiskit_aer/pulse/de/type_utils.py b/qiskit_aer/pulse/de/type_utils.py index 5d671c0637..e445c81df1 100644 --- a/qiskit_aer/pulse/de/type_utils.py +++ b/qiskit_aer/pulse/de/type_utils.py @@ -90,27 +90,27 @@ def from_outer_instance_inner_type_spec(cls, outer_y, inner_type_spec=None): if inner_type_spec is None: return cls.from_instances(outer_y) - inner_type = inner_type_spec.get('type') + inner_type = inner_type_spec.get("type") if inner_type is None: raise Exception("inner_type_spec needs a 'type' key.") - if inner_type == 'array': + if inner_type == "array": outer_y_as_array = np.array(outer_y) # if a specific shape is given attempt to instantiate from a reshaped outer_y - shape = inner_type_spec.get('shape') + shape = inner_type_spec.get("shape") if shape is not None: return cls.from_instances(outer_y_as_array.reshape(shape), outer_y) # handle the case that ndim == 1 is given - ndim = inner_type_spec.get('ndim') + ndim = inner_type_spec.get("ndim") if ndim == 1: return cls.from_instances(outer_y_as_array.flatten(), outer_y) # if neither shape nor ndim is given, assume it can be an array of any shape return cls.from_instances(outer_y_as_array, outer_y) - raise Exception('inner_type_spec not a handled type.') + raise Exception("inner_type_spec not a handled type.") def inner_to_outer(self, y): """Convert a state of inner type to one of outer type.""" @@ -137,15 +137,16 @@ def transform_rhs_funcs(self, rhs_funcs): new_rhs_funcs = {} # transform standard rhs function - rhs = rhs_funcs.get('rhs') + rhs = rhs_funcs.get("rhs") if rhs is not None: + def new_rhs(t, y): outer_y = self.inner_to_outer(y) rhs_val = rhs(t, outer_y) return self.outer_to_inner(rhs_val) - new_rhs_funcs['rhs'] = new_rhs + new_rhs_funcs["rhs"] = new_rhs return new_rhs_funcs @@ -156,11 +157,11 @@ def convert_state(y, type_spec): new_y = None - if type_spec['type'] == 'array': + if type_spec["type"] == "array": # default array data type to complex - new_y = np.array(y, dtype=type_spec.get('dtype', 'complex')) + new_y = np.array(y, dtype=type_spec.get("dtype", "complex")) - shape = type_spec.get('shape') + shape = type_spec.get("shape") if shape is not None: new_y = new_y.reshape(shape) @@ -171,7 +172,7 @@ def type_spec_from_instance(y): """Determine type spec from an instance.""" type_spec = {} if isinstance(y, np.ndarray): - type_spec['type'] = 'array' - type_spec['shape'] = y.shape + type_spec["type"] = "array" + type_spec["shape"] = y.shape return type_spec diff --git a/qiskit_aer/pulse/system_models/duffing_model_generators.py b/qiskit_aer/pulse/system_models/duffing_model_generators.py index 4fc5431971..fd1cc09039 100644 --- a/qiskit_aer/pulse/system_models/duffing_model_generators.py +++ b/qiskit_aer/pulse/system_models/duffing_model_generators.py @@ -22,12 +22,9 @@ from .pulse_system_model import PulseSystemModel -def duffing_system_model(dim_oscillators, - oscillator_freqs, - anharm_freqs, - drive_strengths, - coupling_dict, - dt): +def duffing_system_model( + dim_oscillators, oscillator_freqs, anharm_freqs, drive_strengths, coupling_dict, dt +): r"""Returns a :class:`PulseSystemModel` representing a physical model for a collection of Duffing oscillators. @@ -120,25 +117,27 @@ def duffing_system_model(dim_oscillators, """ # set symbols for string generation - freq_symbol = 'v' - anharm_symbol = 'alpha' - drive_symbol = 'r' - coupling_symbol = 'j' + freq_symbol = "v" + anharm_symbol = "alpha" + drive_symbol = "r" + coupling_symbol = "j" coupling_edges = coupling_dict.keys() # construct coupling graph, and raise warning if coupling_edges contains duplicate edges coupling_graph = CouplingGraph(coupling_edges) if len(coupling_graph.graph) < len(coupling_edges): - warn('Warning: The coupling_dict contains diplicate edges, and the second appearance of \ - the same edge will be ignored.') + warn( + "Warning: The coupling_dict contains diplicate edges, and the second appearance of \ + the same edge will be ignored." + ) # construct the HamiltonianModel num_oscillators = len(oscillator_freqs) oscillators = list(range(num_oscillators)) oscillator_dims = [dim_oscillators] * num_oscillators - freq_symbols = _str_list_generator(freq_symbol + '{0}', oscillators) - anharm_symbols = _str_list_generator(anharm_symbol + '{0}', oscillators) - drive_symbols = _str_list_generator(drive_symbol + '{0}', oscillators) + freq_symbols = _str_list_generator(freq_symbol + "{0}", oscillators) + anharm_symbols = _str_list_generator(anharm_symbol + "{0}", oscillators) + drive_symbols = _str_list_generator(drive_symbol + "{0}", oscillators) sorted_coupling_edges = coupling_graph.sorted_graph # populate coupling strengths in sorted order (vertex indices are now also sorted within edges, # so this needs to be accounted for when retrieving weights from coupling_dict) @@ -149,21 +148,23 @@ def duffing_system_model(dim_oscillators, edge_strength = coupling_dict.get((edge[1], edge[0])) coupling_strengths.append(edge_strength) - coupling_symbols = _str_list_generator(coupling_symbol + '{0}{1}', *zip(*sorted_coupling_edges)) + coupling_symbols = _str_list_generator(coupling_symbol + "{0}{1}", *zip(*sorted_coupling_edges)) cr_idx_dict = coupling_graph.two_way_graph_dict - hamiltonian_dict = _duffing_hamiltonian_dict(oscillators=oscillators, - oscillator_dims=oscillator_dims, - oscillator_freqs=oscillator_freqs, - freq_symbols=freq_symbols, - anharm_freqs=anharm_freqs, - anharm_symbols=anharm_symbols, - drive_strengths=drive_strengths, - drive_symbols=drive_symbols, - ordered_coupling_edges=sorted_coupling_edges, - coupling_strengths=coupling_strengths, - coupling_symbols=coupling_symbols, - cr_idx_dict=cr_idx_dict) + hamiltonian_dict = _duffing_hamiltonian_dict( + oscillators=oscillators, + oscillator_dims=oscillator_dims, + oscillator_freqs=oscillator_freqs, + freq_symbols=freq_symbols, + anharm_freqs=anharm_freqs, + anharm_symbols=anharm_symbols, + drive_strengths=drive_strengths, + drive_symbols=drive_symbols, + ordered_coupling_edges=sorted_coupling_edges, + coupling_strengths=coupling_strengths, + coupling_symbols=coupling_symbols, + cr_idx_dict=cr_idx_dict, + ) hamiltonian_model = HamiltonianModel.from_dict(hamiltonian_dict) @@ -171,28 +172,32 @@ def duffing_system_model(dim_oscillators, u_channel_lo = _cr_lo_list(cr_idx_dict) # construct and return the PulseSystemModel - return PulseSystemModel(hamiltonian=hamiltonian_model, - u_channel_lo=u_channel_lo, - control_channel_labels=coupling_graph.sorted_two_way_graph, - subsystem_list=oscillators, - dt=dt) + return PulseSystemModel( + hamiltonian=hamiltonian_model, + u_channel_lo=u_channel_lo, + control_channel_labels=coupling_graph.sorted_two_way_graph, + subsystem_list=oscillators, + dt=dt, + ) # Helper functions for creating pieces necessary to construct oscillator system models -def _duffing_hamiltonian_dict(oscillators, - oscillator_dims, - oscillator_freqs, - freq_symbols, - anharm_freqs, - anharm_symbols, - drive_strengths, - drive_symbols, - ordered_coupling_edges, - coupling_strengths, - coupling_symbols, - cr_idx_dict): +def _duffing_hamiltonian_dict( + oscillators, + oscillator_dims, + oscillator_freqs, + freq_symbols, + anharm_freqs, + anharm_symbols, + drive_strengths, + drive_symbols, + ordered_coupling_edges, + coupling_strengths, + coupling_symbols, + cr_idx_dict, +): """Creates a hamiltonian string dict for a duffing oscillator model Note, this function makes the following assumptions: @@ -254,7 +259,7 @@ def _duffing_hamiltonian_dict(oscillators, dim_dict = {str(oscillator): dim for oscillator, dim in zip(oscillators, oscillator_dims)} - return {'h_str': hamiltonian_str, 'vars': var_dict, 'qub': dim_dict} + return {"h_str": hamiltonian_str, "vars": var_dict, "qub": dim_dict} def _cr_lo_list(cr_idx_dict): @@ -293,13 +298,10 @@ def _single_duffing_drift_terms(freq_symbols, anharm_symbols, system_list): list: drift term strings """ - harm_terms = _str_list_generator('np.pi*(2*{0}-{1})*O{2}', - freq_symbols, - anharm_symbols, - system_list) - anharm_terms = _str_list_generator('np.pi*{0}*O{1}*O{1}', - anharm_symbols, - system_list) + harm_terms = _str_list_generator( + "np.pi*(2*{0}-{1})*O{2}", freq_symbols, anharm_symbols, system_list + ) + anharm_terms = _str_list_generator("np.pi*{0}*O{1}*O{1}", anharm_symbols, system_list) return harm_terms + anharm_terms @@ -314,9 +316,7 @@ def _drive_terms(drive_symbols, system_list): list: drive term strings """ - return _str_list_generator('2*np.pi*{0}*X{1}||D{1}', - drive_symbols, - system_list) + return _str_list_generator("2*np.pi*{0}*X{1}||D{1}", drive_symbols, system_list) def _exchange_coupling_terms(coupling_symbols, ordered_edges): @@ -331,10 +331,9 @@ def _exchange_coupling_terms(coupling_symbols, ordered_edges): idx1_list, idx2_list = zip(*list(ordered_edges)) - return _str_list_generator('2*np.pi*{0}*(Sp{1}*Sm{2}+Sm{1}*Sp{2})', - coupling_symbols, - idx1_list, - idx2_list) + return _str_list_generator( + "2*np.pi*{0}*(Sp{1}*Sm{2}+Sm{1}*Sp{2})", coupling_symbols, idx1_list, idx2_list + ) def _cr_terms(drive_symbols, driven_system_indices, u_channel_indices): @@ -348,10 +347,9 @@ def _cr_terms(drive_symbols, driven_system_indices, u_channel_indices): list: cr term strings """ - return _str_list_generator('2*np.pi*{0}*X{1}||U{2}', - drive_symbols, - driven_system_indices, - u_channel_indices) + return _str_list_generator( + "2*np.pi*{0}*X{1}||U{2}", drive_symbols, driven_system_indices, u_channel_indices + ) def _str_list_generator(str_template, *args): @@ -451,8 +449,9 @@ def __init__(self, edges): self.sorted_two_way_graph = two_way_graph_list # create the dictionary version - self.two_way_graph_dict = {self.sorted_two_way_graph[k]: k - for k in range(len(self.sorted_two_way_graph))} + self.two_way_graph_dict = { + self.sorted_two_way_graph[k]: k for k in range(len(self.sorted_two_way_graph)) + } def sorted_edge_index(self, edge): """Given an edge, returns the index in self.sorted_graph. Order in edge does not matter. diff --git a/qiskit_aer/pulse/system_models/hamiltonian_model.py b/qiskit_aer/pulse/system_models/hamiltonian_model.py index a4b6c90f1d..90978b3419 100644 --- a/qiskit_aer/pulse/system_models/hamiltonian_model.py +++ b/qiskit_aer/pulse/system_models/hamiltonian_model.py @@ -22,13 +22,10 @@ from .string_model_parser.string_model_parser import HamiltonianParser -class HamiltonianModel(): +class HamiltonianModel: """Hamiltonian model for pulse simulator.""" - def __init__(self, - system=None, - variables=None, - subsystem_dims=None): + def __init__(self, system=None, variables=None, subsystem_dims=None): """Initialize a Hamiltonian model. Args: @@ -65,7 +62,7 @@ def __init__(self, self._calculate_hamiltonian_channels() if len(self._channels) == 0: - raise AerError('HamiltonianModel must contain channels to simulate.') + raise AerError("HamiltonianModel must contain channels to simulate.") # populate self._h_diag, self._evals, self._estates self._compute_drift_data() @@ -89,51 +86,42 @@ def from_dict(cls, hamiltonian, subsystem_list=None): # get variables variables = OrderedDict() - if 'vars' in hamiltonian: - variables = OrderedDict(hamiltonian['vars']) + if "vars" in hamiltonian: + variables = OrderedDict(hamiltonian["vars"]) # Get qubit subspace dimensions - if 'qub' in hamiltonian: + if "qub" in hamiltonian: if subsystem_list is None: - subsystem_list = [int(qubit) for qubit in hamiltonian['qub']] + subsystem_list = [int(qubit) for qubit in hamiltonian["qub"]] else: # if user supplied, make a copy and sort it subsystem_list = subsystem_list.copy() subsystem_list.sort() # force keys in hamiltonian['qub'] to be ints - qub_dict = { - int(key): val - for key, val in hamiltonian['qub'].items() - } - - subsystem_dims = { - int(qubit): qub_dict[int(qubit)] - for qubit in subsystem_list - } + qub_dict = {int(key): val for key, val in hamiltonian["qub"].items()} + + subsystem_dims = {int(qubit): qub_dict[int(qubit)] for qubit in subsystem_list} else: subsystem_dims = {} # Get oscillator subspace dimensions - if 'osc' in hamiltonian: - oscillator_dims = { - int(key): val - for key, val in hamiltonian['osc'].items() - } + if "osc" in hamiltonian: + oscillator_dims = {int(key): val for key, val in hamiltonian["osc"].items()} else: oscillator_dims = {} # Parse the Hamiltonian - system = HamiltonianParser(h_str=hamiltonian['h_str'], - dim_osc=oscillator_dims, - dim_qub=subsystem_dims) + system = HamiltonianParser( + h_str=hamiltonian["h_str"], dim_osc=oscillator_dims, dim_qub=subsystem_dims + ) system.parse(subsystem_list) system = system.compiled return cls(system, variables, subsystem_dims) def get_qubit_lo_from_drift(self): - """ Computes a list of qubit frequencies corresponding to the exact energy + """Computes a list of qubit frequencies corresponding to the exact energy gap between the ground and first excited states of each qubit. If the keys in self._subsystem_dims skips over a qubit, it will default to outputting @@ -150,14 +138,13 @@ def get_qubit_lo_from_drift(self): min_eval = np.min(self._evals) for q_idx in self._subsystem_dims.keys(): single_excite = _first_excited_state(q_idx, self._subsystem_dims) - dressed_eval = _eval_for_max_espace_overlap( - single_excite, self._evals, self._estates) + dressed_eval = _eval_for_max_espace_overlap(single_excite, self._evals, self._estates) qubit_lo_freq[q_idx] = (dressed_eval - min_eval) / (2 * np.pi) return qubit_lo_freq def _calculate_hamiltonian_channels(self): - """ Get all the qubit channels D_i and U_i in the string + """Get all the qubit channels D_i and U_i in the string representation of a system Hamiltonian. Raises: @@ -165,13 +152,10 @@ def _calculate_hamiltonian_channels(self): """ channels = [] for _, ham_str in self._system: - chan_idx = [ - i for i, letter in enumerate(ham_str) if letter in ['D', 'U'] - ] + chan_idx = [i for i, letter in enumerate(ham_str) if letter in ["D", "U"]] for ch in chan_idx: if (ch + 1) == len(ham_str) or not ham_str[ch + 1].isdigit(): - raise Exception('Channel name must include' + - 'an integer labeling the qubit.') + raise Exception("Channel name must include" + "an integer labeling the qubit.") for kk in chan_idx: done = False offset = 0 @@ -183,7 +167,7 @@ def _calculate_hamiltonian_channels(self): elif (kk + offset + 1) == len(ham_str): done = True offset += 1 - temp_chan = ham_str[kk:kk + offset] + temp_chan = ham_str[kk : kk + offset] if temp_chan not in channels: channels.append(temp_chan) channels.sort(key=lambda x: (int(x[1:]), x[0])) @@ -206,12 +190,12 @@ def _compute_drift_data(self): # Get the diagonal elements of the hamiltonian with all the # drive terms set to zero for chan in self._channels: - exec('%s=0' % chan) + exec("%s=0" % chan) # might be a better solution to replace the 'var' in the hamiltonian # string with 'op_system.vars[var]' for var in self._variables: - exec('%s=%f' % (var, self._variables[var])) + exec("%s=%f" % (var, self._variables[var])) full_dim = np.prod(list(self._subsystem_dims.values())) @@ -234,7 +218,7 @@ def _compute_drift_data(self): pos = np.argmax(np.abs(estate_copy)) pos_list.append(pos) - min_overlap = min(np.abs(estate_copy)[pos]**2, min_overlap) + min_overlap = min(np.abs(estate_copy)[pos] ** 2, min_overlap) evals_mapped[pos] = evals[i] estates_mapped[:, pos] = estate @@ -254,15 +238,15 @@ def _hamiltonian_pre_parse_exceptions(hamiltonian): AerError: if some part of the hamiltonian dictionary is unsupported """ - ham_str = hamiltonian.get('h_str', []) - if ham_str in ([], ['']): + ham_str = hamiltonian.get("h_str", []) + if ham_str in ([], [""]): raise AerError("Hamiltonian dict requires a non-empty 'h_str' entry.") - if hamiltonian.get('qub', {}) == {}: + if hamiltonian.get("qub", {}) == {}: raise AerError("Hamiltonian dict requires non-empty 'qub' entry with subsystem dimensions.") - if hamiltonian.get('osc', {}) != {}: - raise AerError('Oscillator-type systems are not supported.') + if hamiltonian.get("osc", {}) != {}: + raise AerError("Oscillator-type systems are not supported.") def _first_excited_state(qubit_idx, subsystem_dims): @@ -279,7 +263,7 @@ def _first_excited_state(qubit_idx, subsystem_dims): Returns: vector: the state with qubit_idx in state 1, and the rest in state 0 """ - vector = np.array([1.]) + vector = np.array([1.0]) # iterate through qubits, tensoring on the state qubit_indices = [int(qubit) for qubit in subsystem_dims] qubit_indices.sort() diff --git a/qiskit_aer/pulse/system_models/pulse_system_model.py b/qiskit_aer/pulse/system_models/pulse_system_model.py index 74e8dde177..c4741d991f 100644 --- a/qiskit_aer/pulse/system_models/pulse_system_model.py +++ b/qiskit_aer/pulse/system_models/pulse_system_model.py @@ -22,7 +22,7 @@ from .hamiltonian_model import HamiltonianModel -class PulseSystemModel(): +class PulseSystemModel: r"""Physical model object for pulse simulator. This class contains model information required by the @@ -51,12 +51,15 @@ class PulseSystemModel(): system_model = PulseSystemModel.from_backend(armonk_backend) """ - def __init__(self, - hamiltonian=None, - u_channel_lo=None, - control_channel_labels=None, - subsystem_list=None, - dt=None): + + def __init__( + self, + hamiltonian=None, + u_channel_lo=None, + control_channel_labels=None, + subsystem_list=None, + dt=None, + ): """Initialize a PulseSystemModel. Args: @@ -101,7 +104,7 @@ def from_backend(cls, backend, subsystem_list=None): config = backend.configuration() if not config.open_pulse: - raise AerError('{} is not an open pulse backend'.format(backend)) + raise AerError("{} is not an open pulse backend".format(backend)) return cls.from_config(config, subsystem_list) @@ -114,8 +117,8 @@ def from_config(cls, configuration, subsystem_list=None): subsystem_list = subsystem_list or list(range(configuration.n_qubits)) ham_string = configuration.hamiltonian hamiltonian = HamiltonianModel.from_dict(ham_string, subsystem_list) - u_channel_lo = getattr(configuration, 'u_channel_lo', None) - dt = getattr(configuration, 'dt', None) + u_channel_lo = getattr(configuration, "u_channel_lo", None) + dt = getattr(configuration, "dt", None) control_channel_labels = [None] * len(u_channel_lo) # populate control_channel_dict @@ -126,12 +129,12 @@ def from_config(cls, configuration, subsystem_list=None): # find drive index drive_idx = None while drive_idx is None: - u_str_label = 'U{0}'.format(str(u_idx)) - for h_term_str in ham_string['h_str']: + u_str_label = "U{0}".format(str(u_idx)) + for h_term_str in ham_string["h_str"]: # check if this string corresponds to this u channel if u_str_label in h_term_str: # get index of X operator drive term - x_idx = h_term_str.find('X') + x_idx = h_term_str.find("X") # if 'X' is found, and is not at the end of the string, drive_idx # is the subsequent character if x_idx != -1 and x_idx + 1 < len(h_term_str): @@ -139,23 +142,25 @@ def from_config(cls, configuration, subsystem_list=None): if drive_idx is not None: # construct string for u channel - u_string = '' + u_string = "" for u_term_dict in u_lo: - scale = getattr(u_term_dict, 'scale', [1.0, 0]) - q_idx = getattr(u_term_dict, 'q') + scale = getattr(u_term_dict, "scale", [1.0, 0]) + q_idx = getattr(u_term_dict, "q") if len(u_string) > 0: - u_string += ' + ' + u_string += " + " if isinstance(scale, complex): - u_string += str(scale) + 'q' + str(q_idx) + u_string += str(scale) + "q" + str(q_idx) else: - u_string += str(scale[0] + scale[1] * 1j) + 'q' + str(q_idx) - control_channel_labels[u_idx] = {'driven_q': drive_idx, 'freq': u_string} + u_string += str(scale[0] + scale[1] * 1j) + "q" + str(q_idx) + control_channel_labels[u_idx] = {"driven_q": drive_idx, "freq": u_string} - return cls(hamiltonian=hamiltonian, - u_channel_lo=u_channel_lo, - control_channel_labels=control_channel_labels, - subsystem_list=subsystem_list, - dt=dt) + return cls( + hamiltonian=hamiltonian, + u_channel_lo=u_channel_lo, + control_channel_labels=control_channel_labels, + subsystem_list=subsystem_list, + dt=dt, + ) def control_channel_index(self, label): """Return the index of the control channel with identifying label. @@ -167,7 +172,7 @@ def control_channel_index(self, label): int or None: index of the ControlChannel """ if label not in self.control_channel_labels: - warn('There is no listed ControlChannel matching the provided label.') + warn("There is no listed ControlChannel matching the provided label.") return None else: return self.control_channel_labels.index(label) @@ -195,9 +200,9 @@ def calculate_channel_frequencies(self, qubit_lo_freq=None): freqs = OrderedDict() for key in self.hamiltonian._channels: chidx = int(key[1:]) - if key[0] == 'D': + if key[0] == "D": freqs[key] = qubit_lo_freq[chidx] - elif key[0] == 'U': + elif key[0] == "U": freqs[key] = 0 for u_lo_idx in self.u_channel_lo[chidx]: if u_lo_idx.q < len(qubit_lo_freq): diff --git a/qiskit_aer/pulse/system_models/string_model_parser/apply_str_func_to_qobj.py b/qiskit_aer/pulse/system_models/string_model_parser/apply_str_func_to_qobj.py index 43cf12757a..4e4646fc26 100644 --- a/qiskit_aer/pulse/system_models/string_model_parser/apply_str_func_to_qobj.py +++ b/qiskit_aer/pulse/system_models/string_model_parser/apply_str_func_to_qobj.py @@ -19,16 +19,14 @@ def dag(qobj): - """ Qiskit wrapper of adjoint - """ + """Qiskit wrapper of adjoint""" return qobj.dag() def apply_func(name, qobj): - """ Apply function of given name, or do nothing if func not found - """ + """Apply function of given name, or do nothing if func not found""" return __funcdict.get(name, lambda x: x)(qobj) # pylint: disable=invalid-name -__funcdict = {'dag': dag} +__funcdict = {"dag": dag} diff --git a/qiskit_aer/pulse/system_models/string_model_parser/gen_operator.py b/qiskit_aer/pulse/system_models/string_model_parser/gen_operator.py index 0c04087e68..9c957ec486 100644 --- a/qiskit_aer/pulse/system_models/string_model_parser/gen_operator.py +++ b/qiskit_aer/pulse/system_models/string_model_parser/gen_operator.py @@ -64,7 +64,7 @@ def sigmax(): Returns: Operator: Operator representation for sigma x. """ - return Operator.from_label('X') + return Operator.from_label("X") def sigmay(): @@ -72,7 +72,7 @@ def sigmay(): Returns: Operator: Operator representation for sigma y. """ - return Operator.from_label('Y') + return Operator.from_label("Y") def sigmaz(): @@ -80,7 +80,7 @@ def sigmaz(): Returns: Operator: Operator representation for sigma z. """ - return Operator.from_label('Z') + return Operator.from_label("Z") def identity(dim): diff --git a/qiskit_aer/pulse/system_models/string_model_parser/operator_from_string.py b/qiskit_aer/pulse/system_models/string_model_parser/operator_from_string.py index dc485f9d22..16a8509cf3 100644 --- a/qiskit_aer/pulse/system_models/string_model_parser/operator_from_string.py +++ b/qiskit_aer/pulse/system_models/string_model_parser/operator_from_string.py @@ -35,26 +35,23 @@ def gen_oper(opname, index, h_osc, h_qub, states=None): opr_tmp = None # get number of levels in Hilbert space - if opname in ['X', 'Y', 'Z', 'Sp', 'Sm', 'I', 'O', 'P']: + if opname in ["X", "Y", "Z", "Sp", "Sm", "I", "O", "P"]: is_qubit = True dim = h_qub.get(index, 2) - if opname in ['X', 'Y', 'Z'] and dim > 2: - if opname == 'X': - opr_tmp = (op_gen.get_oper('A', dim) + - op_gen.get_oper('C', dim)) - elif opname == 'Y': - opr_tmp = (-1j * op_gen.get_oper('A', dim) + - 1j * op_gen.get_oper('C', dim)) + if opname in ["X", "Y", "Z"] and dim > 2: + if opname == "X": + opr_tmp = op_gen.get_oper("A", dim) + op_gen.get_oper("C", dim) + elif opname == "Y": + opr_tmp = -1j * op_gen.get_oper("A", dim) + 1j * op_gen.get_oper("C", dim) else: - opr_tmp = (op_gen.get_oper('I', dim) - - 2 * op_gen.get_oper('N', dim)) + opr_tmp = op_gen.get_oper("I", dim) - 2 * op_gen.get_oper("N", dim) else: is_qubit = False dim = h_osc.get(index, 5) - if opname == 'P': + if opname == "P": opr_tmp = op_gen.get_oper(opname, dim, states) else: if opr_tmp is None: diff --git a/qiskit_aer/pulse/system_models/string_model_parser/operator_generators.py b/qiskit_aer/pulse/system_models/string_model_parser/operator_generators.py index 3fbcd770fb..755100d146 100644 --- a/qiskit_aer/pulse/system_models/string_model_parser/operator_generators.py +++ b/qiskit_aer/pulse/system_models/string_model_parser/operator_generators.py @@ -21,99 +21,85 @@ def sigmax(dim=2): - """Qiskit wrapper of sigma-X operator. - """ + """Qiskit wrapper of sigma-X operator.""" if dim == 2: return gen_operator.sigmax() else: - raise Exception('Invalid level specification of the qubit subspace') + raise Exception("Invalid level specification of the qubit subspace") def sigmay(dim=2): - """Qiskit wrapper of sigma-Y operator. - """ + """Qiskit wrapper of sigma-Y operator.""" if dim == 2: return gen_operator.sigmay() else: - raise Exception('Invalid level specification of the qubit subspace') + raise Exception("Invalid level specification of the qubit subspace") def sigmaz(dim=2): - """Qiskit wrapper of sigma-Z operator. - """ + """Qiskit wrapper of sigma-Z operator.""" if dim == 2: return gen_operator.sigmaz() else: - raise Exception('Invalid level specification of the qubit subspace') + raise Exception("Invalid level specification of the qubit subspace") def sigmap(dim=2): - """Qiskit wrapper of sigma-plus operator. - """ + """Qiskit wrapper of sigma-plus operator.""" return gen_operator.create(dim) def sigmam(dim=2): - """Qiskit wrapper of sigma-minus operator. - """ + """Qiskit wrapper of sigma-minus operator.""" return gen_operator.destroy(dim) def create(dim): - """Qiskit wrapper of creation operator. - """ + """Qiskit wrapper of creation operator.""" return gen_operator.create(dim) def destroy(dim): - """Qiskit wrapper of annihilation operator. - """ + """Qiskit wrapper of annihilation operator.""" return gen_operator.destroy(dim) def num(dim): - """Qiskit wrapper of number operator. - """ + """Qiskit wrapper of number operator.""" return gen_operator.num(dim) def qeye(dim): - """Qiskit wrapper of identity operator. - """ + """Qiskit wrapper of identity operator.""" return gen_operator.identity(dim) def project(dim, states): - """Qiskit wrapper of projection operator. - """ + """Qiskit wrapper of projection operator.""" ket, bra = states if ket in range(dim) and bra in range(dim): return gen_operator.basis(dim, ket) * gen_operator.basis(dim, bra).adjoint() else: - raise Exception('States are specified on the outside of Hilbert space %s' % states) + raise Exception("States are specified on the outside of Hilbert space %s" % states) def tensor(list_qobj): - """ Qiskit wrapper of tensor product - """ + """Qiskit wrapper of tensor product""" return gen_operator.tensor(list_qobj) def basis(level, pos): - """ Qiskit wrapper of basis - """ + """Qiskit wrapper of basis""" return gen_operator.basis(level, pos) def state(state_vec): - """ Qiskit wrapper of qobj - """ + """Qiskit wrapper of qobj""" return gen_operator.state(state_vec) def fock_dm(level, eigv): - """ Qiskit wrapper of fock_dm - """ + """Qiskit wrapper of fock_dm""" return gen_operator.fock_dm(level, eigv) @@ -158,12 +144,20 @@ def qubit_occ_oper_dressed(target_qubit, estates, h_osc, h_qub, level=0): def get_oper(name, *args): - """ Return quantum operator of given name - """ + """Return quantum operator of given name""" return __operdict.get(name, qeye)(*args) -__operdict = {'X': sigmax, 'Y': sigmay, 'Z': sigmaz, - 'Sp': create, 'Sm': destroy, 'I': qeye, - 'O': num, 'P': project, 'A': destroy, - 'C': create, 'N': num} +__operdict = { + "X": sigmax, + "Y": sigmay, + "Z": sigmaz, + "Sp": create, + "Sm": destroy, + "I": qeye, + "O": num, + "P": project, + "A": destroy, + "C": create, + "N": num, +} diff --git a/qiskit_aer/pulse/system_models/string_model_parser/string_model_parser.py b/qiskit_aer/pulse/system_models/string_model_parser/string_model_parser.py index 312f4338f1..7b3337fca1 100644 --- a/qiskit_aer/pulse/system_models/string_model_parser/string_model_parser.py +++ b/qiskit_aer/pulse/system_models/string_model_parser/string_model_parser.py @@ -24,7 +24,7 @@ from .operator_from_string import gen_oper -Token = namedtuple('Token', ('type', 'name')) +Token = namedtuple("Token", ("type", "name")) ham_elements = OrderedDict( QubOpr=re.compile(r"(?PO|Sp|Sm|X|Y|Z|I)(?P[0-9]+)"), @@ -37,15 +37,15 @@ MathOrd0=re.compile(r"[*/]"), MathOrd1=re.compile(r"[+-]"), BrkL=re.compile(r"\("), - BrkR=re.compile(r"\)") + BrkR=re.compile(r"\)"), ) class HamiltonianParser: - """ Generate QuTip hamiltonian object from string - """ + """Generate QuTip hamiltonian object from string""" + def __init__(self, h_str, dim_osc, dim_qub): - """ Create new quantum operator generator + """Create new quantum operator generator Parameters: h_str (list): list of Hamiltonian string @@ -61,13 +61,11 @@ def __init__(self, h_str, dim_osc, dim_qub): @property def compiled(self): - """ Return Hamiltonian in OpenPulse handler format - """ + """Return Hamiltonian in OpenPulse handler format""" return self.__tc_hams + self.__td_hams def parse(self, qubit_list=None): - """ Parse and generate quantum class object - """ + """Parse and generate quantum class object""" self.__td_hams = [] self.__tc_hams = [] @@ -82,14 +80,14 @@ def parse(self, qubit_list=None): # find time-dependent term if p_td: - coef, token = self._tokenizer(p_td.group('opr'), qubit_list) + coef, token = self._tokenizer(p_td.group("opr"), qubit_list) if token is None: continue # combine coefficient to time-dependent term if coef: - td = '*'.join([coef, p_td.group('ch')]) + td = "*".join([coef, p_td.group("ch")]) else: - td = p_td.group('ch') + td = p_td.group("ch") token = self._shunting_yard(token) _td = self._token2qobj(token), td @@ -100,16 +98,15 @@ def parse(self, qubit_list=None): continue token = self._shunting_yard(token) - if (coef == '') or (coef is None): - coef = '1.' + if (coef == "") or (coef is None): + coef = "1." _tc = self._token2qobj(token), coef self.__tc_hams.append(_tc) def _expand_sum(self): - """ Takes a string-based Hamiltonian list and expands the _SUM action items out. - """ + """Takes a string-based Hamiltonian list and expands the _SUM action items out.""" sum_str = re.compile(r"_SUM\[(?P[a-z]),(?P[a-z\d{}+-]+),(?P[a-z\d{}+-]+),") brk_str = re.compile(r"]") @@ -121,15 +118,15 @@ def _expand_sum(self): p_sums = list(sum_str.finditer(ham)) p_brks = list(brk_str.finditer(ham)) if len(p_sums) != len(p_brks): - raise Exception('Missing correct number of brackets in %s' % ham) + raise Exception("Missing correct number of brackets in %s" % ham) # find correct sum-bracket correspondence if any(p_sums) == 0: ham_out.append(ham) else: - itr = p_sums[0].group('itr') - _l = int(p_sums[0].group('l')) - _u = int(p_sums[0].group('u')) + itr = p_sums[0].group("itr") + _l = int(p_sums[0].group("l")) + _u = int(p_sums[0].group("u")) for ii in range(len(p_sums) - 1): if p_sums[ii + 1].end() > p_brks[ii].start(): break @@ -139,20 +136,21 @@ def _expand_sum(self): # substitute iterator value _temp = [] for kk in range(_l, _u + 1): - trg_s = ham[p_sums[0].end():p_brks[ii].start()] + trg_s = ham[p_sums[0].end() : p_brks[ii].start()] # generate replacement pattern pattern = {} for p in re.finditer(r"\{(?P[a-z0-9*/+-]+)\}", trg_s): if p.group() not in pattern: - sub = parse_binop(p.group('op_str'), operands={itr: str(kk)}) + sub = parse_binop(p.group("op_str"), operands={itr: str(kk)}) if sub.isdecimal(): pattern[p.group()] = sub else: pattern[p.group()] = "{%s}" % sub for key, val in pattern.items(): trg_s = trg_s.replace(key, val) - _temp.append(''.join([ham[:p_sums[0].start()], - trg_s, ham[p_brks[ii].end():]])) + _temp.append( + "".join([ham[: p_sums[0].start()], trg_s, ham[p_brks[ii].end() :]]) + ) ham_list.extend(_temp) self.h_str = ham_out @@ -160,98 +158,97 @@ def _expand_sum(self): return ham_out def _tokenizer(self, op_str, qubit_list=None): - """ Convert string to token and coefficient + """Convert string to token and coefficient Check if the index is in qubit_list """ # generate token _op_str = copy.copy(op_str) token_list = [] - prev = 'none' + prev = "none" while any(_op_str): for key, parser in ham_elements.items(): p = parser.match(_op_str) if p: # find quantum operators - if key in ['QubOpr', 'CavOpr']: + if key in ["QubOpr", "CavOpr"]: _key = key _name = p.group() if p.group() not in self.__str2qopr.keys(): - idx = int(p.group('idx')) + idx = int(p.group("idx")) if qubit_list is not None and idx not in qubit_list: return 0, None - name = p.group('opr') + name = p.group("opr") opr = gen_oper(name, idx, self.dim_osc, self.dim_qub) self.__str2qopr[p.group()] = opr - elif key == 'PrjOpr': + elif key == "PrjOpr": _key = key _name = p.group() if p.group() not in self.__str2qopr.keys(): - idx = int(p.group('idx')) - name = 'P' - lvs = int(p.group('ket')), int(p.group('bra')) + idx = int(p.group("idx")) + name = "P" + lvs = int(p.group("ket")), int(p.group("bra")) opr = gen_oper(name, idx, self.dim_osc, self.dim_qub, lvs) self.__str2qopr[p.group()] = opr - elif key in ['Func', 'Ext']: - _name = p.group('name') + elif key in ["Func", "Ext"]: + _name = p.group("name") _key = key - elif key == 'MathOrd1': + elif key == "MathOrd1": _name = p.group() - if prev not in ['QubOpr', 'PrjOpr', 'CavOpr', 'Var', 'Num']: - _key = 'MathUnitary' + if prev not in ["QubOpr", "PrjOpr", "CavOpr", "Var", "Num"]: + _key = "MathUnitary" else: _key = key else: _name = p.group() _key = key token_list.append(Token(_key, _name)) - _op_str = _op_str[p.end():] + _op_str = _op_str[p.end() :] prev = _key break else: - raise Exception('Invalid input string %s is found' % op_str) + raise Exception("Invalid input string %s is found" % op_str) # split coefficient - coef = '' - if any([k.type == 'Var' for k in token_list]): + coef = "" + if any([k.type == "Var" for k in token_list]): for ii, _ in enumerate(token_list): - if token_list[ii].name == '*': - if all([k.type != 'Var' for k in token_list[ii + 1:]]): - coef = ''.join([k.name for k in token_list[:ii]]) - token_list = token_list[ii + 1:] + if token_list[ii].name == "*": + if all([k.type != "Var" for k in token_list[ii + 1 :]]): + coef = "".join([k.name for k in token_list[:ii]]) + token_list = token_list[ii + 1 :] break else: - raise Exception('Invalid order of operators and coefficients in %s' % op_str) + raise Exception("Invalid order of operators and coefficients in %s" % op_str) return coef, token_list def _shunting_yard(self, token_list): - """ Reformat token to reverse Polish notation - """ + """Reformat token to reverse Polish notation""" stack = [] queue = [] while any(token_list): token = token_list.pop(0) - if token.type in ['QubOpr', 'PrjOpr', 'CavOpr', 'Num']: + if token.type in ["QubOpr", "PrjOpr", "CavOpr", "Num"]: queue.append(token) - elif token.type in ['Func', 'Ext']: + elif token.type in ["Func", "Ext"]: stack.append(token) - elif token.type in ['MathUnitary', 'MathOrd0', 'MathOrd1']: + elif token.type in ["MathUnitary", "MathOrd0", "MathOrd1"]: while stack and math_priority(token, stack[-1]): queue.append(stack.pop(-1)) stack.append(token) - elif token.type in ['BrkL']: + elif token.type in ["BrkL"]: stack.append(token) - elif token.type in ['BrkR']: - while stack[-1].type not in ['BrkL', 'Func']: + elif token.type in ["BrkR"]: + while stack[-1].type not in ["BrkL", "Func"]: queue.append(stack.pop(-1)) if not any(stack): - raise Exception('Missing correct number of brackets') + raise Exception("Missing correct number of brackets") pop = stack.pop(-1) - if pop.type == 'Func': + if pop.type == "Func": queue.append(pop) else: - raise Exception('Invalid token %s is found' % token.name) + raise Exception("Invalid token %s is found" % token.name) while any(stack): queue.append(stack.pop(-1)) @@ -259,44 +256,43 @@ def _shunting_yard(self, token_list): return queue def _token2qobj(self, tokens): - """ Generate quantum class object from tokens - """ + """Generate quantum class object from tokens""" stack = [] for token in tokens: - if token.type in ['QubOpr', 'PrjOpr', 'CavOpr']: + if token.type in ["QubOpr", "PrjOpr", "CavOpr"]: stack.append(self.__str2qopr[token.name]) - elif token.type == 'Num': + elif token.type == "Num": stack.append(float(token.name)) - elif token.type in ['MathUnitary']: - if token.name == '-': + elif token.type in ["MathUnitary"]: + if token.name == "-": stack.append(-stack.pop(-1)) - elif token.type in ['MathOrd0', 'MathOrd1']: + elif token.type in ["MathOrd0", "MathOrd1"]: op2 = stack.pop(-1) op1 = stack.pop(-1) - if token.name == '+': + if token.name == "+": stack.append(op1 + op2) - elif token.name == '-': + elif token.name == "-": stack.append(op1 - op2) - elif token.name == '*': + elif token.name == "*": if isinstance(op1, Operator) and isinstance(op2, Operator): stack.append(op1 & op2) else: stack.append(op1 * op2) - elif token.name == '/': + elif token.name == "/": stack.append(op1 / op2) - elif token.type in ['Func', 'Ext']: + elif token.type in ["Func", "Ext"]: stack.append(apply_func(token.name, stack.pop(-1))) else: - raise Exception('Invalid token %s is found' % token.name) + raise Exception("Invalid token %s is found" % token.name) if len(stack) > 1: - raise Exception('Invalid mathematical operation in ' % tokens) + raise Exception("Invalid mathematical operation in " % tokens) return stack[0] class NoiseParser: - """ Generate QuTip noise object from dictionary + """Generate QuTip noise object from dictionary Qubit noise is given in the format of nested dictionary: "qubit": { "0": { @@ -314,59 +310,57 @@ class NoiseParser: } these configurations are combined in the same dictionary """ + def __init__(self, noise_dict, dim_osc, dim_qub): - """ Create new quantum operator generator + """Create new quantum operator generator Parameters: noise_dict (dict): dictionary of noise configuration dim_osc (dict): dimension of oscillator subspace dim_qub (dict): dimension of qubit subspace """ - self.noise_osc = noise_dict.get('oscillator', {'n_th': {}, 'coupling': {}}) - self.noise_qub = noise_dict.get('qubit', {}) + self.noise_osc = noise_dict.get("oscillator", {"n_th": {}, "coupling": {}}) + self.noise_qub = noise_dict.get("qubit", {}) self.dim_osc = dim_osc self.dim_qub = dim_qub self.__c_list = [] @property def compiled(self): - """ Return noise configuration in OpenPulse handler format - """ + """Return noise configuration in OpenPulse handler format""" return self.__c_list def parse(self): - """ Parse and generate quantum class object - """ + """Parse and generate quantum class object""" # Qubit noise for index, config in self.noise_qub.items(): for opname, coef in config.items(): # TODO: support noise in multi-dimensional system # TODO: support noise with math operation - if opname in ['X', 'Y', 'Z', 'Sp', 'Sm']: + if opname in ["X", "Y", "Z", "Sp", "Sm"]: opr = gen_oper(opname, int(index), self.dim_osc, self.dim_qub) else: - raise Exception('Unsupported noise operator %s is given' % opname) + raise Exception("Unsupported noise operator %s is given" % opname) self.__c_list.append(np.sqrt(coef) * opr) # Oscillator noise - ndic = self.noise_osc['n_th'] - cdic = self.noise_osc['coupling'] + ndic = self.noise_osc["n_th"] + cdic = self.noise_osc["coupling"] for (n_ii, n_coef), (c_ii, c_coef) in zip(ndic.items(), cdic.items()): if n_ii == c_ii: if c_coef > 0: - opr = gen_oper('A', int(n_ii), self.dim_osc, self.dim_qub) + opr = gen_oper("A", int(n_ii), self.dim_osc, self.dim_qub) if n_coef: self.__c_list.append(np.sqrt(c_coef * (1 + n_coef)) * opr) self.__c_list.append(np.sqrt(c_coef * n_coef) * opr.dag()) else: self.__c_list.append(np.sqrt(c_coef) * opr) else: - raise Exception('Invalid oscillator index in noise dictionary.') + raise Exception("Invalid oscillator index in noise dictionary.") def math_priority(o1, o2): - """ Check priority of given math operation - """ - rank = {'MathUnitary': 2, 'MathOrd0': 1, 'MathOrd1': 0} + """Check priority of given math operation""" + rank = {"MathUnitary": 2, "MathOrd0": 1, "MathOrd1": 0} diff_ops = rank.get(o1.type, -1) - rank.get(o2.type, -1) if diff_ops > 0: @@ -377,51 +371,50 @@ def math_priority(o1, o2): # pylint: disable=dangerous-default-value def parse_binop(op_str, operands={}, cast_str=True): - """ Calculate binary operation in string format - """ + """Calculate binary operation in string format""" oprs = OrderedDict( sum=r"(?P[a-zA-Z0-9]+)\+(?P[a-zA-Z0-9]+)", sub=r"(?P[a-zA-Z0-9]+)\-(?P[a-zA-Z0-9]+)", mul=r"(?P[a-zA-Z0-9]+)\*(?P[a-zA-Z0-9]+)", div=r"(?P[a-zA-Z0-9]+)\/(?P[a-zA-Z0-9]+)", - non=r"(?P[a-zA-Z0-9]+)" + non=r"(?P[a-zA-Z0-9]+)", ) for key, regr in oprs.items(): p = re.match(regr, op_str) if p: - val0 = operands.get(p.group('v0'), p.group('v0')) - if key == 'non': + val0 = operands.get(p.group("v0"), p.group("v0")) + if key == "non": # substitution retv = val0 else: - val1 = operands.get(p.group('v1'), p.group('v1')) + val1 = operands.get(p.group("v1"), p.group("v1")) # binary operation - if key == 'sum': + if key == "sum": if val0.isdecimal() and val1.isdecimal(): retv = int(val0) + int(val1) else: - retv = '+'.join([str(val0), str(val1)]) - elif key == 'sub': + retv = "+".join([str(val0), str(val1)]) + elif key == "sub": if val0.isdecimal() and val1.isdecimal(): retv = int(val0) - int(val1) else: - retv = '-'.join([str(val0), str(val1)]) - elif key == 'mul': + retv = "-".join([str(val0), str(val1)]) + elif key == "mul": if val0.isdecimal() and val1.isdecimal(): retv = int(val0) * int(val1) else: - retv = '*'.join([str(val0), str(val1)]) - elif key == 'div': + retv = "*".join([str(val0), str(val1)]) + elif key == "div": if val0.isdecimal() and val1.isdecimal(): retv = int(val0) / int(val1) else: - retv = '/'.join([str(val0), str(val1)]) + retv = "/".join([str(val0), str(val1)]) else: retv = 0 break else: - raise Exception('Invalid string %s' % op_str) + raise Exception("Invalid string %s" % op_str) if cast_str: return str(retv) diff --git a/qiskit_aer/quantum_info/states/aer_densitymatrix.py b/qiskit_aer/quantum_info/states/aer_densitymatrix.py index a7b4c1c77c..f9c5090562 100644 --- a/qiskit_aer/quantum_info/states/aer_densitymatrix.py +++ b/qiskit_aer/quantum_info/states/aer_densitymatrix.py @@ -40,7 +40,7 @@ def __init__(self, data, dims=None, **configs): AerDensityMatrix or QuantumCircuit or qiskit.circuit.Instruction): Data from which the densitymatrix can be constructed. This can be either a complex vector, another densitymatrix or statevector or a ``QuantumCircuit`` or - ``Instruction`` (``Operator`` is not supportted in the current implementation). + ``Instruction`` (``Operator`` is not supported in the current implementation). If the data is a circuit or instruction, the densitymatrix is constructed by assuming that all qubits are initialized to the zero state. dims (int or tuple or list): Optional. The subsystem dimension of @@ -53,14 +53,14 @@ def __init__(self, data, dims=None, **configs): Additional Information: The ``dims`` kwarg is used to ``AerDensityMatrix`` constructor. """ - if '_aer_state' in configs: - self._aer_state = configs.pop('_aer_state') + if "_aer_state" in configs: + self._aer_state = configs.pop("_aer_state") else: - if 'method' not in configs: - configs['method'] = 'density_matrix' - elif configs['method'] != 'density_matrix': - method = configs['method'] - raise AerError(f'Method {method} is not supported') + if "method" not in configs: + configs["method"] = "density_matrix" + elif configs["method"] != "density_matrix": + method = configs["method"] + raise AerError(f"Method {method} is not supported") if isinstance(data, (QuantumCircuit, Instruction)): data, aer_state = AerDensityMatrix._from_instruction(data, None, configs) elif isinstance(data, list): @@ -75,9 +75,10 @@ def __init__(self, data, dims=None, **configs): dims = data._op_shape._dims_l data = data._data.copy() elif isinstance(data, DensityMatrix): - data, aer_state = AerDensityMatrix._from_ndarray(np.array(data.data, - dtype=complex), configs) - elif hasattr(data, 'to_operator'): + data, aer_state = AerDensityMatrix._from_ndarray( + np.array(data.data, dtype=complex), configs + ) + elif hasattr(data, "to_operator"): # If the data object has a 'to_operator' attribute this is given # higher preference than the 'to_matrix' method for initializing # an Operator object. @@ -85,14 +86,15 @@ def __init__(self, data, dims=None, **configs): data, aer_state = AerDensityMatrix._from_ndarray(op.data, configs) if dims is None: dims = op.output_dims() - elif hasattr(data, 'to_matrix'): + elif hasattr(data, "to_matrix"): # If no 'to_operator' attribute exists we next look for a # 'to_matrix' attribute to a matrix that will be cast into # a complex numpy matrix. data, aer_state = AerDensityMatrix._from_ndarray( - np.asarray(data.to_matrix(), dtype=complex), configs) + np.asarray(data.to_matrix(), dtype=complex), configs + ) else: - raise AerError(f'Input data is not supported: type={data.__class__}, data={data}') + raise AerError(f"Input data is not supported: type={data.__class__}, data={data}") self._aer_state = aer_state @@ -106,7 +108,7 @@ def seed(self, value=None): if value is None or isinstance(value, int): self._aer_state.set_seed(value) else: - raise AerError(f'This seed is not supported: type={value.__class__}, value={value}') + raise AerError(f"This seed is not supported: type={value.__class__}, value={value}") def _last_result(self): if self._result is None: @@ -116,8 +118,8 @@ def _last_result(self): def metadata(self): """Return result metadata of an operation that executed lastly.""" if self._last_result() is None: - raise AerError('AerState was not used and metdata does not exist.') - return self._last_result()['metadata'] + raise AerError("AerState was not used and metdata does not exist.") + return self._last_result()["metadata"] def __copy__(self): return copy.deepcopy(self) @@ -210,7 +212,7 @@ def _from_1d_array(data): @staticmethod def _from_ndarray(init_data, configs): - aer_state = AerState(method='density_matrix') + aer_state = AerState(method="density_matrix") options = AerSimulator._default_options() for config_key, config_value in configs.items(): @@ -218,7 +220,7 @@ def _from_ndarray(init_data, configs): aer_state.configure(config_key, config_value) if len(init_data) == 0: - raise AerError('initial data must be larger than 0') + raise AerError("initial data must be larger than 0") num_qubits = int(np.log2(len(init_data))) @@ -233,12 +235,12 @@ def from_instruction(cls, instruction): @staticmethod def _from_instruction(inst, init_data, configs): - aer_state = AerState(method='density_matrix') + aer_state = AerState(method="density_matrix") for config_key, config_value in configs.items(): aer_state.configure(config_key, config_value) - basis_gates = BASIS_GATES['density_matrix'] + basis_gates = BASIS_GATES["density_matrix"] aer_state.allocate_qubits(inst.num_qubits) num_qubits = inst.num_qubits @@ -294,13 +296,13 @@ def to_statevector(self, atol=None, rtol=None): rtol = self.rtol if not is_hermitian_matrix(self.data, atol=atol, rtol=rtol): - raise QiskitError('Not a valid density matrix (non-hermitian).') + raise QiskitError("Not a valid density matrix (non-hermitian).") evals, evecs = np.linalg.eig(self.data) nonzero_evals = evals[abs(evals) > atol] if len(nonzero_evals) != 1 or not np.isclose(nonzero_evals[0], 1, atol=atol, rtol=rtol): - raise QiskitError('Density matrix is not a pure state') + raise QiskitError("Density matrix is not a pure state") psi = evecs[:, np.argmax(evals)] # eigenvectors returned in columns. return AerStatevector(psi) diff --git a/qiskit_aer/quantum_info/states/aer_state.py b/qiskit_aer/quantum_info/states/aer_state.py index 0f21602751..0dd0d47fbe 100644 --- a/qiskit_aer/quantum_info/states/aer_state.py +++ b/qiskit_aer/quantum_info/states/aer_state.py @@ -14,6 +14,7 @@ """ from enum import Enum import numpy as np + # pylint: disable=import-error, no-name-in-module from qiskit_aer.backends.controller_wrappers import AerStateWrapper from ...backends.aerbackend import AerError @@ -69,57 +70,57 @@ def renew(self): def _assert_initializing(self): if self._state != _STATE.INITIALIZING: - raise AerError('AerState was already initialized.') + raise AerError("AerState was already initialized.") def _assert_allocated_or_mapped_or_moved(self): if self._state == _STATE.INITIALIZING: - raise AerError('AerState has not been initialized yet.') + raise AerError("AerState has not been initialized yet.") if self._state == _STATE.CLOSED: - raise AerError('AerState has already been closed.') + raise AerError("AerState has already been closed.") def _assert_allocated_or_mapped(self): if self._state == _STATE.INITIALIZING: - raise AerError('AerState has not been initialized yet.') + raise AerError("AerState has not been initialized yet.") if self._state == _STATE.MOVED: - raise AerError('AerState has already been moved.') + raise AerError("AerState has already been moved.") if self._state == _STATE.CLOSED: - raise AerError('AerState has already been closed.') + raise AerError("AerState has already been closed.") def _assert_mapped_or_moved(self): if self._state == _STATE.INITIALIZING: - raise AerError('AerState has not been initialized yet.') + raise AerError("AerState has not been initialized yet.") if self._state == _STATE.ALLOCATED: - raise AerError('AerState has not been moved yet.') + raise AerError("AerState has not been moved yet.") if self._state == _STATE.CLOSED: - raise AerError('AerState has already been closed.') + raise AerError("AerState has already been closed.") def _assert_closed(self): if self._state != _STATE.CLOSED: - raise AerError('AerState is not closed.') + raise AerError("AerState is not closed.") def _allocated(self): if self._state != _STATE.INITIALIZING: - raise AerError('unexpected state transition: {self._state}->{_STATE.ALLOCATED}') + raise AerError("unexpected state transition: {self._state}->{_STATE.ALLOCATED}") self._state = _STATE.ALLOCATED def _mapped(self): if self._state != _STATE.INITIALIZING: - raise AerError('unexpected state transition: {self._state}->{_STATE.MAPPED}') + raise AerError("unexpected state transition: {self._state}->{_STATE.MAPPED}") self._state = _STATE.MAPPED def _released(self): if self._state != _STATE.MAPPED: - raise AerError('unexpected state transition: {self._state}->{_STATE.RELEASED}') + raise AerError("unexpected state transition: {self._state}->{_STATE.RELEASED}") self._state = _STATE.RELEASED def _moved(self): if self._state != _STATE.ALLOCATED: - raise AerError('unexpected state transition: {self._state}->{_STATE.MOVED}') + raise AerError("unexpected state transition: {self._state}->{_STATE.MOVED}") self._state = _STATE.MOVED def _closed(self): if self._state not in (_STATE.MOVED, _STATE.MAPPED, _STATE.RELEASED): - raise AerError('unexpected state transition: {self._state}->{_STATE.CLOSED}') + raise AerError("unexpected state transition: {self._state}->{_STATE.CLOSED}") self._state = _STATE.CLOSED def configure(self, key, value): @@ -127,7 +128,7 @@ def configure(self, key, value): self._assert_initializing() if not isinstance(key, str): - raise AerError('AerState is configured with a str key') + raise AerError("AerState is configured with a str key") if not isinstance(value, str): value = str(value) @@ -146,7 +147,7 @@ def initialize(self, data=None, copy=True): self._assert_initializing() if not self._method: - raise AerError('method is not configured yet.') + raise AerError("method is not configured yet.") if data is None: self._native_state.initialize() @@ -155,22 +156,22 @@ def initialize(self, data=None, copy=True): elif isinstance(data, np.ndarray): return self._initialize_with_ndarray(data, copy) else: - raise AerError(f'unsupported init data: {data.__class__}') + raise AerError(f"unsupported init data: {data.__class__}") def _initialize_with_ndarray(self, data, copy): if AerState._is_in_use(data) and not copy: - raise AerError('another AerState owns this data') + raise AerError("another AerState owns this data") num_of_qubits = int(np.log2(len(data))) if len(data) != np.power(2, num_of_qubits): - raise AerError('length of init data must be power of two') + raise AerError("length of init data must be power of two") init = False - if self._method == 'statevector': + if self._method == "statevector": init = self._native_state.initialize_statevector(num_of_qubits, data, copy) - elif self._method == 'density_matrix': + elif self._method == "density_matrix": if data.shape != (len(data), len(data)): - raise AerError('shape of init data must be a pair of power of two') + raise AerError("shape of init data must be a pair of power of two") init = self._native_state.initialize_density_matrix(num_of_qubits, data, copy) if init: @@ -186,9 +187,9 @@ def _initialize_with_ndarray(self, data, copy): self._native_state.initialize() if not data.flags.c_contiguous and not data.flags.f_contiguous: data = np.ascontiguousarray(data) - if self._method == 'statevector': + if self._method == "statevector": self._native_state.apply_initialize(range(num_of_qubits), data) - elif self._method == 'density_matrix': + elif self._method == "density_matrix": self._native_state.set_density_matrix(range(num_of_qubits), data) else: self._native_state.apply_initialize(range(num_of_qubits), data) @@ -241,7 +242,7 @@ def move_to_ndarray(self): elif self._state == _STATE.MOVED: ret = self._moved_data else: - if self._method == 'density_matrix': + if self._method == "density_matrix": self._moved_data = self._native_state.move_to_matrix() else: self._moved_data = self._native_state.move_to_ndarray() @@ -253,16 +254,16 @@ def allocate_qubits(self, num_of_qubits): """allocate qubits.""" self._assert_initializing() if num_of_qubits <= 0: - raise AerError(f'invalid number of qubits: {num_of_qubits}') + raise AerError(f"invalid number of qubits: {num_of_qubits}") allocated = self._native_state.allocate_qubits(num_of_qubits) self._last_qubit = allocated[len(allocated) - 1] def _assert_in_allocated_qubits(self, qubit): - if hasattr(qubit, '__iter__'): + if hasattr(qubit, "__iter__"): for q in qubit: self._assert_in_allocated_qubits(q) elif qubit < 0 or qubit > self._last_qubit: - raise AerError(f'invalid qubit: index={qubit}') + raise AerError(f"invalid qubit: index={qubit}") @property def num_qubits(self): diff --git a/qiskit_aer/quantum_info/states/aer_statevector.py b/qiskit_aer/quantum_info/states/aer_statevector.py index d27a6a53ac..b243dd2d4f 100644 --- a/qiskit_aer/quantum_info/states/aer_statevector.py +++ b/qiskit_aer/quantum_info/states/aer_statevector.py @@ -40,7 +40,7 @@ def __init__(self, data, dims=None, **configs): qiskit.circuit.Instruction): Data from which the statevector can be constructed. This can be either a complex vector, another statevector or a ``QuantumCircuit`` or ``Instruction`` - (``Operator`` is not supportted in the current implementation). If the data is + (``Operator`` is not supported in the current implementation). If the data is a circuit or instruction, the statevector is constructed by assuming that all qubits are initialized to the zero state. dims (int or tuple or list): Optional. The subsystem dimension of @@ -55,19 +55,20 @@ def __init__(self, data, dims=None, **configs): The ``dims`` kwarg is used to ``Statevector`` constructor. """ - if '_aer_state' in configs: - self._aer_state = configs.pop('_aer_state') + if "_aer_state" in configs: + self._aer_state = configs.pop("_aer_state") else: - if 'method' not in configs: - configs['method'] = 'statevector' - elif configs['method'] not in ('statevector', 'matrix_product_state'): - method = configs['method'] - raise AerError(f'Method {method} is not supported') + if "method" not in configs: + configs["method"] = "statevector" + elif configs["method"] not in ("statevector", "matrix_product_state"): + method = configs["method"] + raise AerError(f"Method {method} is not supported") if isinstance(data, (QuantumCircuit, Instruction)): data, aer_state = AerStatevector._from_instruction(data, None, configs) elif isinstance(data, list): - data, aer_state = AerStatevector._from_ndarray(np.array(data, dtype=complex), - configs) + data, aer_state = AerStatevector._from_ndarray( + np.array(data, dtype=complex), configs + ) elif isinstance(data, np.ndarray): data, aer_state = AerStatevector._from_ndarray(data, configs) elif isinstance(data, AerStatevector): @@ -76,10 +77,11 @@ def __init__(self, data, dims=None, **configs): dims = data._op_shape._dims_l data = data._data.copy() elif isinstance(data, Statevector): - data, aer_state = AerStatevector._from_ndarray(np.array(data.data, dtype=complex), - configs) + data, aer_state = AerStatevector._from_ndarray( + np.array(data.data, dtype=complex), configs + ) else: - raise AerError(f'Input data is not supported: type={data.__class__}, data={data}') + raise AerError(f"Input data is not supported: type={data.__class__}, data={data}") self._aer_state = aer_state @@ -93,7 +95,7 @@ def seed(self, value=None): if value is None or isinstance(value, int): self._aer_state.set_seed(value) else: - raise AerError(f'This seed is not supported: type={value.__class__}, value={value}') + raise AerError(f"This seed is not supported: type={value.__class__}, value={value}") def _last_result(self): if self._result is None: @@ -103,8 +105,8 @@ def _last_result(self): def metadata(self): """Return result metadata of an operation that executed lastly.""" if self._last_result() is None: - raise AerError('AerState was not used and metdata does not exist.') - return self._last_result()['metadata'] + raise AerError("AerState was not used and metdata does not exist.") + return self._last_result()["metadata"] def __copy__(self): return copy.deepcopy(self) @@ -134,7 +136,7 @@ def sample_memory(self, shots, qargs=None): @staticmethod def _from_ndarray(init_data, configs): do_copy = True - if not init_data.flags['C_CONTIGUOUS']: + if not init_data.flags["C_CONTIGUOUS"]: init_data = np.ascontiguousarray(init_data) do_copy = False @@ -146,7 +148,7 @@ def _from_ndarray(init_data, configs): aer_state.configure(config_key, config_value) if len(init_data) == 0: - raise AerError('initial data must be larger than 0') + raise AerError("initial data must be larger than 0") num_qubits = int(np.log2(len(init_data))) @@ -166,11 +168,11 @@ def _from_instruction(inst, init_data, configs): for config_key, config_value in configs.items(): aer_state.configure(config_key, config_value) - if 'method' in configs: - method = configs['method'] + if "method" in configs: + method = configs["method"] else: - method = 'statevector' - aer_state.configure('method', method) + method = "statevector" + aer_state.configure("method", method) basis_gates = BASIS_GATES[method] @@ -202,9 +204,12 @@ def _aer_evolve_circuit(aer_state, circuit, qubits, basis_gates=None): ) inst = instruction.operation qargs = instruction.qubits - AerStatevector._aer_evolve_instruction(aer_state, inst, - [qubits[circuit.find_bit(qarg).index] - for qarg in qargs], basis_gates) + AerStatevector._aer_evolve_instruction( + aer_state, + inst, + [qubits[circuit.find_bit(qarg).index] for qarg in qargs], + basis_gates, + ) @staticmethod def _aer_evolve_instruction(aer_state, inst, qubits, basis_gates=None): @@ -214,46 +219,52 @@ def _aer_evolve_instruction(aer_state, inst, qubits, basis_gates=None): applied = True if basis_gates and inst.name in basis_gates: - if inst.name in ['u3', 'u']: + if inst.name in ["u3", "u"]: aer_state.apply_u(qubits[0], params[0], params[1], params[2]) - elif inst.name == 'h': + elif inst.name == "h": aer_state.apply_h(qubits[0]) - elif inst.name == 'x': + elif inst.name == "x": aer_state.apply_x(qubits[0]) - elif inst.name == 'cx': + elif inst.name == "cx": aer_state.apply_cx(qubits[0], qubits[1]) - elif inst.name == 'y': + elif inst.name == "y": aer_state.apply_y(qubits[0]) - elif inst.name == 'cy': + elif inst.name == "cy": aer_state.apply_cy(qubits[0], qubits[1]) - elif inst.name == 'z': + elif inst.name == "z": aer_state.apply_z(qubits[0]) - elif inst.name == 'cz': + elif inst.name == "cz": aer_state.apply_cz(qubits[0], qubits[1]) - elif inst.name == 'unitary': + elif inst.name == "unitary": aer_state.apply_unitary(qubits, inst.params[0]) - elif inst.name == 'diagonal': + elif inst.name == "diagonal": aer_state.apply_diagonal(qubits, inst.params) - elif inst.name == 'cu': + elif inst.name == "cu": aer_state.apply_cu(qubits[0], qubits[1], params[0], params[1], params[2], params[3]) - elif inst.name == 'mcu': - aer_state.apply_mcu(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1], - params[0], params[1], params[2], params[3]) - elif inst.name in 'mcx': - aer_state.apply_mcx(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1]) - elif inst.name in 'mcy': - aer_state.apply_mcy(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1]) - elif inst.name in 'mcz': - aer_state.apply_mcz(qubits[0:len(qubits) - 1], qubits[len(qubits) - 1]) - elif inst.name == 'id': + elif inst.name == "mcu": + aer_state.apply_mcu( + qubits[0 : len(qubits) - 1], + qubits[len(qubits) - 1], + params[0], + params[1], + params[2], + params[3], + ) + elif inst.name in "mcx": + aer_state.apply_mcx(qubits[0 : len(qubits) - 1], qubits[len(qubits) - 1]) + elif inst.name in "mcy": + aer_state.apply_mcy(qubits[0 : len(qubits) - 1], qubits[len(qubits) - 1]) + elif inst.name in "mcz": + aer_state.apply_mcz(qubits[0 : len(qubits) - 1], qubits[len(qubits) - 1]) + elif inst.name == "id": pass else: applied = False - elif inst.name == 'kraus': + elif inst.name == "kraus": aer_state.apply_kraus(qubits, inst.params) - elif inst.name == 'reset': + elif inst.name == "reset": aer_state.apply_reset(qubits) - elif inst.name == 'barrier': + elif inst.name == "barrier": pass else: applied = False @@ -261,7 +272,7 @@ def _aer_evolve_instruction(aer_state, inst, qubits, basis_gates=None): if not applied: definition = inst.definition if definition is inst or definition is None: - raise AerError('cannot decompose ' + inst.name) + raise AerError("cannot decompose " + inst.name) AerStatevector._aer_evolve_circuit(aer_state, definition, qubits, basis_gates) @classmethod diff --git a/qiskit_aer/utils/noise_model_inserter.py b/qiskit_aer/utils/noise_model_inserter.py index 3ab5ea266e..f5bca2576f 100644 --- a/qiskit_aer/utils/noise_model_inserter.py +++ b/qiskit_aer/utils/noise_model_inserter.py @@ -42,12 +42,13 @@ def insert_noise(circuits, noise_model, transpile=False): default_errors = noise_model._default_quantum_errors for circuit in circuits: if transpile: - transpiled_circuit = qiskit.compiler.transpile(circuit, - basis_gates=noise_model.basis_gates) + transpiled_circuit = qiskit.compiler.transpile( + circuit, basis_gates=noise_model.basis_gates + ) else: transpiled_circuit = circuit qubit_indices = {bit: index for index, bit in enumerate(transpiled_circuit.qubits)} - result_circuit = transpiled_circuit.copy(name=transpiled_circuit.name + '_with_noise') + result_circuit = transpiled_circuit.copy(name=transpiled_circuit.name + "_with_noise") result_circuit.data = [] for inst, qargs, cargs in transpiled_circuit.data: result_circuit.data.append((inst, qargs, cargs)) diff --git a/qiskit_aer/utils/noise_transformation.py b/qiskit_aer/utils/noise_transformation.py index dd00df0947..41d2044065 100644 --- a/qiskit_aer/utils/noise_transformation.py +++ b/qiskit_aer/utils/noise_transformation.py @@ -35,8 +35,13 @@ from qiskit.circuit import Reset from qiskit.circuit.library.standard_gates import ( - IGate, XGate, YGate, ZGate, - HGate, SGate, SdgGate, + IGate, + XGate, + YGate, + ZGate, + HGate, + SGate, + SdgGate, ) from qiskit.compiler import transpile from qiskit.exceptions import MissingOptionalLibraryError @@ -126,16 +131,16 @@ def transpile_noise_model(noise_model: NoiseModel, **transpile_kwargs) -> NoiseM Raises: NoiseError: if the transformation failed. """ + def func(error): return transpile_quantum_error(error, **transpile_kwargs) return transform_noise_model(noise_model, func) -def approximate_quantum_error(error, *, - operator_string=None, - operator_dict=None, - operator_list=None): +def approximate_quantum_error( + error, *, operator_string=None, operator_dict=None, operator_list=None +): r""" Return a ``QuantumError`` object that approximates an error as a mixture of specified operators (channels). @@ -182,28 +187,36 @@ def approximate_quantum_error(error, *, raise NoiseError(f"Invalid input error type: {error.__class__.__name__}") if error.num_qubits > 2: - raise NoiseError("Only 1-qubit and 2-qubit noises can be converted, " - f"{error.num_qubits}-qubit noise found in model") + raise NoiseError( + "Only 1-qubit and 2-qubit noises can be converted, " + f"{error.num_qubits}-qubit noise found in model" + ) if operator_string is not None: valid_operator_strings = _PRESET_OPERATOR_TABLE.keys() operator_string = operator_string.lower() if operator_string not in valid_operator_strings: - raise NoiseError(f"{operator_string} is not a valid operator_string. " - f"It must be one of {valid_operator_strings}") + raise NoiseError( + f"{operator_string} is not a valid operator_string. " + f"It must be one of {valid_operator_strings}" + ) try: operator_list = _PRESET_OPERATOR_TABLE[operator_string][error.num_qubits] except KeyError as err: - raise NoiseError(f"Preset '{operator_string}' operators do not support the " - f"approximation of errors with {error.num_qubits} qubits") from err + raise NoiseError( + f"Preset '{operator_string}' operators do not support the " + f"approximation of errors with {error.num_qubits} qubits" + ) from err if operator_dict is not None: _, operator_list = zip(*operator_dict.items()) if operator_list is not None: if not isinstance(operator_list, Sequence): raise NoiseError(f"operator_list is not a sequence: {operator_list}") try: - channel_list = [op if isinstance(op, QuantumChannel) else QuantumError([(op, 1)]) - for op in operator_list] # preserve operator_list + channel_list = [ + op if isinstance(op, QuantumChannel) else QuantumError([(op, 1)]) + for op in operator_list + ] # preserve operator_list except NoiseError as err: raise NoiseError(f"Invalid type found in operator list: {operator_list}") from err @@ -212,19 +225,14 @@ def approximate_quantum_error(error, *, if identity_prob < 0 or identity_prob > 1: raise NoiseError(f"Channel probabilities sum to {1 - identity_prob}") noise_ops = [((IGate(), [0]), identity_prob)] - for (operator, probability) in zip(operator_list, probabilities): + for operator, probability in zip(operator_list, probabilities): noise_ops.append((operator, probability)) return QuantumError(noise_ops) - raise NoiseError( - "Quantum error approximation failed - no approximating operators detected" - ) + raise NoiseError("Quantum error approximation failed - no approximating operators detected") -def approximate_noise_model(model, *, - operator_string=None, - operator_dict=None, - operator_list=None): +def approximate_noise_model(model, *, operator_string=None, operator_dict=None, operator_list=None): """ Replace all noises in a noise model with ones approximated by a mixture of operators (channels). @@ -257,13 +265,15 @@ def approximate_noise_model(model, *, its possible values are ``'pauli'``, ``'reset'``, ``'clifford'``. The ``'clifford'`` does not support 2-qubit errors. """ + def approximate(noise): return approximate_quantum_error( noise, operator_string=operator_string, operator_dict=operator_dict, - operator_list=operator_list + operator_list=operator_list, ) + return transform_noise_model(model, approximate) @@ -281,12 +291,12 @@ def approximate(noise): _RESET_Q0Q1 = [op_q0 + op_q1 for op_q0 in _RESET_Q0 for op_q1 in _RESET_Q1] # clifford operators _CLIFFORD_GATES = [ - (IGate(), ), - (SGate(), ), - (SdgGate(), ), - (ZGate(), ), + (IGate(),), + (SGate(),), + (SdgGate(),), + (ZGate(),), # u2 gates - (HGate(), ), + (HGate(),), (HGate(), ZGate()), (ZGate(), HGate()), (HGate(), SGate()), @@ -303,10 +313,10 @@ def approximate(noise): (SdgGate(), HGate(), ZGate()), (ZGate(), HGate(), ZGate()), # u3 gates - (XGate(), ), - (YGate(), ), + (XGate(),), + (YGate(),), (SGate(), XGate()), - (SdgGate(), XGate()) + (SdgGate(), XGate()), ] # preset operator table _PRESET_OPERATOR_TABLE = { @@ -320,12 +330,14 @@ def approximate(noise): }, "clifford": { 1: [[(gate, [0]) for gate in _CLIFFORD_GATES[j]] for j in range(1, 24)], - } + }, } -def _transform_by_operator_list(basis_ops: Sequence[Union[QuantumChannel, QuantumError]], - target: Union[QuantumChannel, QuantumError]) -> List[float]: +def _transform_by_operator_list( + basis_ops: Sequence[Union[QuantumChannel, QuantumError]], + target: Union[QuantumChannel, QuantumError], +) -> List[float]: r""" Transform (or approximate) the target quantum channel into a mixture of basis operators (channels) and return the mixing probabilities. @@ -395,13 +407,13 @@ def fidelity(channel): # fidelity w.r.t. identity omitting the N^-2 factor name="Transformation/Approximation of noise", pip_install="pip install cvxpy", msg="CVXPY is required to solve an optimization problem of" - " approximating a noise channel." + " approximating a noise channel.", ) from err # create quadratic program x = cvxpy.Variable(n) prob = cvxpy.Problem( cvxpy.Minimize(cvxpy.quad_form(x, A) + b.T @ x), - constraints=[cvxpy.sum(x) <= 1, x >= 0, source_fids.T @ x <= target_fid] + constraints=[cvxpy.sum(x) <= 1, x >= 0, source_fids.T @ x <= target_fid], ) # solve quadratic program prob.solve() diff --git a/qiskit_aer/version.py b/qiskit_aer/version.py index 237d02af7e..341b876974 100644 --- a/qiskit_aer/version.py +++ b/qiskit_aer/version.py @@ -23,17 +23,21 @@ def _minimal_ext_cmd(cmd): # construct minimal environment env = {} - for k in ['SYSTEMROOT', 'PATH']: + for k in ["SYSTEMROOT", "PATH"]: v = os.environ.get(k) if v is not None: env[k] = v # LANGUAGE is used on win32 - env['LANGUAGE'] = 'C' - env['LANG'] = 'C' - env['LC_ALL'] = 'C' - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, - stderr=subprocess.PIPE, env=env, - cwd=os.path.join(os.path.dirname(ROOT_DIR))) + env["LANGUAGE"] = "C" + env["LANG"] = "C" + env["LC_ALL"] = "C" + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env, + cwd=os.path.join(os.path.dirname(ROOT_DIR)), + ) out = proc.communicate()[0] if proc.returncode > 0: raise OSError @@ -44,8 +48,8 @@ def git_version(): """Get the current git head sha1.""" # Determine if we're at main try: - out = _minimal_ext_cmd(['git', 'rev-parse', 'HEAD']) - git_revision = out.strip().decode('ascii') + out = _minimal_ext_cmd(["git", "rev-parse", "HEAD"]) + git_revision = out.strip().decode("ascii") except OSError: git_revision = "Unknown" @@ -63,16 +67,17 @@ def get_version_info(): # up the build under Python 3. full_version = VERSION - if not os.path.exists(os.path.join(os.path.dirname(os.path.dirname( - os.path.dirname(ROOT_DIR))), '.git')): + if not os.path.exists( + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(ROOT_DIR))), ".git") + ): return full_version try: - release = _minimal_ext_cmd(['git', 'tag', '-l', '--points-at', 'HEAD']) + release = _minimal_ext_cmd(["git", "tag", "-l", "--points-at", "HEAD"]) except Exception: # pylint: disable=broad-except return full_version if not release: git_revision = git_version() - full_version += '.dev0+' + git_revision[:7] + full_version += ".dev0+" + git_revision[:7] return full_version diff --git a/releasenotes/add-aer-densitymatrix-959c261c954fa60e.yaml b/releasenotes/add-aer-densitymatrix-959c261c954fa60e.yaml deleted file mode 100644 index 1fccca7dbc..0000000000 --- a/releasenotes/add-aer-densitymatrix-959c261c954fa60e.yaml +++ /dev/null @@ -1,6 +0,0 @@ ---- -features: - - | - Add AerDensityMatrix class that allows faster Aer simulation for density - matrix. Roughly speaking, this is a counterpart of AerStatevector which - is for state vector. diff --git a/releasenotes/notes/avoid_copy_of_config-7f7891864c1a1bd0.yaml b/releasenotes/notes/avoid_copy_of_config-7f7891864c1a1bd0.yaml new file mode 100644 index 0000000000..b426e1da74 --- /dev/null +++ b/releasenotes/notes/avoid_copy_of_config-7f7891864c1a1bd0.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Performance regression due to introduction of ``AER::Config`` is fixed. + This class has many fields but is frequently copied in ``AER::Transpile::CircuitOptimization``. + Originally ``json_t`` (former class for configuration) was also frequently copied but + it does have entries in most cases and then this copy overhead is not a problem. + With this fix, ``AER::Transpile::CircuitOptimization`` does not copy ``AER::Config``. diff --git a/releasenotes/notes/avoid_kernel_crash_in_mac_from_blas_error-bd5b836a23f2e3ee.yaml b/releasenotes/notes/avoid_kernel_crash_in_mac_from_blas_error-bd5b836a23f2e3ee.yaml new file mode 100644 index 0000000000..11a772b091 --- /dev/null +++ b/releasenotes/notes/avoid_kernel_crash_in_mac_from_blas_error-bd5b836a23f2e3ee.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + When BLAS calls are failed, because omp threads do not handle exceptions, + Aer crashes without any error messages. This fix is for omp threads to catch + exceptions correctly and then rethrow them outside of omp loops. diff --git a/releasenotes/notes/check_param_length-eb69cd92825bbca4.yaml b/releasenotes/notes/check_param_length-eb69cd92825bbca4.yaml new file mode 100644 index 0000000000..41c475c67c --- /dev/null +++ b/releasenotes/notes/check_param_length-eb69cd92825bbca4.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Previously, parameters for gates are not validate in C++. If parameters are shorter than + expected (due to custom gate), segmentaion faults are thrown. This commit adds checks + whether parameter lenght is expceted. This commit will fix issues reported in #1612. + https://github.com/Qiskit/qiskit-aer/issues/1612 diff --git a/releasenotes/notes/check_parameter_binds_exist-9d52c665d5f94dde.yaml b/releasenotes/notes/check_parameter_binds_exist-9d52c665d5f94dde.yaml new file mode 100644 index 0000000000..2b61f5281f --- /dev/null +++ b/releasenotes/notes/check_parameter_binds_exist-9d52c665d5f94dde.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Since 0.12.0, parameter values in circuits are temporarily replaced with constant values + and parameter values are assigned in C++ library. Therefore, if `parameter_binds` is specified, + simulator returns results with the constnat values as paramter values. With this commit, + Aer raises an error if `parameter_binds` is not specified though circuits have parameters. diff --git a/releasenotes/notes/defer-backend-gathering-773d0ed8092c24d9.yaml b/releasenotes/notes/defer-backend-gathering-773d0ed8092c24d9.yaml new file mode 100644 index 0000000000..1045d675f3 --- /dev/null +++ b/releasenotes/notes/defer-backend-gathering-773d0ed8092c24d9.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Available devices and methods are no longer queried when importing Aer. diff --git a/releasenotes/notes/do_not_modify_metadata-60bb4b88707bd021.yaml b/releasenotes/notes/do_not_modify_metadata-60bb4b88707bd021.yaml new file mode 100644 index 0000000000..c0886bef97 --- /dev/null +++ b/releasenotes/notes/do_not_modify_metadata-60bb4b88707bd021.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Previously :class:`~.AerSimulator` modifies circuit metadata to maintain + consistency between input and output of simulation with side effect of + unexpected view of metadata from applicatiln in simiulation. This fix + avoids using circuit metadata to maintain consistency internaly and then + always provides consistent view of metadata to application. diff --git a/releasenotes/notes/estimator-performance-da83a59b9fd69086.yaml b/releasenotes/notes/estimator-performance-da83a59b9fd69086.yaml new file mode 100644 index 0000000000..3e94a3f0ba --- /dev/null +++ b/releasenotes/notes/estimator-performance-da83a59b9fd69086.yaml @@ -0,0 +1,5 @@ +--- +upgrade: + - | + Improved performance when the same circuits and multiple parameters are passed to + :class:`~.Estimator` with ``approximation=True``. diff --git a/releasenotes/notes/estimator-variance-type-2b04ff7bcd305920.yaml b/releasenotes/notes/estimator-variance-type-2b04ff7bcd305920.yaml new file mode 100644 index 0000000000..d905d54aa7 --- /dev/null +++ b/releasenotes/notes/estimator-variance-type-2b04ff7bcd305920.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Fixed a bug where the variance in metadata in EstimatorResult was complex and now returns float. diff --git a/releasenotes/notes/fix-cuStateVec_enable-0936f2269466e3be.yaml b/releasenotes/notes/fix-cuStateVec_enable-0936f2269466e3be.yaml new file mode 100644 index 0000000000..ff637520d0 --- /dev/null +++ b/releasenotes/notes/fix-cuStateVec_enable-0936f2269466e3be.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a build break to compile Qiskit Aer with cuQuautum support (`AER_ENABLE_CUQUANTUM=true`). + This change does not affect build for CPU and normal GPU binaries. diff --git a/releasenotes/notes/fix-none-handling-in-noise-model-34fcc9a3e3cbdf6f.yaml b/releasenotes/notes/fix-none-handling-in-noise-model-34fcc9a3e3cbdf6f.yaml new file mode 100644 index 0000000000..e8069c3cee --- /dev/null +++ b/releasenotes/notes/fix-none-handling-in-noise-model-34fcc9a3e3cbdf6f.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - | + Fixed a bug in :meth:`~.NoiseModel.from_backend` that raised an error when + the backend has no T1 and T2 values (i.e. None) for a qubit in its qubit properties. + This commit updates :meth:`NoiseModel.from_backend` and :func:`basic_device_gate_errors` + so that they add an identity ``QuantumError`` (i.e. effectively no thermal relaxation error) + to a qubit with no T1 and T2 values for all gates acting on qubits including the qubit. + Fixed `#1779 `__ + and `#1815 `__. diff --git a/releasenotes/notes/fix-number-qubits-a417ca6afa64264f.yaml b/releasenotes/notes/fix-number-qubits-a417ca6afa64264f.yaml new file mode 100644 index 0000000000..760a5b788d --- /dev/null +++ b/releasenotes/notes/fix-number-qubits-a417ca6afa64264f.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fix an issue even if the number of qubits is set by a coupling map + or device's configuration, when the simulation method is configured, + the number of qubits is overwritten in accordance with the method. + Fixed `#1769 `__ \ No newline at end of file diff --git a/releasenotes/notes/fix_cuQuantum_libpath-90d24880cd9a9ea8.yaml b/releasenotes/notes/fix_cuQuantum_libpath-90d24880cd9a9ea8.yaml new file mode 100644 index 0000000000..74bb5e5bcd --- /dev/null +++ b/releasenotes/notes/fix_cuQuantum_libpath-90d24880cd9a9ea8.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + This is fix for library path setting in CMakeLists.txt for cuQuantum SDK. + Because the latest cuQuantum includes libraries for CUDA 11.x and 12.x, + this fix uses CUDA version returned from FindCUDA to the path of libraries + of cuQuantum and cuTENSOR. diff --git a/releasenotes/notes/fix_cuQuantum_static-ad132d742a64a3d5.yaml b/releasenotes/notes/fix_cuQuantum_static-ad132d742a64a3d5.yaml new file mode 100644 index 0000000000..86ba4a6d65 --- /dev/null +++ b/releasenotes/notes/fix_cuQuantum_static-ad132d742a64a3d5.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + This is fix for static link libraries of cuQuantum when building with + CUQUANTUM_STATIC=true. diff --git a/releasenotes/notes/fix_mpi_procs-68b76c11fe7a6b8e.yaml b/releasenotes/notes/fix_mpi_procs-68b76c11fe7a6b8e.yaml new file mode 100644 index 0000000000..9e391c2080 --- /dev/null +++ b/releasenotes/notes/fix_mpi_procs-68b76c11fe7a6b8e.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + MPI parallelization was not enabled since we have not used qobj. + This fix sets the number of processes and MPI rank correctly. diff --git a/releasenotes/notes/fix_param_binding_for_pram_circuit-50e64efbedaec8fd.yaml b/releasenotes/notes/fix_param_binding_for_pram_circuit-50e64efbedaec8fd.yaml new file mode 100644 index 0000000000..b8e44ed372 --- /dev/null +++ b/releasenotes/notes/fix_param_binding_for_pram_circuit-50e64efbedaec8fd.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + :class:`~.AerCircuit` is created from a circuit by iterating its operations + while skipping barrier instructions. However, skipping barrier instructions + make wrong positionings of parameter bindings. This fix adds + :meth:`~.AerCircuit.barrier` and keeps parametr bindings correct. \ No newline at end of file diff --git a/releasenotes/notes/fix_qobj_run-8ea657a93ce9acd2.yaml b/releasenotes/notes/fix_qobj_run-8ea657a93ce9acd2.yaml new file mode 100644 index 0000000000..615dfd366e --- /dev/null +++ b/releasenotes/notes/fix_qobj_run-8ea657a93ce9acd2.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Aer still supports Qobj as an argument of :meth:`~.AerSimulator.run` though + it was deprecated. However, since 0.12.0, it always fails if no ``run_options`` + is specified. This fix enables simulation of Qobj without ``run_options``. diff --git a/releasenotes/notes/implicit_cast_for_arguments-a3c671db2fff6f17.yaml b/releasenotes/notes/implicit_cast_for_arguments-a3c671db2fff6f17.yaml new file mode 100644 index 0000000000..69618c0b99 --- /dev/null +++ b/releasenotes/notes/implicit_cast_for_arguments-a3c671db2fff6f17.yaml @@ -0,0 +1,9 @@ +--- +deprecations: + - | + Options of meth:`~.AerSimulator.run` need to use correct types. +fixes: + - | + Since 0.12.0, :class:`AerConfig` is used for simulation configuration while + performing strict type checking for arguments of meth:`~.AerSimulator.run`. + This commit adds casting if argument types are not expected. diff --git a/releasenotes/notes/primitives-grouping-index-bug-56f69afbdc3e86a0.yaml b/releasenotes/notes/primitives-grouping-index-bug-56f69afbdc3e86a0.yaml new file mode 100644 index 0000000000..109f2aac84 --- /dev/null +++ b/releasenotes/notes/primitives-grouping-index-bug-56f69afbdc3e86a0.yaml @@ -0,0 +1,5 @@ +--- +issues: + - | + Fix a bug that returns wrong expectation values in :class:`~Estimator` when + ``abelian_grouping=True``. diff --git a/releasenotes/notes/release_0121-eeda752822eb0ad3.yaml b/releasenotes/notes/release_0121-eeda752822eb0ad3.yaml new file mode 100644 index 0000000000..88721a28a5 --- /dev/null +++ b/releasenotes/notes/release_0121-eeda752822eb0ad3.yaml @@ -0,0 +1,4 @@ +--- +prelude: > + Qiskit Aer 0.12.1 is the first patch release to 0.12.0. + This fixes some bugs that have been discovered since the release of 0.12.0. diff --git a/releasenotes/notes/support_int_initialize-8491979c4a003908.yaml b/releasenotes/notes/support_int_initialize-8491979c4a003908.yaml new file mode 100644 index 0000000000..ad45dfcbc4 --- /dev/null +++ b/releasenotes/notes/support_int_initialize-8491979c4a003908.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + :meth:``QuantumCircuit.initialize()`` with `int` value was not processed + correctly as reported in `#1821 `. + This commit enables such initialization by decomposing initialize instructions. diff --git a/releasenotes/notes/support_param_for_global_phase-704a97129e7bdbaa.yaml b/releasenotes/notes/support_param_for_global_phase-704a97129e7bdbaa.yaml new file mode 100644 index 0000000000..f4fdaa4241 --- /dev/null +++ b/releasenotes/notes/support_param_for_global_phase-704a97129e7bdbaa.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + :class:`~qiskit.circuit.QuantumCircuit` supports parameterization for its `global_phase`. + However, Aer has not allowed such parameterization and failed when transpiler generates + parameterized global phases. This commit supports parameterization of `global_phase` and + resolve issues related to https://github.com/Qiskit/qiskit-aer/issues/1795, + https://github.com/Qiskit/qiskit-aer/issues/1781, and https://github.com/Qiskit/qiskit-aer/issues/1798. diff --git a/releasenotes/notes/use_omp_set_max_active_levels-7e6c1d301c4434a6.yaml b/releasenotes/notes/use_omp_set_max_active_levels-7e6c1d301c4434a6.yaml new file mode 100644 index 0000000000..faa4c650b1 --- /dev/null +++ b/releasenotes/notes/use_omp_set_max_active_levels-7e6c1d301c4434a6.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + Aer will now use ``omp_set_max_active_levels()`` instead of the deprecated ``omp_set_nested()`` when compiled against recent versions of OpenMP. diff --git a/requirements-dev.txt b/requirements-dev.txt index 87dfcda37d..7e21763be6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,12 +5,14 @@ scikit-build>=0.11.0 asv cvxpy>=1.0.0,<1.1.15;platform_system != 'Windows' and python_version < '3.11' pylint -pycodestyle +black[jupyter]~=23.0 +clang-format~=15.0.7 Sphinx>=1.8.3 jupyter-sphinx reno>=3.4.0 ddt>=1.2.0,!=1.4.0 matplotlib>=3.3 seaborn>=0.9.0 -qiskit_sphinx_theme>=1.10 - +qiskit_sphinx_theme~=1.12.0 +sphinx-design>=0.2.0 +nbsphinx diff --git a/setup.py b/setup.py index 9901c915e1..d35d076c23 100644 --- a/setup.py +++ b/setup.py @@ -10,31 +10,27 @@ from skbuild import setup -PACKAGE_NAME = os.getenv('QISKIT_AER_PACKAGE_NAME', 'qiskit-aer') +PACKAGE_NAME = os.getenv("QISKIT_AER_PACKAGE_NAME", "qiskit-aer") -extras_requirements = { - "dask": ["dask", "distributed"] -} +extras_requirements = {"dask": ["dask", "distributed"]} requirements = [ - 'qiskit-terra>=0.21.0', - 'numpy>=1.16.3', - 'scipy>=1.0', + "qiskit-terra>=0.21.0", + "numpy>=1.16.3", + "scipy>=1.0", ] -VERSION_PATH = os.path.join(os.path.dirname(__file__), - "qiskit_aer", "VERSION.txt") +VERSION_PATH = os.path.join(os.path.dirname(__file__), "qiskit_aer", "VERSION.txt") with open(VERSION_PATH, "r") as version_file: VERSION = version_file.read().strip() -README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'README.md') +README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") with open(README_PATH) as readme_file: README = readme_file.read() cmake_args = [] -is_win_32_bit = (platform.system() == 'Windows' and platform.architecture()[0] == "32bit") +is_win_32_bit = platform.system() == "Windows" and platform.architecture()[0] == "32bit" if is_win_32_bit: cmake_args.append("-DCMAKE_GENERATOR_PLATFORM=Win32") @@ -42,10 +38,10 @@ name=PACKAGE_NAME, version=VERSION, packages=setuptools.find_packages(exclude=["test*"]), - cmake_source_dir='.', + cmake_source_dir=".", description="Qiskit Aer - High performance simulators for Qiskit", long_description=README, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", url="https://github.com/Qiskit/qiskit-aer", author="AER Development Team", author_email="hello@qiskit.org", @@ -74,5 +70,5 @@ extras_require=extras_requirements, cmake_args=cmake_args, keywords="qiskit aer simulator quantum addon backend", - zip_safe=False + zip_safe=False, ) diff --git a/src/controllers/aer_controller.hpp b/src/controllers/aer_controller.hpp index 405e827392..14e8e986d6 100644 --- a/src/controllers/aer_controller.hpp +++ b/src/controllers/aer_controller.hpp @@ -41,12 +41,12 @@ #include #endif +#include "framework/config.hpp" #include "framework/creg.hpp" #include "framework/qobj.hpp" #include "framework/results/experiment_result.hpp" #include "framework/results/result.hpp" #include "framework/rng.hpp" -#include "framework/config.hpp" #include "noise/noise_model.hpp" #include "transpile/cacheblocking.hpp" @@ -59,8 +59,8 @@ #include "simulators/statevector/qubitvector.hpp" #include "simulators/statevector/statevector_state.hpp" #include "simulators/superoperator/superoperator_state.hpp" -#include "simulators/unitary/unitary_state.hpp" #include "simulators/tensor_network/tensor_net_state.hpp" +#include "simulators/unitary/unitary_state.hpp" namespace AER { @@ -85,9 +85,8 @@ class Controller { template Result execute(const inputdata_t &qobj); - Result execute(std::vector &circuits, - Noise::NoiseModel &noise_model, - const Config &config); + Result execute(std::vector> &circuits, + Noise::NoiseModel &noise_model, const Config &config); //----------------------------------------------------------------------- // Config settings @@ -124,16 +123,15 @@ class Controller { enum class Precision { Double, Single }; const std::unordered_map method_names_ = { - {Method::automatic, "automatic"}, - {Method::statevector, "statevector"}, - {Method::density_matrix, "density_matrix"}, - {Method::matrix_product_state, "matrix_product_state"}, - {Method::stabilizer, "stabilizer"}, - {Method::extended_stabilizer, "extended_stabilizer"}, - {Method::unitary, "unitary"}, - {Method::superop, "superop"}, - {Method::tensor_network, "tensor_network"} - }; + {Method::automatic, "automatic"}, + {Method::statevector, "statevector"}, + {Method::density_matrix, "density_matrix"}, + {Method::matrix_product_state, "matrix_product_state"}, + {Method::stabilizer, "stabilizer"}, + {Method::extended_stabilizer, "extended_stabilizer"}, + {Method::unitary, "unitary"}, + {Method::superop, "superop"}, + {Method::tensor_network, "tensor_network"}}; //----------------------------------------------------------------------- // Config @@ -169,7 +167,8 @@ class Controller { // This method must initialize a state and return output data for // the required number of shots. void run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method, const Config &config, ExperimentResult &result) const; + const Method method, const Config &config, + ExperimentResult &result) const; //---------------------------------------------------------------- // Run circuit helpers @@ -178,7 +177,7 @@ class Controller { // Execute n-shots of a circuit on the input state template void run_circuit_helper(const Circuit &circ, const Noise::NoiseModel &noise, - const Config &config, const Method method, + const Config &config, const Method method, ExperimentResult &result) const; // Execute a single shot a of circuit by initializing the state vector, @@ -192,12 +191,9 @@ class Controller { // running all ops in circ, and updating data with // simulation output. template - void run_with_sampling(const Circuit &circ, - State_t &state, - ExperimentResult &result, - RngEngine &rng, - const uint_t block_bits, - const uint_t shots) const; + void run_with_sampling(const Circuit &circ, State_t &state, + ExperimentResult &result, RngEngine &rng, + const uint_t block_bits, const uint_t shots) const; // Execute multiple shots a of circuit by initializing the state vector, // running all ops in circ, and updating data with @@ -212,8 +208,7 @@ class Controller { template void run_circuit_with_sampled_noise(const Circuit &circ, const Noise::NoiseModel &noise, - const Config &config, - const Method method, + const Config &config, const Method method, ExperimentResult &result) const; //---------------------------------------------------------------- @@ -225,7 +220,7 @@ class Controller { template void measure_sampler(InputIterator first_meas, InputIterator last_meas, uint_t shots, State_t &state, ExperimentResult &result, - RngEngine &rng , int_t shot_index = -1) const; + RngEngine &rng, int_t shot_index = -1) const; // Check if measure sampling optimization is valid for the input circuit // for the given method. This checks if operation types before @@ -243,11 +238,10 @@ class Controller { // If `throw_except` is true an exception will be thrown on the return false // case listing the invalid instructions in the circuit or noise model, or // the required memory. - bool validate_method(Method method, - const Circuit &circ, + bool validate_method(Method method, const Circuit &circ, const Noise::NoiseModel &noise, bool throw_except = false) const; - + template bool validate_state(const state_t &state, const Circuit &circ, const Noise::NoiseModel &noise, @@ -261,14 +255,14 @@ class Controller { //---------------------------------------------------------------- // Utility functions //---------------------------------------------------------------- - + // Return a vector of simulation methods for each circuit. // If the default method is automatic this will be computed based on the // circuit and noise model. // The noise model will be modified to enable superop or kraus sampling // methods if required by the chosen methods. std::vector - simulation_methods(std::vector &circuits, + simulation_methods(std::vector> &circuits, Noise::NoiseModel &noise_model) const; // Return the simulation method to use based on the input circuit @@ -285,14 +279,13 @@ class Controller { // Return cache blocking transpiler pass Transpile::CacheBlocking - transpile_cache_blocking(Controller::Method method, - const Circuit &circ, + transpile_cache_blocking(Controller::Method method, const Circuit &circ, const Noise::NoiseModel &noise, const Config &config) const; - //return maximum number of qubits for matrix + // return maximum number of qubits for matrix int_t get_max_matrix_qubits(const Circuit &circ) const; - int_t get_matrix_bits(const Operations::Op& op) const; + int_t get_matrix_bits(const Operations::Op &op) const; bool has_statevector_ops(const Circuit &circuit) const; @@ -304,10 +297,9 @@ class Controller { void clear_parallelization(); // Set parallelization for experiments - void - set_parallelization_experiments(const std::vector &circuits, - const Noise::NoiseModel &noise, - const std::vector &methods); + void set_parallelization_experiments( + const std::vector> &circuits, + const Noise::NoiseModel &noise, const std::vector &methods); // Set circuit parallelization void set_parallelization_circuit(const Circuit &circ, @@ -355,7 +347,8 @@ class Controller { bool parallel_nested_ = false; - //max number of states can be stored on memory for batched multi-shots/experiments optimization + // max number of states can be stored on memory for batched + // multi-shots/experiments optimization int max_batched_states_; // max number of qubits in given circuits @@ -371,15 +364,18 @@ class Controller { uint_t cache_block_qubit_ = 0; - //multi-chunks are required to simulate circuits + // multi-chunks are required to simulate circuits bool multi_chunk_required_ = false; - //config setting for multi-shot parallelization + // config setting for multi-shot parallelization bool batched_shots_gpu_ = true; - int_t batched_shots_gpu_max_qubits_ = 16; //multi-shot parallelization is applied if qubits is less than max qubits - bool enable_batch_multi_shots_ = false; //multi-shot parallelization can be applied + int_t batched_shots_gpu_max_qubits_ = + 16; // multi-shot parallelization is applied if qubits is less than max + // qubits + bool enable_batch_multi_shots_ = + false; // multi-shot parallelization can be applied - //settings for cuStateVec + // settings for cuStateVec bool cuStateVec_enable_ = false; }; @@ -455,13 +451,13 @@ void Controller::set_config(const Config &config) { // enable multiple qregs if cache blocking is enabled if (config.blocking_qubits.has_value()) - cache_block_qubit_ = config.blocking_qubits.value(); + cache_block_qubit_ = config.blocking_qubits.value(); - //enable batched multi-shots/experiments optimization + // enable batched multi-shots/experiments optimization batched_shots_gpu_ = config.batched_shots_gpu; batched_shots_gpu_max_qubits_ = config.batched_shots_gpu_max_qubits; - //cuStateVec configs + // cuStateVec configs cuStateVec_enable_ = false; if (config.cuStateVec_enable.has_value()) cuStateVec_enable_ = config.cuStateVec_enable.value(); @@ -486,10 +482,10 @@ void Controller::set_config(const Config &config) { method_ = Method::tensor_network; } else if (config.method != "automatic") { throw std::runtime_error(std::string("Invalid simulation method (") + - method + std::string(").")); + method + std::string(").")); } - if(method_ == Method::density_matrix || method_ == Method::unitary) + if (method_ == Method::density_matrix || method_ == Method::unitary) batched_shots_gpu_max_qubits_ /= 2; // Override automatic simulation method with a fixed method @@ -510,30 +506,31 @@ void Controller::set_config(const Config &config) { #else #ifndef AER_CUSTATEVEC - if(cuStateVec_enable_){ - //Aer is not built for cuStateVec - throw std::runtime_error( - "Simulation device \"GPU\" does not support cuStateVec on this system"); + if (cuStateVec_enable_) { + // Aer is not built for cuStateVec + throw std::runtime_error("Simulation device \"GPU\" does not support " + "cuStateVec on this system"); } #endif int nDev; if (cudaGetDeviceCount(&nDev) != cudaSuccess) { - cudaGetLastError(); - throw std::runtime_error("No CUDA device available!"); + cudaGetLastError(); + throw std::runtime_error("No CUDA device available!"); } sim_device_ = Device::GPU; #endif - } - else { + } else { throw std::runtime_error(std::string("Invalid simulation device (\"") + - sim_device_name_ + std::string("\").")); + sim_device_name_ + std::string("\").")); } - if(method_ == Method::tensor_network){ + if (method_ == Method::tensor_network) { #if defined(AER_THRUST_CUDA) && defined(AER_CUTENSORNET) - if(sim_device_ != Device::GPU) + if (sim_device_ != Device::GPU) #endif - throw std::runtime_error("Invalid combination of simulation method and device, \"tensor_network\" only supports \"device=GPU\""); + throw std::runtime_error( + "Invalid combination of simulation method and device, " + "\"tensor_network\" only supports \"device=GPU\""); } std::string precision = config.precision; @@ -543,7 +540,7 @@ void Controller::set_config(const Config &config) { sim_precision_ = Precision::Single; } else { throw std::runtime_error(std::string("Invalid simulation precision (") + - precision + std::string(").")); + precision + std::string(").")); } } @@ -576,40 +573,41 @@ void Controller::clear_parallelization() { } void Controller::set_parallelization_experiments( - const std::vector &circuits, - const Noise::NoiseModel &noise, - const std::vector &methods) -{ + const std::vector> &circuits, + const Noise::NoiseModel &noise, const std::vector &methods) { std::vector required_memory_mb_list(circuits.size()); max_qubits_ = 0; for (size_t j = 0; j < circuits.size(); j++) { - if(circuits[j].num_qubits > max_qubits_) - max_qubits_ = circuits[j].num_qubits; - required_memory_mb_list[j] = required_memory_mb(circuits[j], noise, methods[j]); + if (circuits[j]->num_qubits > max_qubits_) + max_qubits_ = circuits[j]->num_qubits; + required_memory_mb_list[j] = + required_memory_mb(*circuits[j], noise, methods[j]); } std::sort(required_memory_mb_list.begin(), required_memory_mb_list.end(), std::greater<>()); - //set max batchable number of circuits - if(batched_shots_gpu_){ - if(required_memory_mb_list[0] == 0 || max_qubits_ == 0) + // set max batchable number of circuits + if (batched_shots_gpu_) { + if (required_memory_mb_list[0] == 0 || max_qubits_ == 0) max_batched_states_ = 1; - else{ - if(sim_device_ == Device::GPU){ - max_batched_states_ = ((max_gpu_memory_mb_/num_gpus_*8/10) / required_memory_mb_list[0])*num_gpus_; - } - else{ - max_batched_states_ = (max_memory_mb_*8/10) / required_memory_mb_list[0]; + else { + if (sim_device_ == Device::GPU) { + max_batched_states_ = ((max_gpu_memory_mb_ / num_gpus_ * 8 / 10) / + required_memory_mb_list[0]) * + num_gpus_; + } else { + max_batched_states_ = + (max_memory_mb_ * 8 / 10) / required_memory_mb_list[0]; } } } - if(max_qubits_ == 0) + if (max_qubits_ == 0) max_qubits_ = 1; - if(explicit_parallelization_ ) + if (explicit_parallelization_) return; - if(circuits.size() == 1){ + if (circuits.size() == 1) { parallel_experiments_ = 1; return; } @@ -647,55 +645,56 @@ void Controller::set_parallelization_experiments( void Controller::set_parallelization_circuit(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method) -{ + const Method method) { enable_batch_multi_shots_ = false; - if(batched_shots_gpu_ && sim_device_ == Device::GPU && - circ.shots > 1 && max_batched_states_ >= num_gpus_ && - batched_shots_gpu_max_qubits_ >= circ.num_qubits ){ - enable_batch_multi_shots_ = true; + if (batched_shots_gpu_ && sim_device_ == Device::GPU && circ.shots > 1 && + max_batched_states_ >= num_gpus_ && + batched_shots_gpu_max_qubits_ >= circ.num_qubits) { + enable_batch_multi_shots_ = true; } - if(sim_device_ == Device::GPU && cuStateVec_enable_){ - enable_batch_multi_shots_ = false; //cuStateVec does not support batch execution of multi-shots + if (sim_device_ == Device::GPU && cuStateVec_enable_) { + enable_batch_multi_shots_ = + false; // cuStateVec does not support batch execution of multi-shots return; } - if(explicit_parallelization_) + if (explicit_parallelization_) return; // Check for trivial parallelization conditions switch (method) { - case Method::statevector: - case Method::stabilizer: - case Method::unitary: - case Method::matrix_product_state: { - if (circ.shots == 1 || num_process_per_experiment_ > 1 || - (!noise.has_quantum_errors() && - check_measure_sampling_opt(circ, method))) { - parallel_shots_ = 1; - parallel_state_update_ = - std::max({1, max_parallel_threads_ / parallel_experiments_}); - return; - } - break; + case Method::statevector: + case Method::stabilizer: + case Method::unitary: + case Method::matrix_product_state: { + if (circ.shots == 1 || num_process_per_experiment_ > 1 || + (!noise.has_quantum_errors() && + check_measure_sampling_opt(circ, method))) { + parallel_shots_ = 1; + parallel_state_update_ = + std::max({1, max_parallel_threads_ / parallel_experiments_}); + return; } - case Method::density_matrix: - case Method::superop: - case Method::tensor_network: { - if (circ.shots == 1 || num_process_per_experiment_ > 1 || - check_measure_sampling_opt(circ, method)) { - parallel_shots_ = 1; - parallel_state_update_ = - std::max({1, max_parallel_threads_ / parallel_experiments_}); - return; - } - break; + break; + } + case Method::density_matrix: + case Method::superop: + case Method::tensor_network: { + if (circ.shots == 1 || num_process_per_experiment_ > 1 || + check_measure_sampling_opt(circ, method)) { + parallel_shots_ = 1; + parallel_state_update_ = + std::max({1, max_parallel_threads_ / parallel_experiments_}); + return; } - case Method::extended_stabilizer: - break; - default: - throw std::invalid_argument("Cannot set parallelization for unresolved method."); + break; + } + case Method::extended_stabilizer: + break; + default: + throw std::invalid_argument( + "Cannot set parallelization for unresolved method."); } // Use a local variable to not override stored maximum based @@ -715,7 +714,8 @@ void Controller::set_parallelization_circuit(const Circuit &circ, // And assign the remaining threads to state update int circ_memory_mb = required_memory_mb(circ, noise, method) / num_process_per_experiment_; - size_t mem_size = (sim_device_ == Device::GPU) ? max_gpu_memory_mb_ : max_memory_mb_; + size_t mem_size = + (sim_device_ == Device::GPU) ? max_gpu_memory_mb_ : max_memory_mb_; if (mem_size < circ_memory_mb) throw std::runtime_error( "a circuit requires more memory than max_memory_mb."); @@ -724,7 +724,7 @@ void Controller::set_parallelization_circuit(const Circuit &circ, int shots = circ.shots; parallel_shots_ = std::min( - {static_cast(mem_size/(circ_memory_mb*2)), max_shots, shots}); + {static_cast(mem_size / (circ_memory_mb * 2)), max_shots, shots}); } parallel_state_update_ = (parallel_shots_ > 1) @@ -734,21 +734,23 @@ void Controller::set_parallelization_circuit(const Circuit &circ, bool Controller::multiple_chunk_required(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method) const -{ + const Method method) const { if (circ.num_qubits < 3) return false; if (cache_block_qubit_ >= 2 && cache_block_qubit_ < circ.num_qubits) return true; - if(num_process_per_experiment_ == 1 && sim_device_ == Device::GPU && num_gpus_ > 0){ - return (max_gpu_memory_mb_ / num_gpus_ < required_memory_mb(circ, noise, method)); + if (num_process_per_experiment_ == 1 && sim_device_ == Device::GPU && + num_gpus_ > 0) { + return (max_gpu_memory_mb_ / num_gpus_ < + required_memory_mb(circ, noise, method)); } - if(num_process_per_experiment_ > 1){ + if (num_process_per_experiment_ > 1) { size_t total_mem = max_memory_mb_; - if(sim_device_ == Device::GPU) + if (sim_device_ == Device::GPU) total_mem += max_gpu_memory_mb_; - if(total_mem*num_process_per_experiment_ > required_memory_mb(circ, noise, method)) + if (total_mem * num_process_per_experiment_ > + required_memory_mb(circ, noise, method)) return true; } @@ -757,27 +759,24 @@ bool Controller::multiple_chunk_required(const Circuit &circ, bool Controller::multiple_shots_required(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method) const -{ + const Method method) const { if (circ.shots < 2) return false; - if (method == Method::density_matrix || - method == Method::superop || + if (method == Method::density_matrix || method == Method::superop || method == Method::unitary) { return false; } bool can_sample = check_measure_sampling_opt(circ, method); - if (noise.is_ideal()){ - return !can_sample; + if (noise.is_ideal()) { + return !can_sample; } return true; } -size_t Controller::get_system_memory_mb() -{ +size_t Controller::get_system_memory_mb() { size_t total_physical_memory = Utils::get_system_memory_mb(); #ifdef AER_MPI // get minimum memory size per process @@ -821,19 +820,16 @@ size_t Controller::get_gpu_memory_mb() { return total_physical_memory >> 20; } - -Transpile::CacheBlocking -Controller::transpile_cache_blocking(Controller::Method method, const Circuit &circ, - const Noise::NoiseModel &noise, - const Config &config) const -{ +Transpile::CacheBlocking Controller::transpile_cache_blocking( + Controller::Method method, const Circuit &circ, + const Noise::NoiseModel &noise, const Config &config) const { Transpile::CacheBlocking cache_block_pass; - const bool is_matrix = (method == Method::density_matrix - || method == Method::unitary); + const bool is_matrix = + (method == Method::density_matrix || method == Method::unitary); const auto complex_size = (sim_precision_ == Precision::Single) - ? sizeof(std::complex) - : sizeof(std::complex); + ? sizeof(std::complex) + : sizeof(std::complex); cache_block_pass.set_num_processes(num_process_per_experiment_); cache_block_pass.set_config(config); @@ -842,7 +838,7 @@ Controller::transpile_cache_blocking(Controller::Method method, const Circuit &c // if blocking is not set by config, automatically set if required if (multiple_chunk_required(circ, noise, method)) { int nplace = num_process_per_experiment_; - if(sim_device_ == Device::GPU && num_gpus_ > 0) + if (sim_device_ == Device::GPU && num_gpus_ > 0) nplace *= num_gpus_; cache_block_pass.set_blocking(circ.num_qubits, get_min_memory_mb() << 20, nplace, complex_size, is_matrix); @@ -856,11 +852,6 @@ Controller::transpile_cache_blocking(Controller::Method method, const Circuit &c //------------------------------------------------------------------------- template Result Controller::execute(const inputdata_t &input_qobj) { -#ifdef AER_MPI - MPI_Comm_size(MPI_COMM_WORLD, &num_processes_); - MPI_Comm_rank(MPI_COMM_WORLD, &myrank_); -#endif - // Load QOBJ in a try block so we can catch parsing errors and still return // a valid JSON output containing the error message. try { @@ -906,13 +897,16 @@ Result Controller::execute(const inputdata_t &input_qobj) { // Experiment execution //------------------------------------------------------------------------- -Result Controller::execute(std::vector &circuits, +Result Controller::execute(std::vector> &circuits, Noise::NoiseModel &noise_model, - const Config &config) -{ + const Config &config) { // Start QOBJ timer auto timer_start = myclock_t::now(); +#ifdef AER_MPI + MPI_Comm_size(MPI_COMM_WORLD, &num_processes_); + MPI_Comm_rank(MPI_COMM_WORLD, &myrank_); +#endif // Determine simulation method for each circuit // and enable required noise sampling methods auto methods = simulation_methods(circuits, noise_model); @@ -922,15 +916,15 @@ Result Controller::execute(std::vector &circuits, // Execute each circuit in a try block try { - //check if multi-chunk distribution is required + // check if multi-chunk distribution is required bool multi_chunk_required_ = false; - for (size_t j = 0; j < circuits.size(); j++){ - if(circuits[j].num_qubits > 0){ - if(multiple_chunk_required(circuits[j], noise_model, methods[j])) + for (size_t j = 0; j < circuits.size(); j++) { + if (circuits[j]->num_qubits > 0) { + if (multiple_chunk_required(*circuits[j], noise_model, methods[j])) multi_chunk_required_ = true; } } - if(multi_chunk_required_) + if (multi_chunk_required_) num_process_per_experiment_ = num_processes_; else num_process_per_experiment_ = 1; @@ -955,7 +949,8 @@ Result Controller::execute(std::vector &circuits, // store rank and number of processes, if no distribution rank=0 procs=1 is // set - result.metadata.add(num_process_per_experiment_, "num_processes_per_experiments"); + result.metadata.add(num_process_per_experiment_, + "num_processes_per_experiments"); result.metadata.add(num_processes_, "num_mpi_processes"); result.metadata.add(myrank_, "mpi_rank"); @@ -966,8 +961,12 @@ Result Controller::execute(std::vector &circuits, // Nested parallel experiments parallel_nested_ = true; - //nested should be set to zero if num_threads clause will be used + // nested should be set to zero if num_threads clause will be used +#if _OPENMP >= 200805 + omp_set_max_active_levels(0); +#else omp_set_nested(0); +#endif result.metadata.add(parallel_nested_, "omp_nested"); } else { @@ -976,33 +975,35 @@ Result Controller::execute(std::vector &circuits, #endif #ifdef AER_MPI - //average random seed to set the same seed to each process (when seed_simulator is not set) - if(num_processes_ > 1){ + // average random seed to set the same seed to each process (when + // seed_simulator is not set) + if (num_processes_ > 1) { reg_t seeds(circuits.size()); reg_t avg_seeds(circuits.size()); - for(int_t i=0;iseed; + MPI_Allreduce(seeds.data(), avg_seeds.data(), circuits.size(), + MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD); + for (int_t i = 0; i < circuits.size(); i++) + circuits[i]->seed = avg_seeds[i] / num_processes_; } #endif const int NUM_RESULTS = result.results.size(); - //following looks very similar but we have to separate them to avoid omp nested loops that causes performance degradation - //(DO NOT use if statement in #pragma omp) + // following looks very similar but we have to separate them to avoid omp + // nested loops that causes performance degradation (DO NOT use if statement + // in #pragma omp) if (parallel_experiments_ == 1) { for (int j = 0; j < NUM_RESULTS; ++j) { - set_parallelization_circuit(circuits[j], noise_model, methods[j]); - run_circuit(circuits[j], noise_model,methods[j], - config, result.results[j]); + set_parallelization_circuit(*circuits[j], noise_model, methods[j]); + run_circuit(*circuits[j], noise_model, methods[j], config, + result.results[j]); } - } - else{ + } else { #pragma omp parallel for num_threads(parallel_experiments_) for (int j = 0; j < NUM_RESULTS; ++j) { - run_circuit(circuits[j], noise_model,methods[j], - config, result.results[j]); + run_circuit(*circuits[j], noise_model, methods[j], config, + result.results[j]); } } @@ -1042,9 +1043,10 @@ Result Controller::execute(std::vector &circuits, //------------------------------------------------------------------------- // Base class override //------------------------------------------------------------------------- -void Controller::run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, - const Method method,const Config &config, ExperimentResult &result) const -{ +void Controller::run_circuit(const Circuit &circ, + const Noise::NoiseModel &noise, + const Method method, const Config &config, + ExperimentResult &result) const { // Run the circuit switch (method) { case Method::statevector: { @@ -1052,13 +1054,11 @@ void Controller::run_circuit(const Circuit &circ, const Noise::NoiseModel &noise // Chunk based simualtion if (sim_precision_ == Precision::Double) { // Double-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( + return run_circuit_helper>>( circ, noise, config, Method::statevector, result); } else { // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( + return run_circuit_helper>>( circ, noise, config, Method::statevector, result); } } else { @@ -1150,8 +1150,8 @@ void Controller::run_circuit(const Circuit &circ, const Noise::NoiseModel &noise case Method::stabilizer: // Stabilizer simulation // TODO: Stabilizer doesn't yet support custom state initialization - return run_circuit_helper( - circ, noise, config, Method::stabilizer, result); + return run_circuit_helper(circ, noise, config, + Method::stabilizer, result); case Method::extended_stabilizer: return run_circuit_helper( circ, noise, config, Method::extended_stabilizer, result); @@ -1267,7 +1267,7 @@ Transpile::Fusion Controller::transpile_fusion(Method method, } case Method::matrix_product_state: { fusion_pass.active = false; - return fusion_pass; // Do not allow the config to set active for MPS + return fusion_pass; // Do not allow the config to set active for MPS } case Method::statevector: { if (fusion_pass.allow_kraus) { @@ -1282,13 +1282,13 @@ Transpile::Fusion Controller::transpile_fusion(Method method, break; } case Method::tensor_network: { - if(opset.contains(Operations::OpType::save_statevec) || opset.contains(Operations::OpType::save_statevec_dict)){ + if (opset.contains(Operations::OpType::save_statevec) || + opset.contains(Operations::OpType::save_statevec_dict)) { if (fusion_pass.allow_kraus) { // Halve default max fused qubits for Kraus noise fusion fusion_pass.max_qubit /= 2; } - } - else{ + } else { // Halve the default threshold and max fused qubits for density matrix fusion_pass.threshold /= 2; fusion_pass.max_qubit /= 2; @@ -1312,10 +1312,8 @@ Transpile::Fusion Controller::transpile_fusion(Method method, template void Controller::run_circuit_helper(const Circuit &circ, const Noise::NoiseModel &noise, - const Config &config, - const Method method, - ExperimentResult &result) const -{ + const Config &config, const Method method, + ExperimentResult &result) const { // Start individual circuit timer auto timer_start = myclock_t::now(); // state circuit timer @@ -1348,7 +1346,7 @@ void Controller::run_circuit_helper(const Circuit &circ, result.metadata.add(false, "measure_sampling"); result.metadata.add(false, "batched_shots_optimization"); - if(circ.num_qubits > 0){ //do nothing for query steps + if (circ.num_qubits > 0) { // do nothing for query steps // Choose execution method based on noise and method Circuit opt_circ; bool noise_sampling = false; @@ -1364,35 +1362,40 @@ void Controller::run_circuit_helper(const Circuit &circ, result.metadata.add("readout", "noise"); } // Superop noise sampling - else if (method == Method::density_matrix || method == Method::superop || (method == Method::tensor_network && !has_statevector_ops(circ))) { + else if (method == Method::density_matrix || method == Method::superop || + (method == Method::tensor_network && + !has_statevector_ops(circ))) { // Sample noise using SuperOp method - opt_circ = noise.sample_noise(circ, rng, Noise::NoiseModel::Method::superop); + opt_circ = + noise.sample_noise(circ, rng, Noise::NoiseModel::Method::superop); result.metadata.add("superop", "noise"); } // Kraus noise sampling else if (noise.opset().contains(Operations::OpType::kraus) || noise.opset().contains(Operations::OpType::superop)) { - opt_circ = noise.sample_noise(circ, rng, Noise::NoiseModel::Method::kraus); + opt_circ = + noise.sample_noise(circ, rng, Noise::NoiseModel::Method::kraus); result.metadata.add("kraus", "noise"); } // General circuit noise sampling else { - if(enable_batch_multi_shots_ && !multi_chunk_required_){ - //batched optimization samples noise at runtime - opt_circ = noise.sample_noise(circ, rng, Noise::NoiseModel::Method::circuit, true); - } - else{ + if (enable_batch_multi_shots_ && !multi_chunk_required_) { + // batched optimization samples noise at runtime + opt_circ = noise.sample_noise( + circ, rng, Noise::NoiseModel::Method::circuit, true); + } else { noise_sampling = true; } result.metadata.add("circuit", "noise"); } - if(noise_sampling){ - run_circuit_with_sampled_noise(circ, noise, config, method, result); - } - else{ + if (noise_sampling) { + run_circuit_with_sampled_noise(circ, noise, config, method, + result); + } else { // Run multishot simulation without noise sampling - run_circuit_without_sampled_noise(opt_circ, noise, config, method, result); + run_circuit_without_sampled_noise(opt_circ, noise, config, + method, result); } } @@ -1430,13 +1433,11 @@ void Controller::run_single_shot(const Circuit &circ, State_t &state, } template -void Controller::run_with_sampling(const Circuit &circ, - State_t &state, - ExperimentResult &result, - RngEngine &rng, +void Controller::run_with_sampling(const Circuit &circ, State_t &state, + ExperimentResult &result, RngEngine &rng, const uint_t block_bits, const uint_t shots) const { - auto& ops = circ.ops; + auto &ops = circ.ops; auto first_meas = circ.first_measure_pos; // Position of first measurement op bool final_ops = (first_meas == ops.size()); @@ -1447,22 +1448,22 @@ void Controller::run_with_sampling(const Circuit &circ, state.initialize_qreg(circ.num_qubits); state.initialize_creg(circ.num_memory, circ.num_registers); - state.apply_ops(ops.cbegin(), ops.cbegin() + first_meas, result, rng, final_ops); + state.apply_ops(ops.cbegin(), ops.cbegin() + first_meas, result, rng, + final_ops); // Get measurement operations and set of measured qubits - measure_sampler(circ.ops.begin() + first_meas, circ.ops.end(), shots, state, result, rng); + measure_sampler(circ.ops.begin() + first_meas, circ.ops.end(), shots, state, + result, rng); } template -void Controller::run_circuit_without_sampled_noise(Circuit &circ, - const Noise::NoiseModel &noise, - const Config &config, - const Method method, - ExperimentResult &result) const -{ +void Controller::run_circuit_without_sampled_noise( + Circuit &circ, const Noise::NoiseModel &noise, const Config &config, + const Method method, ExperimentResult &result) const { State_t state; - // Validate gateset and memory requirements, raise exception if they're exceeded + // Validate gateset and memory requirements, raise exception if they're + // exceeded validate_state(state, circ, noise, true); // Set state config @@ -1481,8 +1482,9 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, // Cache blocking pass uint_t block_bits = circ.num_qubits; - if(state.multi_chunk_distribution_supported()){ - auto cache_block_pass = transpile_cache_blocking(method, circ, dummy_noise, config); + if (state.multi_chunk_distribution_supported()) { + auto cache_block_pass = + transpile_cache_blocking(method, circ, dummy_noise, config); cache_block_pass.set_sample_measure(can_sample); cache_block_pass.optimize_circuit(circ, dummy_noise, state.opset(), result); if (cache_block_pass.enabled()) { @@ -1508,8 +1510,8 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, #pragma omp parallel for num_threads(parallel_shots_) for (int i = 0; i < parallel_shots_; i++) { - uint_t i_shot = circ.shots*i/parallel_shots_; - uint_t shot_end = circ.shots*(i+1)/parallel_shots_; + uint_t i_shot = circ.shots * i / parallel_shots_; + uint_t shot_end = circ.shots * (i + 1) / parallel_shots_; uint_t this_shot = shot_end - i_shot; State_t shot_state; @@ -1524,7 +1526,8 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, RngEngine rng; rng.set_seed(circ.seed + i); - run_with_sampling(circ, shot_state, par_results[i], rng, block_bits, this_shot); + run_with_sampling(circ, shot_state, par_results[i], rng, block_bits, + this_shot); shot_state.add_metadata(par_results[i]); } @@ -1532,8 +1535,8 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, result.combine(std::move(res)); } - if (sim_device_name_ == "GPU"){ - if(parallel_shots_ >= num_gpus_) + if (sim_device_name_ == "GPU") { + if (parallel_shots_ >= num_gpus_) result.metadata.add(num_gpus_, "gpu_parallel_shots_"); else result.metadata.add(parallel_shots_, "gpu_parallel_shots_"); @@ -1542,67 +1545,71 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, // Add measure sampling metadata result.metadata.add(true, "measure_sampling"); - } - else{ + } else { // Perform standard execution if we cannot apply the // measurement sampling optimization - if(block_bits == circ.num_qubits && enable_batch_multi_shots_ && state.multi_shot_parallelization_supported()){ - //apply batched multi-shots optimization (currenly only on GPU) + if (block_bits == circ.num_qubits && enable_batch_multi_shots_ && + state.multi_shot_parallelization_supported()) { + // apply batched multi-shots optimization (currenly only on GPU) state.set_max_bached_shots(max_batched_states_); state.set_distribution(num_processes_); state.set_max_matrix_qubits(max_bits); state.set_num_creg_bits(circ.num_memory, circ.num_registers); - state.allocate(circ.num_qubits, circ.num_qubits, circ.shots); //allocate multiple-shots + state.allocate(circ.num_qubits, circ.num_qubits, + circ.shots); // allocate multiple-shots - //qreg is initialized inside state class + // qreg is initialized inside state class state.initialize_creg(circ.num_memory, circ.num_registers); - state.apply_ops_multi_shots(circ.ops.cbegin(), circ.ops.cend(), noise, result, circ.seed, true); + state.apply_ops_multi_shots(circ.ops.cbegin(), circ.ops.cend(), noise, + result, circ.seed, true); result.save_count_data(state.cregs(), save_creg_memory_); // Add batched multi-shots optimizaiton metadata result.metadata.add(true, "batched_shots_optimization"); - } - else{ + } else { std::vector par_results(parallel_shots_); int_t par_shots = parallel_shots_; - if(block_bits != circ.num_qubits) + if (block_bits != circ.num_qubits) par_shots = 1; - auto run_circuit_without_sampled_noise_lambda = [this,&par_results,circ,noise,config,method,block_bits,max_bits,par_shots](int_t i){ - uint_t i_shot,shot_end; - i_shot = circ.shots*i/par_shots; - shot_end = circ.shots*(i+1)/par_shots; - - State_t par_state; - // Set state config - par_state.set_config(config); - par_state.set_parallelization(parallel_state_update_); - par_state.set_global_phase(circ.global_phase_angle); - par_state.enable_density_matrix(!has_statevector_ops(circ)); - - par_state.set_distribution(num_process_per_experiment_); - par_state.set_max_matrix_qubits(max_bits ); - - // allocate qubit register - par_state.allocate(circ.num_qubits, block_bits); - - for(;i_shot 1),0,par_shots,run_circuit_without_sampled_noise_lambda); + auto run_circuit_without_sampled_noise_lambda = + [this, &par_results, circ, noise, config, method, block_bits, + max_bits, par_shots](int_t i) { + uint_t i_shot, shot_end; + i_shot = circ.shots * i / par_shots; + shot_end = circ.shots * (i + 1) / par_shots; + + State_t par_state; + // Set state config + par_state.set_config(config); + par_state.set_parallelization(parallel_state_update_); + par_state.set_global_phase(circ.global_phase_angle); + par_state.enable_density_matrix(!has_statevector_ops(circ)); + + par_state.set_distribution(num_process_per_experiment_); + par_state.set_max_matrix_qubits(max_bits); + + // allocate qubit register + par_state.allocate(circ.num_qubits, block_bits); + + for (; i_shot < shot_end; i_shot++) { + RngEngine rng; + rng.set_seed(circ.seed + i_shot); + run_single_shot(circ, par_state, par_results[i], rng); + } + par_state.add_metadata(par_results[i]); + }; + Utils::apply_omp_parallel_for((par_shots > 1), 0, par_shots, + run_circuit_without_sampled_noise_lambda); for (auto &res : par_results) { result.combine(std::move(res)); } - if (sim_device_name_ == "GPU"){ - if(par_shots >= num_gpus_) + if (sim_device_name_ == "GPU") { + if (par_shots >= num_gpus_) result.metadata.add(num_gpus_, "gpu_parallel_shots_"); else result.metadata.add(par_shots, "gpu_parallel_shots_"); @@ -1615,16 +1622,17 @@ void Controller::run_circuit_without_sampled_noise(Circuit &circ, template void Controller::run_circuit_with_sampled_noise( const Circuit &circ, const Noise::NoiseModel &noise, const Config &config, - const Method method, ExperimentResult &result) const -{ + const Method method, ExperimentResult &result) const { std::vector par_results(parallel_shots_); - auto run_circuit_with_sampled_noise_lambda = [this,&par_results,circ,noise,config,method](int_t i){ + auto run_circuit_with_sampled_noise_lambda = [this, &par_results, circ, noise, + config, method](int_t i) { State_t state; - uint_t i_shot,shot_end; + uint_t i_shot, shot_end; Noise::NoiseModel dummy_noise; - // Validate gateset and memory requirements, raise exception if they're exceeded + // Validate gateset and memory requirements, raise exception if they're + // exceeded validate_state(state, circ, noise, true); // Set state config @@ -1635,12 +1643,13 @@ void Controller::run_circuit_with_sampled_noise( // Transpilation for circuit noise method auto fusion_pass = transpile_fusion(method, circ.opset(), config); - auto cache_block_pass = transpile_cache_blocking(method, circ, noise, config); + auto cache_block_pass = + transpile_cache_blocking(method, circ, noise, config); - i_shot = circ.shots*i/parallel_shots_; - shot_end = circ.shots*(i+1)/parallel_shots_; + i_shot = circ.shots * i / parallel_shots_; + shot_end = circ.shots * (i + 1) / parallel_shots_; - for(;i_shot 1),0,parallel_shots_,run_circuit_with_sampled_noise_lambda); + Utils::apply_omp_parallel_for((parallel_shots_ > 1), 0, parallel_shots_, + run_circuit_with_sampled_noise_lambda); for (auto &res : par_results) { result.combine(std::move(res)); } - if (sim_device_name_ == "GPU"){ - if(parallel_shots_ >= num_gpus_) + if (sim_device_name_ == "GPU") { + if (parallel_shots_ >= num_gpus_) result.metadata.add(num_gpus_, "gpu_parallel_shots_"); else result.metadata.add(parallel_shots_, "gpu_parallel_shots_"); @@ -1695,20 +1705,19 @@ bool Controller::check_measure_sampling_opt(const Circuit &circ, // If density matrix, unitary, superop method all supported instructions // allow sampling - if (method == Method::density_matrix || - method == Method::superop || + if (method == Method::density_matrix || method == Method::superop || method == Method::unitary) { return true; } - if(method == Method::tensor_network){ - //if there are no save statevec ops, tensor network simulator runs as density matrix simulator - if((!circ.opset().contains(Operations::OpType::save_statevec)) && - (!circ.opset().contains(Operations::OpType::save_statevec_dict)) ){ - return true; + if (method == Method::tensor_network) { + // if there are no save statevec ops, tensor network simulator runs as + // density matrix simulator + if ((!circ.opset().contains(Operations::OpType::save_statevec)) && + (!circ.opset().contains(Operations::OpType::save_statevec_dict))) { + return true; } } - // If circuit contains a non-initial initialize that is not a full width // instruction we can't sample if (circ.can_sample_initialize == false) { @@ -1724,7 +1733,7 @@ bool Controller::check_measure_sampling_opt(const Circuit &circ, circ.opset().contains(Operations::OpType::kraus) || circ.opset().contains(Operations::OpType::superop) || circ.opset().contains(Operations::OpType::jump) || - circ.opset().contains(Operations::OpType::mark )) { + circ.opset().contains(Operations::OpType::mark)) { return false; } // Otherwise true @@ -1732,10 +1741,10 @@ bool Controller::check_measure_sampling_opt(const Circuit &circ, } template -void Controller::measure_sampler( - InputIterator first_meas, InputIterator last_meas, uint_t shots, - State_t &state, ExperimentResult &result, RngEngine &rng, int_t shot_index) const -{ +void Controller::measure_sampler(InputIterator first_meas, + InputIterator last_meas, uint_t shots, + State_t &state, ExperimentResult &result, + RngEngine &rng, int_t shot_index) const { // Check if meas_circ is empty, and if so return initial creg if (first_meas == last_meas) { while (shots-- > 0) { @@ -1766,7 +1775,7 @@ void Controller::measure_sampler( // Generate the samples uint_t shots_or_index; - if(shot_index < 0) + if (shot_index < 0) shots_or_index = shots; else shots_or_index = shot_index; @@ -1797,8 +1806,10 @@ void Controller::measure_sampler( } // Process samples - uint_t num_memory = (memory_map.empty()) ? 0ULL : 1 + memory_map.rbegin()->first; - uint_t num_registers = (register_map.empty()) ? 0ULL : 1 + register_map.rbegin()->first; + uint_t num_memory = + (memory_map.empty()) ? 0ULL : 1 + memory_map.rbegin()->first; + uint_t num_registers = + (register_map.empty()) ? 0ULL : 1 + register_map.rbegin()->first; ClassicalRegister creg; while (!all_samples.empty()) { auto sample = all_samples.back(); @@ -1821,38 +1832,44 @@ void Controller::measure_sampler( } // Save count data - result.save_count_data(creg, save_creg_memory_); + result.save_count_data(creg, save_creg_memory_); // pop off processed sample all_samples.pop_back(); } } - //------------------------------------------------------------------------- // Validation //------------------------------------------------------------------------- std::vector -Controller::simulation_methods(std::vector &circuits, +Controller::simulation_methods(std::vector> &circuits, Noise::NoiseModel &noise_model) const { // Does noise model contain kraus noise - bool kraus_noise = (noise_model.opset().contains(Operations::OpType::kraus) || - noise_model.opset().contains(Operations::OpType::superop)); + bool kraus_noise = + (noise_model.opset().contains(Operations::OpType::kraus) || + noise_model.opset().contains(Operations::OpType::superop)); if (method_ == Method::automatic) { // Determine simulation methods for each circuit and noise model std::vector sim_methods; bool superop_enabled = false; bool kraus_enabled = false; - for (const auto& circ: circuits) { + for (const auto &_circ : circuits) { + const auto circ = *_circ; auto method = automatic_simulation_method(circ, noise_model); sim_methods.push_back(method); - if (!superop_enabled && (method == Method::density_matrix || method == Method::superop || (method == Method::tensor_network && !has_statevector_ops(circ)) )) { + if (!superop_enabled && + (method == Method::density_matrix || method == Method::superop || + (method == Method::tensor_network && !has_statevector_ops(circ)))) { noise_model.enable_superop_method(max_parallel_threads_); superop_enabled = true; } else if (kraus_noise && !kraus_enabled && - (method == Method::statevector || method == Method::matrix_product_state || (method == Method::tensor_network && has_statevector_ops(circ))) ) { + (method == Method::statevector || + method == Method::matrix_product_state || + (method == Method::tensor_network && + has_statevector_ops(circ)))) { noise_model.enable_kraus_method(max_parallel_threads_); kraus_enabled = true; } @@ -1864,29 +1881,26 @@ Controller::simulation_methods(std::vector &circuits, std::vector sim_methods(circuits.size(), method_); if (method_ == Method::density_matrix || method_ == Method::superop) { noise_model.enable_superop_method(max_parallel_threads_); - } else if (kraus_noise && ( - method_ == Method::statevector - || method_ == Method::matrix_product_state)) { + } else if (kraus_noise && (method_ == Method::statevector || + method_ == Method::matrix_product_state)) { noise_model.enable_kraus_method(max_parallel_threads_); - } - else if(method_ == Method::tensor_network){ + } else if (method_ == Method::tensor_network) { bool has_save_statevec = false; - for (const auto& circ: circuits) { - has_save_statevec |= has_statevector_ops(circ); - if(has_save_statevec) + for (const auto &circ : circuits) { + has_save_statevec |= has_statevector_ops(*circ); + if (has_save_statevec) break; } - if(!has_save_statevec) + if (!has_save_statevec) noise_model.enable_superop_method(max_parallel_threads_); - else if(kraus_noise) + else if (kraus_noise) noise_model.enable_kraus_method(max_parallel_threads_); } return sim_methods; } -Controller::Method -Controller::automatic_simulation_method(const Circuit &circ, - const Noise::NoiseModel &noise_model) const { +Controller::Method Controller::automatic_simulation_method( + const Circuit &circ, const Noise::NoiseModel &noise_model) const { // If circuit and noise model are Clifford run on Stabilizer simulator if (validate_method(Method::stabilizer, circ, noise_model, false)) { return Method::stabilizer; @@ -1908,12 +1922,10 @@ Controller::automatic_simulation_method(const Circuit &circ, // operations only with preference given by memory requirements // statevector > density matrix > matrix product state > unitary > superop // typically any save state instructions will decide the method. - const std::vector methods({Method::statevector, - Method::density_matrix, - Method::matrix_product_state, - Method::unitary, - Method::superop}); - for (const auto& method : methods) { + const std::vector methods( + {Method::statevector, Method::density_matrix, + Method::matrix_product_state, Method::unitary, Method::superop}); + for (const auto &method : methods) { if (validate_method(method, circ, noise_model, false)) return method; } @@ -1925,34 +1937,40 @@ Controller::automatic_simulation_method(const Circuit &circ, return Method::statevector; } -bool Controller::validate_method(Method method, - const Circuit &circ, +bool Controller::validate_method(Method method, const Circuit &circ, const Noise::NoiseModel &noise_model, bool throw_except) const { // Switch wrapper for templated function validate_state switch (method) { - case Method::stabilizer: - return validate_state(Stabilizer::State(), circ, noise_model, throw_except); - case Method::extended_stabilizer: - return validate_state(ExtendedStabilizer::State(), circ, noise_model, throw_except); - case Method::matrix_product_state: - return validate_state(MatrixProductState::State(), circ, noise_model, throw_except); - case Method::statevector: - return validate_state(Statevector::State<>(), circ, noise_model, throw_except); - case Method::density_matrix: - return validate_state(DensityMatrix::State<>(), circ, noise_model, throw_except); - case Method::unitary: - return validate_state(QubitUnitary::State<>(), circ, noise_model, throw_except); - case Method::superop: - return validate_state(QubitSuperoperator::State<>(), circ, noise_model, throw_except); - case Method::tensor_network: - return validate_state(TensorNetwork::State<>(), circ, noise_model, throw_except); - case Method::automatic: - throw std::runtime_error("Cannot validate circuit for unresolved simulation method."); + case Method::stabilizer: + return validate_state(Stabilizer::State(), circ, noise_model, throw_except); + case Method::extended_stabilizer: + return validate_state(ExtendedStabilizer::State(), circ, noise_model, + throw_except); + case Method::matrix_product_state: + return validate_state(MatrixProductState::State(), circ, noise_model, + throw_except); + case Method::statevector: + return validate_state(Statevector::State<>(), circ, noise_model, + throw_except); + case Method::density_matrix: + return validate_state(DensityMatrix::State<>(), circ, noise_model, + throw_except); + case Method::unitary: + return validate_state(QubitUnitary::State<>(), circ, noise_model, + throw_except); + case Method::superop: + return validate_state(QubitSuperoperator::State<>(), circ, noise_model, + throw_except); + case Method::tensor_network: + return validate_state(TensorNetwork::State<>(), circ, noise_model, + throw_except); + case Method::automatic: + throw std::runtime_error( + "Cannot validate circuit for unresolved simulation method."); } } - template bool Controller::validate_state(const state_t &state, const Circuit &circ, const Noise::NoiseModel &noise, @@ -1980,13 +1998,17 @@ bool Controller::validate_state(const state_t &state, const Circuit &circ, // Validate memory requirements bool memory_valid = true; if (max_memory_mb_ > 0) { - size_t required_mb = state.required_memory_mb(circ.num_qubits, circ.ops) / num_process_per_experiment_; - size_t mem_size = (sim_device_ == Device::GPU) ? max_memory_mb_ + max_gpu_memory_mb_ : max_memory_mb_; + size_t required_mb = state.required_memory_mb(circ.num_qubits, circ.ops) / + num_process_per_experiment_; + size_t mem_size = (sim_device_ == Device::GPU) + ? max_memory_mb_ + max_gpu_memory_mb_ + : max_memory_mb_; memory_valid = (required_mb <= mem_size); if (throw_except && !memory_valid) { error_msg << "Insufficient memory to run circuit " << circ_name; error_msg << " using the " << state.name() << " simulator."; - error_msg << " Required memory: " << required_mb << "M, max memory: " << max_memory_mb_ << "M"; + error_msg << " Required memory: " << required_mb + << "M, max memory: " << max_memory_mb_ << "M"; if (sim_device_ == Device::GPU) { error_msg << " (Host) + " << max_gpu_memory_mb_ << "M (GPU)"; } @@ -2014,13 +2036,15 @@ void Controller::save_exception_to_results(Result &result, } } -int_t Controller::get_matrix_bits(const Operations::Op& op) const -{ +int_t Controller::get_matrix_bits(const Operations::Op &op) const { int_t bit = 1; - if(op.type == Operations::OpType::matrix || op.type == Operations::OpType::diagonal_matrix || op.type == Operations::OpType::initialize) + if (op.type == Operations::OpType::matrix || + op.type == Operations::OpType::diagonal_matrix || + op.type == Operations::OpType::initialize) bit = op.qubits.size(); - else if(op.type == Operations::OpType::kraus || op.type == Operations::OpType::superop){ - if(method_ == Method::density_matrix) + else if (op.type == Operations::OpType::kraus || + op.type == Operations::OpType::superop) { + if (method_ == Method::density_matrix) bit = op.qubits.size() * 2; else bit = op.qubits.size(); @@ -2028,22 +2052,22 @@ int_t Controller::get_matrix_bits(const Operations::Op& op) const return bit; } -int_t Controller::get_max_matrix_qubits(const Circuit &circ) const -{ +int_t Controller::get_max_matrix_qubits(const Circuit &circ) const { int_t max_bits = 0; int_t i; - for(i=0;i #include "misc/hacks.hpp" +#include -#include "framework/results/result.hpp" -#include "framework/python_parser.hpp" -#include "framework/matrix.hpp" #include "framework/config.hpp" +#include "framework/matrix.hpp" +#include "framework/python_parser.hpp" +#include "framework/results/result.hpp" //========================================================================= // Controller Execute interface @@ -30,22 +30,24 @@ namespace AER { template -Result controller_execute(const inputdata_t& qobj) { +Result controller_execute(const inputdata_t &qobj) { controller_t controller; // Fix for MacOS and OpenMP library double initialization crash. // Issue: https://github.com/Qiskit/qiskit-aer/issues/1 if (Parser::check_key("config", qobj)) { - std::string path; - const auto& config = Parser::get_value("config", qobj); - Parser::get_value(path, "library_dir", config); - Hacks::maybe_load_openmp(path); + std::string path; + const auto &config = Parser::get_value("config", qobj); + Parser::get_value(path, "library_dir", config); + Hacks::maybe_load_openmp(path); } return controller.execute(qobj); } template -Result controller_execute(std::vector& input_circs, AER::Noise::NoiseModel &noise_model, AER::Config &config) { +Result controller_execute(std::vector> &input_circs, + AER::Noise::NoiseModel &noise_model, + AER::Config &config) { controller_t controller; bool truncate = config.enable_truncation; @@ -63,7 +65,7 @@ Result controller_execute(std::vector& input_circs, AER::Noise::NoiseMo // i is the instruction index in the experiment // j is the param index in the instruction // pars = [par0, par1, ...] is a list of different parameterizations - using pos_t = std::pair; + using pos_t = std::pair; using exp_params_t = std::vector>>; std::vector param_table = config.param_table; @@ -73,54 +75,69 @@ Result controller_execute(std::vector& input_circs, AER::Noise::NoiseMo R"(Invalid parameterized circuits: "parameterizations" length does not match number of circuits.)"); } - std::vector circs; + std::vector> circs; + std::vector> template_circs; try { // Load circuits - for (size_t i=0; iset_params(truncate); + circ->set_metadata(config, truncate); circs.push_back(circ); + template_circs.push_back(circ); } else { // Get base circuit without truncation - circ.set_params(false); - circ.set_metadata(config, truncate); + circ->set_params(false); + circ->set_metadata(config, truncate); // Load different parameterizations of the initial circuit const auto circ_params = param_table[i]; const size_t num_params = circ_params[0].second.size(); - const size_t num_instr = circ.ops.size(); - for (size_t j=0; jops.size(); + for (size_t j = 0; j < num_params; j++) { // Make a copy of the initial circuit - Circuit param_circ = circ; + auto param_circ = std::make_shared(*circ); for (const auto ¶ms : circ_params) { const auto instr_pos = params.first.first; const auto param_pos = params.first.second; // Validation - if (instr_pos >= num_instr) { - throw std::invalid_argument(R"(Invalid parameterized qobj: instruction position out of range)"); - } - auto &op = param_circ.ops[instr_pos]; - if (param_pos >= op.params.size()) { - throw std::invalid_argument(R"(Invalid parameterized qobj: instruction param position out of range)"); - } - if (j >= params.second.size()) { - throw std::invalid_argument(R"(Invalid parameterized qobj: parameterization value out of range)"); + if (instr_pos == AER::Config::GLOBAL_PHASE_POS) { + // negative position is for global phase + circ->global_phase_angle = params.second[j]; + } else { + if (instr_pos >= num_instr) { + std::cout << "Invalid parameterization: instruction position " + "out of range: " + << instr_pos << std::endl; + throw std::invalid_argument( + R"(Invalid parameterization: instruction position out of range)"); + } + auto &op = param_circ->ops[instr_pos]; + if (param_pos >= op.params.size()) { + throw std::invalid_argument( + R"(Invalid parameterization: instruction param position out of range)"); + } + if (j >= params.second.size()) { + throw std::invalid_argument( + R"(Invalid parameterization: parameterization value out of range)"); + } + // Update the param + op.params[param_pos] = params.second[j]; } - // Update the param - op.params[param_pos] = params.second[j]; } // Run truncation. - // TODO: Truncation should be performed and parameters should be resolved after it. - // However, parameters are associated with indices of instructions, which can be changed in truncation. - // Therefore, current implementation performs truncation for each parameter set. + // TODO: Truncation should be performed and parameters should be + // resolved after it. However, parameters are associated with indices + // of instructions, which can be changed in truncation. Therefore, + // current implementation performs truncation for each parameter set. if (truncate) { - param_circ.set_params(true); - param_circ.set_metadata(config, true); + param_circ->set_params(true); + param_circ->set_metadata(config, true); } - circs.push_back(std::move(param_circ)); + circs.push_back(param_circ); + template_circs.push_back(circ); } } } @@ -138,10 +155,10 @@ Result controller_execute(std::vector& input_circs, AER::Noise::NoiseMo if (config.seed_simulator.has_value()) seed = config.seed_simulator.value(); else - seed = circs[0].seed; + seed = circs[0]->seed; - for (auto& circ: circs) { - circ.seed = seed + seed_shift; + for (auto &circ : circs) { + circ->seed = seed + seed_shift; seed_shift += 2113; } @@ -149,9 +166,13 @@ Result controller_execute(std::vector& input_circs, AER::Noise::NoiseMo // Issue: https://github.com/Qiskit/qiskit-aer/issues/1 Hacks::maybe_load_openmp(config.library_dir); controller.set_config(config); - return controller.execute(circs, noise_model, config); -} + auto ret = controller.execute(circs, noise_model, config); + + for (size_t i = 0; i < ret.results.size(); ++i) + ret.results[i].circuit = template_circs[i]; + return ret; +} } // end namespace AER #endif diff --git a/src/controllers/state_controller.hpp b/src/controllers/state_controller.hpp index fd54a581d0..bb7ac166e7 100644 --- a/src/controllers/state_controller.hpp +++ b/src/controllers/state_controller.hpp @@ -15,11 +15,11 @@ #ifndef _aer_state_hpp_ #define _aer_state_hpp_ -#include +#include #include +#include #include #include -#include #include "framework/rng.hpp" #include "misc/warnings.hpp" @@ -37,11 +37,11 @@ DISABLE_WARNING_PUSH DISABLE_WARNING_POP #include "framework/creg.hpp" +#include "framework/linalg/vector.hpp" #include "framework/qobj.hpp" #include "framework/results/experiment_result.hpp" #include "framework/results/result.hpp" #include "framework/rng.hpp" -#include "framework/linalg/vector.hpp" #include "noise/noise_model.hpp" @@ -56,8 +56,8 @@ DISABLE_WARNING_POP #include "simulators/statevector/qubitvector.hpp" #include "simulators/statevector/statevector_state.hpp" #include "simulators/superoperator/superoperator_state.hpp" -#include "simulators/unitary/unitarymatrix.hpp" #include "simulators/unitary/unitary_state.hpp" +#include "simulators/unitary/unitarymatrix.hpp" #ifdef AER_THRUST_SUPPORTED #include "simulators/density_matrix/densitymatrix_thrust.hpp" @@ -103,7 +103,7 @@ class AerState { //----------------------------------------------------------------------- AerState() { set_random_seed(); }; - virtual ~AerState() { }; + virtual ~AerState(){}; //----------------------------------------------------------------------- // Configuration @@ -111,28 +111,28 @@ class AerState { // set configuration. // All of the configuration must be done before calling any gate operations. - virtual void configure(const std::string& key, const std::string& value); + virtual void configure(const std::string &key, const std::string &value); // configure a method. - virtual bool set_method(const std::string& name); + virtual bool set_method(const std::string &name); // configure a device. - virtual bool set_device(const std::string& name); + virtual bool set_device(const std::string &name); // configure a precision. - virtual bool set_precision(const std::string& name); + virtual bool set_precision(const std::string &name); // configure custatevec enabled or not - virtual bool set_custatevec(const bool& enabled); + virtual bool set_custatevec(const bool &enabled); // configure number of threads to update state - virtual bool set_parallel_state_update(const uint_t& parallel_state_update); + virtual bool set_parallel_state_update(const uint_t ¶llel_state_update); // configure max number of qubits for a gate - virtual bool set_max_gate_qubits(const uint_t& max_gate_qubits); + virtual bool set_max_gate_qubits(const uint_t &max_gate_qubits); // configure cache blocking qubits - virtual bool set_blocking_qubits(const uint_t& blocking_qubits); + virtual bool set_blocking_qubits(const uint_t &blocking_qubits); // Return true if gate operations have been performed and no configuration // is permitted. @@ -154,7 +154,7 @@ class AerState { // Clear state and buffered ops virtual void clear(); - virtual ExperimentResult& last_result() { return last_result_; }; + virtual ExperimentResult &last_result() { return last_result_; }; //----------------------------------------------------------------------- // Initialization @@ -170,14 +170,16 @@ class AerState { void set_seed(int_t seed); // Allocate qubits with inputted complex array - // method must be statevector and the length of the array must be 2^{num_qubits} - // given data will not be freed in this class - virtual reg_t initialize_statevector(uint_t num_qubits, complex_t* data, bool copy); + // method must be statevector and the length of the array must be + // 2^{num_qubits} given data will not be freed in this class + virtual reg_t initialize_statevector(uint_t num_qubits, complex_t *data, + bool copy); // Allocate qubits with inputted complex array - // method must be densitymatrix and the length of the array must be 4^{num_qubits} - // given data will not be freed in this class - virtual reg_t initialize_density_matrix(uint_t num_qubits, complex_t* data, bool f_order, bool copy); + // method must be densitymatrix and the length of the array must be + // 4^{num_qubits} given data will not be freed in this class + virtual reg_t initialize_density_matrix(uint_t num_qubits, complex_t *data, + bool f_order, bool copy); // Release internal statevector as a vector virtual AER::Vector move_to_vector(); @@ -208,8 +210,11 @@ class AerState { // Apply a N-qubit matrix to the state vector. virtual void apply_unitary(const reg_t &qubits, const cmatrix_t &mat); - // Apply a stacked set of 2^control_count target_count--qubit matrix to the state vector. - virtual void apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, const std::vector &mats); + // Apply a stacked set of 2^control_count target_count--qubit matrix to the + // state vector. + virtual void apply_multiplexer(const reg_t &control_qubits, + const reg_t &target_qubits, + const std::vector &mats); // Apply a N-qubit diagonal matrix to the state vector. virtual void apply_diagonal_matrix(const reg_t &qubits, const cvector_t &mat); @@ -259,22 +264,28 @@ class AerState { // 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 - virtual void apply_mcphase(const reg_t &qubits, const std::complex phase); + virtual void apply_mcphase(const reg_t &qubits, + const std::complex phase); // Apply an optimized H gate virtual void apply_h(const uint_t qubit); // Apply an optimized single-qubit U gate - virtual void apply_u(const uint_t qubit, const double theta, const double phi, const double lambda); + virtual void apply_u(const uint_t qubit, const double theta, const double phi, + const double lambda); // Apply an optimized CU gate - virtual void apply_cu(const reg_t &qubits, const double theta, const double phi, const double lambda, const double gammma); + virtual void apply_cu(const reg_t &qubits, const double theta, + const double phi, const double lambda, + const double gammma); // Apply a general multi-controlled single-qubit unitary 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 - virtual void apply_mcu(const reg_t &qubits, const double theta, const double phi, const double lambda, const double gammma); + virtual void apply_mcu(const reg_t &qubits, const double theta, + const double phi, const double lambda, + const double gammma); // Apply a general multi-controlled SWAP gate // If N=2 this implements an optimized SWAP gate @@ -341,19 +352,21 @@ class AerState { // The input is a length M list of random reals between [0, 1) used for // generating samples. // The returned value is unordered sampled outcomes - virtual std::vector sample_memory(const reg_t &qubits, uint_t shots); + virtual std::vector sample_memory(const reg_t &qubits, + uint_t shots); // Return M sampled outcomes for Z-basis measurement of specified qubits // The input is a length M list of random reals between [0, 1) used for // generating samples. // The returned value is a map from outcome to its number of samples. - virtual std::unordered_map sample_counts(const reg_t &qubits, uint_t shots); + virtual std::unordered_map sample_counts(const reg_t &qubits, + uint_t shots); //----------------------------------------------------------------------- // Operation management //----------------------------------------------------------------------- // Buffer Operations::Op - virtual void buffer_op(const Operations::Op&& op); + virtual void buffer_op(const Operations::Op &&op); // Flush buffered Operations::Op virtual void flush_ops(); @@ -384,22 +397,20 @@ class AerState { Method method_ = Method::statevector; const std::unordered_map method_names_ = { - {Method::statevector, "statevector"}, - {Method::density_matrix, "density_matrix"}, - {Method::matrix_product_state, "matrix_product_state"}, - {Method::stabilizer, "stabilizer"}, - {Method::extended_stabilizer, "extended_stabilizer"}, - {Method::unitary, "unitary"}, - {Method::superop, "superop"} - }; + {Method::statevector, "statevector"}, + {Method::density_matrix, "density_matrix"}, + {Method::matrix_product_state, "matrix_product_state"}, + {Method::stabilizer, "stabilizer"}, + {Method::extended_stabilizer, "extended_stabilizer"}, + {Method::unitary, "unitary"}, + {Method::superop, "superop"}}; Device device_ = Device::CPU; const std::unordered_map device_names_ = { - {Device::CPU, "CPU"}, - {Device::GPU, "GPU"}, - {Device::ThrustCPU, "ThrustCPU"} - }; + {Device::CPU, "CPU"}, + {Device::GPU, "GPU"}, + {Device::ThrustCPU, "ThrustCPU"}}; Precision precision_ = Precision::Double; @@ -430,22 +441,24 @@ class AerState { bool AerState::is_gpu(bool raise_error) const { #ifndef AER_THRUST_CUDA if (raise_error) - throw std::runtime_error("Simulation device \"GPU\" is not supported on this system"); + throw std::runtime_error( + "Simulation device \"GPU\" is not supported on this system"); else return false; #else int nDev; if (cudaGetDeviceCount(&nDev) != cudaSuccess) { - if (raise_error) { - cudaGetLastError(); - throw std::runtime_error("No CUDA device available!"); - } else return false; + if (raise_error) { + cudaGetLastError(); + throw std::runtime_error("No CUDA device available!"); + } else + return false; } #endif return true; } -void AerState::configure(const std::string& _key, const std::string& _value) { +void AerState::configure(const std::string &_key, const std::string &_value) { std::string key = _key; std::transform(key.begin(), key.end(), key.begin(), ::tolower); @@ -477,44 +490,67 @@ void AerState::configure(const std::string& _key, const std::string& _value) { throw std::runtime_error(msg.str()); } - static std::unordered_set str_config = { "method", "device", "precision", "extended_stabilizer_sampling_method", - "mps_sample_measure_algorithm", "mps_log_data", "mps_swap_direction"}; - static std::unordered_set int_config = { "seed_simulator", "max_parallel_threads", "max_memory_mb", "parallel_state_update", - "blocking_qubits", "batched_shots_gpu_max_qubits", "statevector_parallel_threshold", - "statevector_sample_measure_opt", "stabilizer_max_snapshot_probabilities", - "extended_stabilizer_metropolis_mixing_time", "extended_stabilizer_norm_estimation_samples", - "extended_stabilizer_norm_estimation_repetitions", "extended_stabilizer_parallel_threshold", - "extended_stabilizer_probabilities_snapshot_samples", "matrix_product_state_max_bond_dimension", - "fusion_max_qubit", "fusion_threshold"}; - static std::unordered_set double_config = { "extended_stabilizer_approximation_error", "matrix_product_state_truncation_threshold", - }; - static std::unordered_set bool_config = { "custatevec_enable", "blocking_enable", "batched_shots_gpu", "fusion_enable", "fusion_verbose"}; - - if (str_config.find(key) != str_config.end() ) { + static std::unordered_set str_config = { + "method", + "device", + "precision", + "extended_stabilizer_sampling_method", + "mps_sample_measure_algorithm", + "mps_log_data", + "mps_swap_direction"}; + static std::unordered_set int_config = { + "seed_simulator", + "max_parallel_threads", + "max_memory_mb", + "parallel_state_update", + "blocking_qubits", + "batched_shots_gpu_max_qubits", + "statevector_parallel_threshold", + "statevector_sample_measure_opt", + "stabilizer_max_snapshot_probabilities", + "extended_stabilizer_metropolis_mixing_time", + "extended_stabilizer_norm_estimation_samples", + "extended_stabilizer_norm_estimation_repetitions", + "extended_stabilizer_parallel_threshold", + "extended_stabilizer_probabilities_snapshot_samples", + "matrix_product_state_max_bond_dimension", + "fusion_max_qubit", + "fusion_threshold"}; + static std::unordered_set double_config = { + "extended_stabilizer_approximation_error", + "matrix_product_state_truncation_threshold", + }; + static std::unordered_set bool_config = { + "custatevec_enable", "blocking_enable", "batched_shots_gpu", + "fusion_enable", "fusion_verbose"}; + + if (str_config.find(key) != str_config.end()) { configs_[_key] = _value; - } else if (int_config.find(key) != int_config.end() ) { + } else if (int_config.find(key) != int_config.end()) { configs_[_key] = std::stoi(value); - } else if (bool_config.find(key) != bool_config.end() ) { + } else if (bool_config.find(key) != bool_config.end()) { configs_[_key] = "true" == value; - } else if (double_config.find(key) != double_config.end() ) { + } else if (double_config.find(key) != double_config.end()) { configs_[_key] = std::stod(value); } else { std::stringstream msg; msg << "not supported configuration: " << key << "=" << value << std::endl; throw std::runtime_error(msg.str()); } - }; -bool AerState::set_method(const std::string& method_name) { +bool AerState::set_method(const std::string &method_name) { assert_not_initialized(); - auto it = find_if(method_names_.begin(), method_names_.end(), [method_name](const auto& vt) { return vt.second == method_name; }); - if (it == method_names_.end()) return false; + auto it = find_if( + method_names_.begin(), method_names_.end(), + [method_name](const auto &vt) { return vt.second == method_name; }); + if (it == method_names_.end()) + return false; method_ = it->first; return true; }; -bool AerState::set_device(const std::string& device_name) { +bool AerState::set_device(const std::string &device_name) { assert_not_initialized(); if (device_name == "cpu") device_ = Device::CPU; @@ -527,7 +563,7 @@ bool AerState::set_device(const std::string& device_name) { return true; }; -bool AerState::set_precision(const std::string& precision_name) { +bool AerState::set_precision(const std::string &precision_name) { assert_not_initialized(); if (precision_name == "single") precision_ = Precision::Single; @@ -538,26 +574,25 @@ bool AerState::set_precision(const std::string& precision_name) { return true; }; -bool AerState::set_custatevec(const bool& enabled) { +bool AerState::set_custatevec(const bool &enabled) { assert_not_initialized(); cuStateVec_enable_ = enabled; return true; }; -bool AerState::set_parallel_state_update(const uint_t& parallel_state_update) { +bool AerState::set_parallel_state_update(const uint_t ¶llel_state_update) { assert_not_initialized(); parallel_state_update_ = parallel_state_update; return true; }; -bool AerState::set_max_gate_qubits(const uint_t& max_gate_qubits) { +bool AerState::set_max_gate_qubits(const uint_t &max_gate_qubits) { assert_not_initialized(); max_gate_qubits_ = max_gate_qubits; return true; }; -bool AerState::set_blocking_qubits(const uint_t& blocking_qubits) -{ +bool AerState::set_blocking_qubits(const uint_t &blocking_qubits) { assert_not_initialized(); cache_block_qubits_ = blocking_qubits; return true; @@ -579,9 +614,7 @@ void AerState::assert_not_initialized() const { } }; -void AerState::set_random_seed() { - set_seed(std::random_device()()); -}; +void AerState::set_random_seed() { set_seed(std::random_device()()); }; void AerState::set_seed(int_t seed) { seed_ = seed; @@ -592,7 +625,7 @@ reg_t AerState::allocate_qubits(uint_t num_qubits) { assert_not_initialized(); reg_t ret; for (auto i = 0; i < num_qubits; ++i) - ret.push_back(num_of_qubits_++); + ret.push_back(num_of_qubits_++); return ret; }; @@ -618,85 +651,108 @@ void AerState::initialize_state_controller() { cache_block_pass_.set_config(configs_); }; -void AerState::initialize_qreg_state(std::shared_ptr state) { +void AerState::initialize_qreg_state( + std::shared_ptr state) { if (!state) { if (method_ == Method::statevector) { if (device_ == Device::CPU) if (precision_ == Precision::Double) - state_ = std::make_shared>>(); + state_ = + std::make_shared>>(); else - state_ = std::make_shared>>(); + state_ = + std::make_shared>>(); else // if (device_ == Device::GPU) - #ifdef AER_THRUST_SUPPORTED - if (precision_ == Precision::Double) - state_ = std::make_shared>>(); - else - state_ = std::make_shared>>(); - #else - throw std::runtime_error("specified method does not support non-CPU device: method=statevector"); - #endif +#ifdef AER_THRUST_SUPPORTED + if (precision_ == Precision::Double) + state_ = std::make_shared< + Statevector::State>>(); + else + state_ = std::make_shared< + Statevector::State>>(); +#else + throw std::runtime_error("specified method does not support non-CPU " + "device: method=statevector"); +#endif } else if (method_ == Method::density_matrix) { if (device_ == Device::CPU) if (precision_ == Precision::Double) - state_ = std::make_shared>>(); + state_ = std::make_shared< + DensityMatrix::State>>(); else - state_ = std::make_shared>>(); + state_ = std::make_shared< + DensityMatrix::State>>(); else // if (device_ == Device::GPU) - #ifdef AER_THRUST_SUPPORTED - if (precision_ == Precision::Double) - state_ = std::make_shared>>(); - else - state_ = std::make_shared>>(); - #else - throw std::runtime_error("specified method does not support non-CPU device: method=density_matrix"); - #endif +#ifdef AER_THRUST_SUPPORTED + if (precision_ == Precision::Double) + state_ = std::make_shared< + DensityMatrix::State>>(); + else + state_ = std::make_shared< + DensityMatrix::State>>(); +#else + throw std::runtime_error("specified method does not support non-CPU " + "device: method=density_matrix"); +#endif } else if (method_ == Method::unitary) { if (device_ == Device::CPU) if (precision_ == Precision::Double) - state_ = std::make_shared>>(); + state_ = std::make_shared< + QubitUnitary::State>>(); else - state_ = std::make_shared>>(); + state_ = + std::make_shared>>(); else // if (device_ == Device::GPU) - #ifdef AER_THRUST_SUPPORTED - if (precision_ == Precision::Double) - state_ = std::make_shared>>(); - else - state_ = std::make_shared>>(); - #else - throw std::runtime_error("specified method does not support non-CPU device: method=unitary"); - #endif +#ifdef AER_THRUST_SUPPORTED + if (precision_ == Precision::Double) + state_ = std::make_shared< + QubitUnitary::State>>(); + else + state_ = std::make_shared< + QubitUnitary::State>>(); +#else + throw std::runtime_error( + "specified method does not support non-CPU device: method=unitary"); +#endif } else if (method_ == Method::matrix_product_state) { if (device_ == Device::CPU) state_ = std::make_shared(); else // if (device_ == Device::GPU) - throw std::runtime_error("specified method does not support non-CPU device: method=matrix_product_state"); + throw std::runtime_error("specified method does not support non-CPU " + "device: method=matrix_product_state"); } else if (method_ == Method::stabilizer) { if (device_ == Device::CPU) state_ = std::make_shared(); else // if (device_ == Device::GPU) - throw std::runtime_error("specified method does not support non-CPU device: method=stabilizer"); + throw std::runtime_error("specified method does not support non-CPU " + "device: method=stabilizer"); } else if (method_ == Method::extended_stabilizer) { if (device_ == Device::CPU) state_ = std::make_shared(); else // if (device_ == Device::GPU) - throw std::runtime_error("specified method does not support non-CPU device: method=extended_stabilizer"); + throw std::runtime_error("specified method does not support non-CPU " + "device: method=extended_stabilizer"); } else if (method_ == Method::superop) { if (device_ == Device::CPU) if (precision_ == Precision::Double) - state_ = std::make_shared>>(); + state_ = std::make_shared< + QubitSuperoperator::State>>(); else - state_ = std::make_shared>>(); + state_ = std::make_shared< + QubitSuperoperator::State>>(); else // if (device_ == Device::GPU) - throw std::runtime_error("specified method does not support non-CPU device: method=superop"); + throw std::runtime_error( + "specified method does not support non-CPU device: method=superop"); } else { - throw std::runtime_error("not supported method."); + throw std::runtime_error("not supported method."); } } else { state_ = state; } uint_t block_qubits = cache_block_qubits_; - if(!cache_block_pass_.enabled() || !state_->multi_chunk_distribution_supported()) + if (!cache_block_pass_.enabled() || + !state_->multi_chunk_distribution_supported()) block_qubits = num_of_qubits_; state_->set_config(configs_); state_->set_distribution(num_process_per_experiment_); @@ -719,7 +775,8 @@ void AerState::initialize() { initialized_ = true; }; -reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t* data, bool copy) { +reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t *data, + bool copy) { assert_not_initialized(); num_of_qubits_ = num_of_qubits; @@ -728,9 +785,11 @@ reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t* data, bo initialize_state_controller(); if (device_ != Device::CPU) - throw std::runtime_error("only CPU device supports initialize_statevector()"); + throw std::runtime_error( + "only CPU device supports initialize_statevector()"); if (precision_ != Precision::Double) - throw std::runtime_error("only Double precision supports initialize_statevector()"); + throw std::runtime_error( + "only Double precision supports initialize_statevector()"); auto state = std::make_shared>>(); @@ -739,8 +798,8 @@ reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t* data, bo if (cache_block_qubits_ > 0) copy = true; - auto vec = copy? AER::Vector::copy_from_buffer(data_size, data) - : AER::Vector::move_from_buffer(data_size, data); + auto vec = copy ? AER::Vector::copy_from_buffer(data_size, data) + : AER::Vector::move_from_buffer(data_size, data); auto qv = QV::QubitVector(); qv.move_from_vector(std::move(vec)); @@ -756,7 +815,8 @@ reg_t AerState::initialize_statevector(uint_t num_of_qubits, complex_t* data, bo return ret; }; -reg_t AerState::initialize_density_matrix(uint_t num_of_qubits, complex_t* data, bool f_order, bool copy) { +reg_t AerState::initialize_density_matrix(uint_t num_of_qubits, complex_t *data, + bool f_order, bool copy) { assert_not_initialized(); num_of_qubits_ = num_of_qubits; @@ -765,19 +825,22 @@ reg_t AerState::initialize_density_matrix(uint_t num_of_qubits, complex_t* data, initialize_state_controller(); if (device_ != Device::CPU) - throw std::runtime_error("only CPU device supports initialize_density_matrix()"); + throw std::runtime_error( + "only CPU device supports initialize_density_matrix()"); if (precision_ != Precision::Double) - throw std::runtime_error("only Double precision supports initialize_density_matrix()"); + throw std::runtime_error( + "only Double precision supports initialize_density_matrix()"); - auto state = std::make_shared>>(); + auto state = + std::make_shared>>(); initialize_qreg_state(state); if (cache_block_qubits_ > 0) copy = true; - auto vec = copy? AER::Vector::copy_from_buffer(data_size, data) - : AER::Vector::move_from_buffer(data_size, data); + auto vec = copy ? AER::Vector::copy_from_buffer(data_size, data) + : AER::Vector::move_from_buffer(data_size, data); auto dm = QV::DensityMatrix(); dm.move_from_vector(std::move(vec)); @@ -812,14 +875,16 @@ AER::Vector AerState::move_to_vector() { flush_ops(); Operations::Op op; - if (method_ == Method::statevector || method_ == Method::matrix_product_state) { + if (method_ == Method::statevector || + method_ == Method::matrix_product_state) { op.type = Operations::OpType::save_statevec; op.name = "save_statevec"; } else if (method_ == Method::density_matrix) { op.type = Operations::OpType::save_state; op.name = "save_density_matrix"; } else { - throw std::runtime_error("move_to_vector() supports only statevector or matrix_product_state or density_matrix methods"); + throw std::runtime_error("move_to_vector() supports only statevector or " + "matrix_product_state or density_matrix methods"); } for (auto i = 0; i < num_of_qubits_; ++i) op.qubits.push_back(i); @@ -829,17 +894,27 @@ AER::Vector AerState::move_to_vector() { ExperimentResult ret; state_->apply_op(op, ret, rng_, true); - if (method_ == Method::statevector || method_ == Method::matrix_product_state) { - auto vec = std::move(static_cast>>(std::move(ret).data).value()["s"].value()); + if (method_ == Method::statevector || + method_ == Method::matrix_product_state) { + auto vec = std::move( + static_cast>>(std::move(ret).data) + .value()["s"] + .value()); clear(); return std::move(vec); } else if (method_ == Method::density_matrix) { - auto mat = std::move(static_cast, 1>>(std::move(ret).data).value()["s"].value()); - auto vec = Vector::move_from_buffer(mat.GetColumns() * mat.GetRows(), mat.move_to_buffer()); + auto mat = + std::move(static_cast, 1>>( + std::move(ret).data) + .value()["s"] + .value()); + auto vec = Vector::move_from_buffer( + mat.GetColumns() * mat.GetRows(), mat.move_to_buffer()); clear(); return std::move(vec); } else { - throw std::runtime_error("move_to_vector() supports only statevector or matrix_product_state or density_matrix methods"); + throw std::runtime_error("move_to_vector() supports only statevector or " + "matrix_product_state or density_matrix methods"); } }; @@ -849,14 +924,16 @@ matrix AerState::move_to_matrix() { flush_ops(); Operations::Op op; - if (method_ == Method::statevector || method_ == Method::matrix_product_state) { + if (method_ == Method::statevector || + method_ == Method::matrix_product_state) { op.type = Operations::OpType::save_statevec; op.name = "save_statevec"; } else if (method_ == Method::density_matrix) { op.type = Operations::OpType::save_state; op.name = "save_density_matrix"; } else { - throw std::runtime_error("move_to_matrix() supports only statevector or matrix_product_state or density_matrix methods"); + throw std::runtime_error("move_to_matrix() supports only statevector or " + "matrix_product_state or density_matrix methods"); } for (auto i = 0; i < num_of_qubits_; ++i) op.qubits.push_back(i); @@ -866,41 +943,35 @@ matrix AerState::move_to_matrix() { ExperimentResult ret; state_->apply_op(op, ret, rng_, true); - if (method_ == Method::statevector || method_ == Method::matrix_product_state) { + if (method_ == Method::statevector || + method_ == Method::matrix_product_state) { auto vec = std::move( - std::move( - std::move( - static_cast>>( - std::move(ret).data - ) - ).value() - )["s"].value() - ); + std::move(std::move(static_cast>>( + std::move(ret).data)) + .value())["s"] + .value()); clear(); return matrix((1ULL << num_of_qubits_), 1, vec.move_to_buffer()); } else if (method_ == Method::density_matrix) { auto mat = std::move( - std::move( - std::move( - static_cast, 1>>( - std::move(ret).data - ) - ).value() - )["s"].value() - ); + std::move( + std::move(static_cast, 1>>( + std::move(ret).data)) + .value())["s"] + .value()); clear(); return std::move(mat); } else { - throw std::runtime_error("move_to_matrix() supports only statevector or matrix_product_state or density_matrix methods"); + throw std::runtime_error("move_to_matrix() supports only statevector or " + "matrix_product_state or density_matrix methods"); } }; - //----------------------------------------------------------------------- // Apply Initialization //----------------------------------------------------------------------- -void AerState::apply_initialize(const reg_t &qubits, cvector_t && vec) { +void AerState::apply_initialize(const reg_t &qubits, cvector_t &&vec) { assert_initialized(); Operations::Op op; op.type = Operations::OpType::initialize; @@ -912,7 +983,7 @@ void AerState::apply_initialize(const reg_t &qubits, cvector_t && vec) { state_->apply_op(op, last_result_, rng_); }; -void AerState::set_statevector(const reg_t &qubits, cvector_t && vec) { +void AerState::set_statevector(const reg_t &qubits, cvector_t &&vec) { assert_initialized(); Operations::Op op; op.type = Operations::OpType::set_statevec; @@ -956,7 +1027,8 @@ void AerState::apply_unitary(const reg_t &qubits, const cmatrix_t &mat) { buffer_op(std::move(op)); } -void AerState::apply_diagonal_matrix(const reg_t &qubits, const cvector_t &mat) { +void AerState::apply_diagonal_matrix(const reg_t &qubits, + const cvector_t &mat) { assert_initialized(); Operations::Op op; op.type = Operations::OpType::diagonal_matrix; @@ -967,7 +1039,9 @@ void AerState::apply_diagonal_matrix(const reg_t &qubits, const cvector_t &mat) buffer_op(std::move(op)); } -void AerState::apply_multiplexer(const reg_t &control_qubits, const reg_t &target_qubits, const std::vector &mats) { +void AerState::apply_multiplexer(const reg_t &control_qubits, + const reg_t &target_qubits, + const std::vector &mats) { assert_initialized(); if (mats.empty()) @@ -1105,7 +1179,8 @@ void AerState::apply_mcz(const reg_t &qubits) { buffer_op(std::move(op)); } -void AerState::apply_mcphase(const reg_t &qubits, const std::complex phase) { +void AerState::apply_mcphase(const reg_t &qubits, + const std::complex phase) { assert_initialized(); Operations::Op op; @@ -1129,7 +1204,8 @@ void AerState::apply_h(const uint_t qubit) { buffer_op(std::move(op)); } -void AerState::apply_u(const uint_t qubit, const double theta, const double phi, const double lambda) { +void AerState::apply_u(const uint_t qubit, const double theta, const double phi, + const double lambda) { assert_initialized(); Operations::Op op; @@ -1141,7 +1217,9 @@ void AerState::apply_u(const uint_t qubit, const double theta, const double phi, buffer_op(std::move(op)); } -void AerState::apply_cu(const reg_t &qubits, const double theta, const double phi, const double lambda, const double gamma) { +void AerState::apply_cu(const reg_t &qubits, const double theta, + const double phi, const double lambda, + const double gamma) { assert_initialized(); Operations::Op op; @@ -1153,7 +1231,9 @@ void AerState::apply_cu(const reg_t &qubits, const double theta, const double ph buffer_op(std::move(op)); } -void AerState::apply_mcu(const reg_t &qubits, const double theta, const double phi, const double lambda, const double gamma) { +void AerState::apply_mcu(const reg_t &qubits, const double theta, + const double phi, const double lambda, + const double gamma) { assert_initialized(); Operations::Op op; @@ -1233,7 +1313,7 @@ uint_t AerState::apply_measure(const reg_t &qubits) { uint_t bitstring = 0; uint_t bit = 1; - for (const auto& qubit: qubits) { + for (const auto &qubit : qubits) { if (state_->creg().creg_memory()[qubit] == '1') bitstring |= bit; bit <<= 1; @@ -1255,7 +1335,8 @@ void AerState::apply_reset(const reg_t &qubits) { state_->apply_op(op, last_result_, rng_); } -void AerState::apply_kraus(const reg_t &qubits, const std::vector &krausops) { +void AerState::apply_kraus(const reg_t &qubits, + const std::vector &krausops) { assert_initialized(); Operations::Op op; @@ -1286,7 +1367,9 @@ double AerState::probability(const uint_t outcome) { last_result_ = ExperimentResult(); state_->apply_op(op, last_result_, rng_); - return ((DataMap)last_result_.data).value()["s"].value()[0][0]; + return ((DataMap)last_result_.data) + .value()["s"] + .value()[0][0]; } // Return the probability amplitude for outcome in [0, 2^num_qubits - 1] @@ -1305,7 +1388,9 @@ complex_t AerState::amplitude(const uint_t outcome) { last_result_ = ExperimentResult(); state_->apply_op(op, last_result_, rng_); - return ((DataMap>)last_result_.data).value()["s"].value()[0][0]; + return ((DataMap>)last_result_.data) + .value()["s"] + .value()[0][0]; }; std::vector AerState::probabilities() { @@ -1322,7 +1407,9 @@ std::vector AerState::probabilities() { last_result_ = ExperimentResult(); state_->apply_op(op, last_result_, rng_); - return ((DataMap)last_result_.data).value()["s"].value()[0]; + return ((DataMap)last_result_.data) + .value()["s"] + .value()[0]; } std::vector AerState::probabilities(const reg_t &qubits) { @@ -1340,10 +1427,13 @@ std::vector AerState::probabilities(const reg_t &qubits) { last_result_ = ExperimentResult(); state_->apply_op(op, last_result_, rng_); - return ((DataMap)last_result_.data).value()["s"].value()[0]; + return ((DataMap)last_result_.data) + .value()["s"] + .value()[0]; } -std::vector AerState::sample_memory(const reg_t &qubits, uint_t shots) { +std::vector AerState::sample_memory(const reg_t &qubits, + uint_t shots) { assert_initialized(); flush_ops(); @@ -1351,24 +1441,27 @@ std::vector AerState::sample_memory(const reg_t &qubits, uint_t sho std::vector ret; ret.reserve(shots); std::vector samples = state_->sample_measure(qubits, shots, rng_); - for (auto& sample : samples) { - ret.push_back(Utils::int2string(Utils::reg2int(sample, 2), 2, qubits.size())); + for (auto &sample : samples) { + ret.push_back( + Utils::int2string(Utils::reg2int(sample, 2), 2, qubits.size())); } return ret; } -std::unordered_map AerState::sample_counts(const reg_t &qubits, uint_t shots) { +std::unordered_map AerState::sample_counts(const reg_t &qubits, + uint_t shots) { assert_initialized(); flush_ops(); std::vector samples = state_->sample_measure(qubits, shots, rng_); std::unordered_map ret; - for(const auto & sample: samples) { + for (const auto &sample : samples) { uint_t sample_u = 0ULL; uint_t mask = 1ULL; - for (const auto b: sample) { - if (b) sample_u |= mask; + for (const auto b : sample) { + if (b) + sample_u |= mask; mask <<= 1; } if (ret.find(sample_u) == ret.end()) @@ -1382,7 +1475,7 @@ std::unordered_map AerState::sample_counts(const reg_t &qubits, //----------------------------------------------------------------------- // Operation management //----------------------------------------------------------------------- -void AerState::buffer_op(const Operations::Op&& op) { +void AerState::buffer_op(const Operations::Op &&op) { assert_initialized(); buffer_.ops.push_back(std::move(op)); }; @@ -1391,7 +1484,8 @@ void AerState::initialize_experiment_result() { last_result_ = ExperimentResult(); last_result_.set_config(configs_); last_result_.metadata.add(method_names_.at(method_), "method"); - if (method_ == Method::statevector || method_ == Method::density_matrix || method_ == Method::unitary) + if (method_ == Method::statevector || method_ == Method::density_matrix || + method_ == Method::unitary) last_result_.metadata.add(device_names_.at(device_), "device"); else last_result_.metadata.add("CPU", "device"); @@ -1404,14 +1498,16 @@ void AerState::initialize_experiment_result() { }; void AerState::finalize_experiment_result(bool success, double time_taken) { - last_result_.status = success? ExperimentResult::Status::completed : ExperimentResult::Status::error; + last_result_.status = success ? ExperimentResult::Status::completed + : ExperimentResult::Status::error; last_result_.time_taken = time_taken; }; void AerState::flush_ops() { assert_initialized(); - if (buffer_.ops.empty()) return; + if (buffer_.ops.empty()) + return; auto timer_start = myclock_t::now(); @@ -1421,7 +1517,9 @@ void AerState::flush_ops() { transpile_ops(); state_->apply_ops(buffer_.ops.begin(), buffer_.ops.end(), last_result_, rng_); - finalize_experiment_result(true, std::chrono::duration(myclock_t::now() - timer_start).count()); + finalize_experiment_result( + true, + std::chrono::duration(myclock_t::now() - timer_start).count()); clear_ops(); }; @@ -1469,12 +1567,17 @@ void AerState::transpile_ops() { } // Override default fusion settings with custom config fusion_pass_.set_config(configs_); - fusion_pass_.optimize_circuit(buffer_, noise_model_, state_->opset(), last_result_); - - //cache blocking - if(cache_block_pass_.enabled() && state_->multi_chunk_distribution_supported()){ - cache_block_pass_.set_restore_qubit_map(true); //restore swapped qubits because buffer_ does not include output qubits - cache_block_pass_.optimize_circuit(buffer_, noise_model_, state_->opset(), last_result_); + fusion_pass_.optimize_circuit(buffer_, noise_model_, state_->opset(), + last_result_); + + // cache blocking + if (cache_block_pass_.enabled() && + state_->multi_chunk_distribution_supported()) { + cache_block_pass_.set_restore_qubit_map( + true); // restore swapped qubits because buffer_ does not include output + // qubits + cache_block_pass_.optimize_circuit(buffer_, noise_model_, state_->opset(), + last_result_); } } diff --git a/src/framework/avx2_detect.hpp b/src/framework/avx2_detect.hpp index 49f5d73955..2287782f38 100644 --- a/src/framework/avx2_detect.hpp +++ b/src/framework/avx2_detect.hpp @@ -16,63 +16,59 @@ #define _aer_controller_avx2_detect_hpp_ #include -#include #include +#include #include "misc/common_macros.hpp" #if defined(_MSC_VER) - #include +#include #elif defined(GNUC_AVX2) - #include +#include #endif - namespace { -inline void ccpuid(int cpu_info[4], int function_id){ +inline void ccpuid(int cpu_info[4], int function_id) { #if defined(_MSC_VER) __cpuid(cpu_info, function_id); #elif defined(GNUC_AVX2) - __cpuid(function_id, - cpu_info[0], - cpu_info[1], - cpu_info[2], - cpu_info[3]); + __cpuid(function_id, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); #else // We don't support this platform intrinsics cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; #endif } -inline void cpuidex(int cpu_info[4], int function_id, int subfunction_id){ +inline void cpuidex(int cpu_info[4], int function_id, int subfunction_id) { #if defined(_MSC_VER) __cpuidex(cpu_info, function_id, subfunction_id); #elif defined(GNUC_AVX2) - __cpuid_count(function_id, subfunction_id, cpu_info[0], cpu_info[1], cpu_info[2], cpu_info[3]); + __cpuid_count(function_id, subfunction_id, cpu_info[0], cpu_info[1], + cpu_info[2], cpu_info[3]); #else // We don't support this platform intrinsics - cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; + cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0; #endif } -} +} // namespace namespace AER { -inline bool is_avx2_supported(){ +inline bool is_avx2_supported() { #if defined(GNUC_AVX2) || defined(_MSC_VER) static bool cached = false; static bool is_supported = false; - if(cached) + if (cached) return is_supported; std::array cpui; ccpuid(cpui.data(), 0); auto num_ids = cpui[0]; - if(num_ids < 7){ + if (num_ids < 7) { cached = true; is_supported = false; return false; } std::vector> data; - for (int i = 0; i <= num_ids; ++i){ + for (int i = 0; i <= num_ids; ++i) { cpuidex(cpui.data(), i, 0); data.push_back(cpui); } @@ -87,11 +83,9 @@ inline bool is_avx2_supported(){ is_supported = is_fma_supported && is_avx2_supported; return is_supported; #else - return false; + return false; #endif } // end namespace AER -} +} // namespace AER #endif - - diff --git a/src/framework/blas_protos.hpp b/src/framework/blas_protos.hpp old mode 100755 new mode 100644 index d2803c66eb..dc17a25ac8 --- a/src/framework/blas_protos.hpp +++ b/src/framework/blas_protos.hpp @@ -19,10 +19,10 @@ #ifndef _aer_framework_blas_protos_hpp #define _aer_framework_blas_protos_hpp +#include #include #include #include -#include #ifdef __cplusplus extern "C" { @@ -79,45 +79,43 @@ void zgemm_(const char *TransA, const char *TransB, const size_t *M, const std::complex *beta, std::complex *C, size_t *ldc); -// Reduces a Single-Precison Complex Hermitian matrix A to real symmetric tridiagonal form -void chetrd_(char *TRANS, int *N, std::complex *A, - int *LDA, float *d, float *e, std::complex *tau, - std::complex *work, int *lwork, int *info); +// Reduces a Single-Precison Complex Hermitian matrix A to real symmetric +// tridiagonal form +void chetrd_(char *TRANS, int *N, std::complex *A, int *LDA, float *d, + float *e, std::complex *tau, std::complex *work, + int *lwork, int *info); -// Reduces a Double-Precison Complex Hermitian matrix A to real symmetric tridiagonal form T -void zhetrd_(char *TRANS, int *N, std::complex *A, - int *LDA, double *d, double *e, std::complex *tau, - std::complex *work, int *lwork, int *info); +// Reduces a Double-Precison Complex Hermitian matrix A to real symmetric +// tridiagonal form T +void zhetrd_(char *TRANS, int *N, std::complex *A, int *LDA, double *d, + double *e, std::complex *tau, std::complex *work, + int *lwork, int *info); // Computes all eigenvalues and, optionally, eigenvectors of a // Single-Precison Complex symmetric positive definite tridiagonal matrix -void cpteqr_(char* compz, int *n, float *d, float *e, - std::complex *z, int* ldz, - std::complex *work, int *info); +void cpteqr_(char *compz, int *n, float *d, float *e, std::complex *z, + int *ldz, std::complex *work, int *info); // Computes all eigenvalues and, optionally, eigenvectors of a // Double-Precison Complex symmetric positive definite tridiagonal matrix -void zpteqr_(char* compz, int *n, double *d, double *e, - std::complex *z, int* ldz, - std::complex *work, int *info); +void zpteqr_(char *compz, int *n, double *d, double *e, std::complex *z, + int *ldz, std::complex *work, int *info); // Computes selected eigenvalues and, optionally, eigenvectors // of a Single-Precison Complex Hermitian matrix A void cheevx_(char *jobz, char *range, char *uplo, int *n, - std::complex *a, int *lda, float *vl, - float *vu, int *il, int *iu, float *abstol, - int *m, float *w, std::complex *z, int *ldz, - std::complex *work, int *lwork, float *rwork, + std::complex *a, int *lda, float *vl, float *vu, int *il, + int *iu, float *abstol, int *m, float *w, std::complex *z, + int *ldz, std::complex *work, int *lwork, float *rwork, int *iwork, int *ifail, int *info); // Computes selected eigenvalues and, optionally, eigenvectors // of a Double-Precison Complex Hermitian matrix A void zheevx_(char *jobz, char *range, char *uplo, int *n, - std::complex *a, int *lda, double *vl, - double *vu, int *il, int *iu, double *abstol, - int *m, double *w, std::complex *z, int *ldz, - std::complex *work, int *lwork, double *rwork, - int *iwork, int *ifail, int *info); + std::complex *a, int *lda, double *vl, double *vu, int *il, + int *iu, double *abstol, int *m, double *w, + std::complex *z, int *ldz, std::complex *work, + int *lwork, double *rwork, int *iwork, int *ifail, int *info); // Determines Single-Precision machine parameters. float slamch_(char *cmach); diff --git a/src/framework/circuit.hpp b/src/framework/circuit.hpp old mode 100755 new mode 100644 index b5471e4e43..50045996db --- a/src/framework/circuit.hpp +++ b/src/framework/circuit.hpp @@ -17,9 +17,9 @@ #include +#include "framework/config.hpp" #include "framework/operations.hpp" #include "framework/opset.hpp" -#include "framework/config.hpp" using complex_t = std::complex; @@ -40,59 +40,62 @@ class Circuit { std::vector ops; // Circuit parameters updated by from ops by set_params - uint_t num_qubits = 0; // maximum number of qubits needed for ops - uint_t num_memory = 0; // maximum number of memory clbits needed for ops - uint_t num_registers = 0; // maximum number of registers clbits needed for ops - + uint_t num_qubits = 0; // maximum number of qubits needed for ops + uint_t num_memory = 0; // maximum number of memory clbits needed for ops + uint_t num_registers = 0; // maximum number of registers clbits needed for ops + // Measurement params - bool has_conditional = false; // True if any ops are conditional - bool can_sample = true; // True if circuit tail contains measure, roerror, barrier. - size_t first_measure_pos = 0; // Position of first measure instruction - bool can_sample_initialize = true; // True if circuit contains at most 1 initialize - // and it is the first instruction in the circuit + bool has_conditional = false; // True if any ops are conditional + bool can_sample = + true; // True if circuit tail contains measure, roerror, barrier. + size_t first_measure_pos = 0; // Position of first measure instruction + bool can_sample_initialize = + true; // True if circuit contains at most 1 initialize + // and it is the first instruction in the circuit // Circuit metadata constructed from json QobjExperiment uint_t shots = 1; uint_t seed; json_t header; double global_phase_angle = 0; - bool remapped_qubits = false; // True if qubits have been remapped + bool remapped_qubits = false; // True if qubits have been remapped // Constructor - // The constructor automatically calculates the num_qubits, num_memory, num_registers - // parameters by scanning the input list of ops. - Circuit() {set_random_seed();} + // The constructor automatically calculates the num_qubits, num_memory, + // num_registers parameters by scanning the input list of ops. + Circuit() { set_random_seed(); } Circuit(const std::vector &_ops, bool truncation = false); Circuit(std::vector &&_ops, bool truncation = false); // Construct a circuit from JSON - template - Circuit(const inputdata_t& circ, bool truncation = false); + template + Circuit(const inputdata_t &circ, bool truncation = false); - template - Circuit(const inputdata_t& circ, const json_t& qobj_config, bool truncation = false); + template + Circuit(const inputdata_t &circ, const json_t &qobj_config, + bool truncation = false); //----------------------------------------------------------------------- // Set containers //----------------------------------------------------------------------- // Return the opset for the circuit - inline const auto& opset() const {return opset_;} + inline const auto &opset() const { return opset_; } // Return the used qubits for the circuit - inline const auto& qubits() const {return qubitset_;} + inline const auto &qubits() const { return qubitset_; } // Return the used memory for the circuit - inline const auto& memory() const {return memoryset_;} + inline const auto &memory() const { return memoryset_; } // Return the used registers for the circuit - inline const auto& registers() const {return registerset_;} + inline const auto ®isters() const { return registerset_; } // Return the mapping of input op qubits to circuit qubits - inline const auto& qubit_map() const {return qubitmap_;} + inline const auto &qubit_map() const { return qubitmap_; } //----------------------------------------------------------------------- - // Utility methods + // Utility methods //----------------------------------------------------------------------- // Automatically set the number of qubits, memory, registers, and check @@ -110,35 +113,33 @@ class Circuit { void set_metadata(const AER::Config &config, bool truncation); // Set the circuit rng seed to random value - inline void set_random_seed() {seed = std::random_device()();} + inline void set_random_seed() { seed = std::random_device()(); } //----------------------------------------------------------------------- // Op insert helpers //----------------------------------------------------------------------- - void bfunc(const std::string &mask, const std::string &val, const std::string &relation, const uint_t regidx) { + void bfunc(const std::string &mask, const std::string &val, + const std::string &relation, const uint_t regidx) { ops.push_back(Operations::make_bfunc(mask, val, relation, regidx)); } - void gate(const std::string &name, - const reg_t &qubits, + void gate(const std::string &name, const reg_t &qubits, const std::vector ¶ms, const std::vector &string_params, - const int_t cond_regidx=-1, - const std::string label="") { - ops.push_back(Operations::make_gate(name, qubits, params, string_params, cond_regidx, label)); + const int_t cond_regidx = -1, const std::string label = "") { + ops.push_back(Operations::make_gate(name, qubits, params, string_params, + cond_regidx, label)); + check_gate_params(ops.back()); } - void diagonal(const reg_t &qubits, - const cvector_t &vec, + void diagonal(const reg_t &qubits, const cvector_t &vec, const std::string &label) { ops.push_back(Operations::make_diagonal(qubits, vec, label)); } - void unitary(const reg_t &qubits, - const cmatrix_t &mat, - const int_t cond_regidx=-1, - const std::string label="") { + void unitary(const reg_t &qubits, const cmatrix_t &mat, + const int_t cond_regidx = -1, const std::string label = "") { ops.push_back(Operations::make_unitary(qubits, mat, cond_regidx, label)); } @@ -152,87 +153,88 @@ class Circuit { ops.push_back(Operations::make_roerror(memory, probabilities)); } - void multiplexer(const reg_t &qubits, - const std::vector &mats, - const int_t cond_regidx = -1, - std::string label="") { - ops.push_back(Operations::make_multiplexer(qubits, mats, cond_regidx, label)); + void multiplexer(const reg_t &qubits, const std::vector &mats, + const int_t cond_regidx = -1, std::string label = "") { + ops.push_back( + Operations::make_multiplexer(qubits, mats, cond_regidx, label)); } - void kraus(const reg_t &qubits, - const std::vector &mats, + void kraus(const reg_t &qubits, const std::vector &mats, const int_t cond_regidx = -1) { ops.push_back(Operations::make_kraus(qubits, mats, cond_regidx)); } - void superop(const reg_t &qubits, - const cmatrix_t &mat, + void superop(const reg_t &qubits, const cmatrix_t &mat, const int_t cond_regidx = -1) { ops.push_back(Operations::make_superop(qubits, mat, cond_regidx)); } - void save_state(const reg_t &qubits, - const std::string &name, + void save_state(const reg_t &qubits, const std::string &name, const std::string &snapshot_type, - const std::string &label="") { - ops.push_back(Operations::make_save_state(qubits, name, snapshot_type, label)); + const std::string &label = "") { + ops.push_back( + Operations::make_save_state(qubits, name, snapshot_type, label)); } - void save_amplitudes(const reg_t &qubits, - const std::string &name, + void save_amplitudes(const reg_t &qubits, const std::string &name, const std::vector &basis_state, const std::string &snapshot_type, - const std::string &label="") { - ops.push_back(Operations::make_save_amplitudes(qubits, name, basis_state, snapshot_type, label)); + const std::string &label = "") { + ops.push_back(Operations::make_save_amplitudes(qubits, name, basis_state, + snapshot_type, label)); } - void save_expval(const reg_t &qubits, - const std::string &name, + void save_expval(const reg_t &qubits, const std::string &name, const std::vector pauli_strings, const std::vector coeff_reals, const std::vector coeff_imags, const std::string &snapshot_type, - const std::string label="") { - ops.push_back(Operations::make_save_expval(qubits, name, pauli_strings, coeff_reals, coeff_imags, snapshot_type, label)); + const std::string label = "") { + ops.push_back(Operations::make_save_expval(qubits, name, pauli_strings, + coeff_reals, coeff_imags, + snapshot_type, label)); } - void set_qerror_loc(const reg_t &qubits, - const std::string &label, + void set_qerror_loc(const reg_t &qubits, const std::string &label, const int_t conditional = -1) { ops.push_back(Operations::make_qerror_loc(qubits, label, conditional)); } - template + template void set_statevector(const reg_t &qubits, const inputdata_t ¶m) { - ops.push_back(Operations::make_set_vector(qubits, "set_statevector", param)); + ops.push_back( + Operations::make_set_vector(qubits, "set_statevector", param)); } - template + template void set_density_matrix(const reg_t &qubits, const inputdata_t ¶m) { - ops.push_back(Operations::make_set_matrix(qubits, "set_density_matrix", param)); + ops.push_back( + Operations::make_set_matrix(qubits, "set_density_matrix", param)); } - template + template void set_unitary(const reg_t &qubits, const inputdata_t ¶m) { ops.push_back(Operations::make_set_matrix(qubits, "set_unitary", param)); } - template + template void set_superop(const reg_t &qubits, const inputdata_t ¶m) { ops.push_back(Operations::make_set_matrix(qubits, "set_superop", param)); } - template + template void set_matrix_product_state(const reg_t &qubits, const inputdata_t ¶m) { - ops.push_back(Operations::make_set_mps(qubits, "set_matrix_product_state", param)); + ops.push_back( + Operations::make_set_mps(qubits, "set_matrix_product_state", param)); } - template + template void set_clifford(const reg_t &qubits, const inputdata_t ¶m) { ops.push_back(Operations::make_set_clifford(qubits, "set_clifford", param)); } - void jump(const reg_t &qubits, const std::vector ¶ms, const int_t cond_regidx = -1) { + void jump(const reg_t &qubits, const std::vector ¶ms, + const int_t cond_regidx = -1) { ops.push_back(Operations::make_jump(qubits, params, cond_regidx)); } @@ -240,7 +242,12 @@ class Circuit { ops.push_back(Operations::make_mark(qubits, params)); } - void measure(const reg_t &qubits, const reg_t &memory, const reg_t ®isters) { + void barrier(const reg_t &qubits) { + ops.push_back(Operations::make_barrier(qubits)); + } + + void measure(const reg_t &qubits, const reg_t &memory, + const reg_t ®isters) { ops.push_back(Operations::make_measure(qubits, memory, registers)); } @@ -259,23 +266,21 @@ class Circuit { std::unordered_map qubitmap_; // Add type, qubit, memory, conditional metadata information from op - void add_op_metadata(const Op& op); + void add_op_metadata(const Op &op); // Reset circuit metadata void reset_metadata(); // Helper function for optimized set params - bool check_result_ancestor(const Op& op, - std::unordered_set& ancestor_qubits) const; - + bool check_result_ancestor(const Op &op, + std::unordered_set &ancestor_qubits) const; + // Helper function for optimized set params - void remap_qubits(Op& op) const; + void remap_qubits(Op &op) const; }; - // Json conversion function -inline void from_json(const json_t& js, Circuit &circ) {circ = Circuit(js);} - +inline void from_json(const json_t &js, Circuit &circ) { circ = Circuit(js); } //============================================================================ // Implementation: Circuit methods @@ -291,11 +296,14 @@ Circuit::Circuit(std::vector &&_ops, bool truncation) : Circuit() { set_params(truncation); } -template -Circuit::Circuit(const inputdata_t &circ, bool truncation) : Circuit(circ, json_t(), truncation) {} +template +Circuit::Circuit(const inputdata_t &circ, bool truncation) + : Circuit(circ, json_t(), truncation) {} -template -Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool truncation) : Circuit() { +template +Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, + bool truncation) + : Circuit() { // Get config auto config = qobj_config; if (Parser::check_key("config", circ)) { @@ -305,23 +313,24 @@ Circuit::Circuit(const inputdata_t &circ, const json_t &qobj_config, bool trunca config[it.key()] = it.value(); // overwrite circuit level config values } } - + // Set header Parser::get_value(header, "header", circ); Parser::get_value(global_phase_angle, "global_phase", header); - + // Load instructions if (Parser::check_key("instructions", circ) == false) { - throw std::invalid_argument("Invalid Qobj experiment: no \"instructions\" field."); + throw std::invalid_argument( + "Invalid Qobj experiment: no \"instructions\" field."); } const auto input_ops = Parser::get_list("instructions", circ); - + // Convert to Ops - // TODO: If parser could support reverse iteration through the list of ops without - // conversion we could call `get_reversed_ops` on the inputdata without first - // converting. + // TODO: If parser could support reverse iteration through the list of ops + // without conversion we could call `get_reversed_ops` on the inputdata + // without first converting. std::vector converted_ops; - for(auto the_op: input_ops){ + for (auto the_op : input_ops) { converted_ops.emplace_back(Operations::input_to_op(the_op)); } ops = std::move(converted_ops); @@ -337,7 +346,8 @@ void Circuit::set_metadata(const AER::Config &config, bool truncation) { // Check for specified memory slots uint_t memory_slots = config.memory_slots; if (memory_slots < num_memory) { - throw std::invalid_argument("Invalid Qobj experiment: not enough memory slots."); + throw std::invalid_argument( + "Invalid Qobj experiment: not enough memory slots."); } // override memory slot number num_memory = memory_slots; @@ -346,7 +356,8 @@ void Circuit::set_metadata(const AER::Config &config, bool truncation) { if (config.n_qubits.has_value()) { uint_t n_qubits = config.n_qubits.value(); if (n_qubits < num_qubits) { - throw std::invalid_argument("Invalid Qobj experiment: n_qubits < instruction qubits."); + throw std::invalid_argument( + "Invalid Qobj experiment: n_qubits < instruction qubits."); } if (!truncation) { // Override minimal circuit qubit number with qobj number if truncation @@ -372,14 +383,14 @@ void Circuit::reset_metadata() { num_qubits = 0; num_memory = 0; num_registers = 0; - + has_conditional = false; can_sample = true; first_measure_pos = 0; can_sample_initialize = true; } -void Circuit::add_op_metadata(const Op& op) { +void Circuit::add_op_metadata(const Op &op) { has_conditional |= op.conditional; opset_.insert(op); qubitset_.insert(op.qubits.begin(), op.qubits.end()); @@ -396,11 +407,11 @@ void Circuit::add_op_metadata(const Op& op) { } } - void Circuit::set_params(bool truncation) { - // Clear current circuit metadata + // Clear current circuit metadata reset_metadata(); - if (ops.empty()) return; + if (ops.empty()) + return; // Analyze input ops from tail to head to get locations of ancestor, // first measurement position and last initialize position @@ -414,11 +425,11 @@ void Circuit::set_params(bool truncation) { bool ops_to_remove = false; std::unordered_set ancestor_qubits; - for (size_t i = 0; i < size; ++ i) { + for (size_t i = 0; i < size; ++i) { const size_t rpos = size - i - 1; - const auto& op = ops[rpos]; + const auto &op = ops[rpos]; if (op.type == OpType::mark && last_ancestor_pos == 0) - last_ancestor_pos = rpos; + last_ancestor_pos = rpos; if (!truncation || check_result_ancestor(op, ancestor_qubits)) { add_op_metadata(op); ancestor[rpos] = true; @@ -432,7 +443,7 @@ void Circuit::set_params(bool truncation) { if (last_ancestor_pos == 0) { last_ancestor_pos = rpos; } - } else if (truncation && !ops_to_remove){ + } else if (truncation && !ops_to_remove) { ops_to_remove = true; } } @@ -442,7 +453,7 @@ void Circuit::set_params(bool truncation) { if (truncation) { // Generate mapping of original qubits to ancestor set uint_t idx = 0; - for (const auto& qubit: qubitset_) { + for (const auto &qubit : qubitset_) { if (!remapped_qubits && idx != qubit) { // qubits will be remapped remapped_qubits = true; @@ -481,47 +492,46 @@ void Circuit::set_params(bool truncation) { continue; } - const auto& op = ops[pos]; + const auto &op = ops[pos]; if (op.conditional) { can_sample = false; break; } switch (op.type) { - case OpType::measure: - case OpType::roerror: { - meas_qubits.insert(op.qubits.begin(), op.qubits.end()); - tail_meas_ops.push_back(op); - break; - } - case OpType::save_state: - case OpType::save_expval: - case OpType::save_expval_var: - case OpType::save_statevec: - case OpType::save_statevec_dict: - case OpType::save_densmat: - case OpType::save_probs: - case OpType::save_probs_ket: - case OpType::save_amps: - case OpType::save_amps_sq: - case OpType::save_stabilizer: - case OpType::save_clifford: - case OpType::save_unitary: - case OpType::save_mps: - case OpType::save_superop: - { - can_sample = false; - break; - } - default: { - for (const auto &qubit : op.qubits) { - if (meas_qubits.find(qubit) != meas_qubits.end()) { - can_sample = false; - break; - } + case OpType::measure: + case OpType::roerror: { + meas_qubits.insert(op.qubits.begin(), op.qubits.end()); + tail_meas_ops.push_back(op); + break; + } + case OpType::save_state: + case OpType::save_expval: + case OpType::save_expval_var: + case OpType::save_statevec: + case OpType::save_statevec_dict: + case OpType::save_densmat: + case OpType::save_probs: + case OpType::save_probs_ket: + case OpType::save_amps: + case OpType::save_amps_sq: + case OpType::save_stabilizer: + case OpType::save_clifford: + case OpType::save_unitary: + case OpType::save_mps: + case OpType::save_superop: { + can_sample = false; + break; + } + default: { + for (const auto &qubit : op.qubits) { + if (meas_qubits.find(qubit) != meas_qubits.end()) { + can_sample = false; + break; } - tail_pos.push_back(pos); } + tail_pos.push_back(pos); + } } if (!can_sample) { break; @@ -540,7 +550,8 @@ void Circuit::set_params(bool truncation) { head_end = last_ancestor_pos + 1; } for (size_t pos = 0; pos < head_end; ++pos) { - if (ops_to_remove && !ancestor[pos] && ops[pos].type != OpType::mark && ops[pos].type != OpType::jump) { + if (ops_to_remove && !ancestor[pos] && ops[pos].type != OpType::mark && + ops[pos].type != OpType::jump) { // Skip if not ancestor continue; } @@ -553,10 +564,11 @@ void Circuit::set_params(bool truncation) { if (ops[op_idx].type == OpType::jump) { dests.insert(ops[op_idx].string_params[0]); } else if (ops[op_idx].type == OpType::mark) { - auto& mark_name = ops[op_idx].string_params[0]; + auto &mark_name = ops[op_idx].string_params[0]; if (marks.find(mark_name) != marks.end()) { std::stringstream msg; - msg << "Duplicated mark destination:\"" << mark_name << "\"." << std::endl; + msg << "Duplicated mark destination:\"" << mark_name << "\"." + << std::endl; throw std::runtime_error(msg.str()); } marks.insert(mark_name); @@ -582,7 +594,7 @@ void Circuit::set_params(bool truncation) { if (!ops_to_remove && !ancestor[tpos]) { continue; } - auto& op = ops[tpos]; + auto &op = ops[tpos]; if (remapped_qubits) { remap_qubits(ops[tpos]); } @@ -593,7 +605,7 @@ void Circuit::set_params(bool truncation) { } // Now add remaining delayed measure ops first_measure_pos = op_idx; - for (auto & op : tail_meas_ops) { + for (auto &op : tail_meas_ops) { if (remapped_qubits) { remap_qubits(op); } @@ -608,55 +620,54 @@ void Circuit::set_params(bool truncation) { ops.resize(op_idx); } - -void Circuit::remap_qubits(Op& op) const { +void Circuit::remap_qubits(Op &op) const { reg_t new_qubits; - for (auto& qubit : op.qubits) { + for (auto &qubit : op.qubits) { new_qubits.push_back(qubitmap_.at(qubit)); } op.qubits = std::move(new_qubits); } - -bool Circuit::check_result_ancestor(const Op& op, std::unordered_set& ancestor_qubits) const { +bool Circuit::check_result_ancestor( + const Op &op, std::unordered_set &ancestor_qubits) const { switch (op.type) { - case OpType::barrier: - case OpType::nop: { - return false; - } - case OpType::bfunc: { - return true; - } - // Result generating types - case OpType::measure: - case OpType::roerror: - case OpType::save_state: - case OpType::save_expval: - case OpType::save_expval_var: - case OpType::save_statevec: - case OpType::save_statevec_dict: - case OpType::save_densmat: - case OpType::save_probs: - case OpType::save_probs_ket: - case OpType::save_amps: - case OpType::save_amps_sq: - case OpType::save_stabilizer: - case OpType::save_clifford: - case OpType::save_unitary: - case OpType::save_mps: - case OpType::save_superop: { - ancestor_qubits.insert(op.qubits.begin(), op.qubits.end()); - return true; - } - default: { - for (const auto& qubit : op.qubits) { - if (ancestor_qubits.find(qubit) != ancestor_qubits.end()) { - ancestor_qubits.insert(op.qubits.begin(), op.qubits.end()); - return true; - } + case OpType::barrier: + case OpType::nop: { + return false; + } + case OpType::bfunc: { + return true; + } + // Result generating types + case OpType::measure: + case OpType::roerror: + case OpType::save_state: + case OpType::save_expval: + case OpType::save_expval_var: + case OpType::save_statevec: + case OpType::save_statevec_dict: + case OpType::save_densmat: + case OpType::save_probs: + case OpType::save_probs_ket: + case OpType::save_amps: + case OpType::save_amps_sq: + case OpType::save_stabilizer: + case OpType::save_clifford: + case OpType::save_unitary: + case OpType::save_mps: + case OpType::save_superop: { + ancestor_qubits.insert(op.qubits.begin(), op.qubits.end()); + return true; + } + default: { + for (const auto &qubit : op.qubits) { + if (ancestor_qubits.find(qubit) != ancestor_qubits.end()) { + ancestor_qubits.insert(op.qubits.begin(), op.qubits.end()); + return true; } - return false; } + return false; + } } } diff --git a/src/framework/config.hpp b/src/framework/config.hpp index 59b0c76004..56a8015a0b 100644 --- a/src/framework/config.hpp +++ b/src/framework/config.hpp @@ -15,9 +15,10 @@ #ifndef _aer_framework_config_hpp_ #define _aer_framework_config_hpp_ -#include -#include +#include "json.hpp" #include "types.hpp" +#include +#include namespace AER { @@ -33,26 +34,22 @@ struct optional { return val; } - void value(const T& input) { + void value(const T &input) { exist = true; val = input; } - void clear() { - exist = false; - } + void clear() { exist = false; } - // operator bool() const { + // operator bool() const { // return exist; // } - bool has_value() const { - return exist; - } + bool has_value() const { return exist; } }; template -bool get_value(optional &var, const std::string& key, const json_t &js) { +bool get_value(optional &var, const std::string &key, const json_t &js) { if (JSON::check_key(key, js)) { var.value(js[key].get()); return true; @@ -63,7 +60,7 @@ bool get_value(optional &var, const std::string& key, const json_t &js) { } template -bool get_value(T &var, const std::string& key, const json_t &js) { +bool get_value(T &var, const std::string &key, const json_t &js) { return JSON::get_value(var, key, js); } @@ -74,7 +71,7 @@ struct Config { uint_t shots = 1024; std::string method = "automatic"; std::string device = "CPU"; - std::string precision ="double"; + std::string precision = "double"; // executor=None, // max_job_size=None, // max_shot_size=None, @@ -85,8 +82,8 @@ struct Config { optional max_parallel_experiments; optional max_parallel_shots; optional max_memory_mb; - bool fusion_enable=true; - bool fusion_verbose=false; + bool fusion_enable = true; + bool fusion_verbose = false; optional fusion_max_qubit; optional fusion_threshold; optional accept_distributed_results; @@ -131,7 +128,9 @@ struct Config { // system configurations std::string library_dir = ""; - using pos_t = std::pair; + const static int_t GLOBAL_PHASE_POS = + -1; // special param position for global phase + using pos_t = std::pair; using exp_params_t = std::vector>>; std::vector param_table; optional n_qubits; @@ -173,7 +172,7 @@ struct Config { shots = 1024; method = "automatic"; device = "CPU"; - precision ="double"; + precision = "double"; // executor=None, // max_job_size=None, // max_shot_size=None, @@ -184,8 +183,8 @@ struct Config { max_parallel_experiments.clear(); max_parallel_shots.clear(); max_memory_mb.clear(); - fusion_enable=true; - fusion_verbose=false; + fusion_enable = true; + fusion_verbose = false; fusion_max_qubit.clear(); fusion_threshold.clear(); accept_distributed_results.clear(); @@ -266,7 +265,7 @@ struct Config { extended_stabilizer_norm_estimation_default_samples.clear(); } - void merge(const Config& other) { + void merge(const Config &other) { shots = other.shots; method = other.method; device = other.device; @@ -277,44 +276,69 @@ struct Config { enable_truncation = other.enable_truncation; zero_threshold = other.zero_threshold; validation_threshold = other.validation_threshold; - if (other.max_parallel_threads.has_value()) max_parallel_threads.value(other.max_parallel_threads.value()); - if (other.max_parallel_experiments.has_value()) max_parallel_experiments.value(other.max_parallel_experiments.value()); - if (other.max_parallel_shots.has_value()) max_parallel_shots.value(other.max_parallel_shots.value()); - if (other.max_memory_mb.has_value()) max_memory_mb.value(other.max_memory_mb.value()); + if (other.max_parallel_threads.has_value()) + max_parallel_threads.value(other.max_parallel_threads.value()); + if (other.max_parallel_experiments.has_value()) + max_parallel_experiments.value(other.max_parallel_experiments.value()); + if (other.max_parallel_shots.has_value()) + max_parallel_shots.value(other.max_parallel_shots.value()); + if (other.max_memory_mb.has_value()) + max_memory_mb.value(other.max_memory_mb.value()); fusion_enable = other.fusion_enable; fusion_verbose = other.fusion_verbose; - if (other.fusion_max_qubit.has_value()) fusion_max_qubit.value(other.fusion_max_qubit.value()); - if (other.fusion_threshold.has_value()) fusion_threshold.value(other.fusion_threshold.value()); - if (other.accept_distributed_results.has_value()) accept_distributed_results.value(other.accept_distributed_results.value()); - if (other.memory.has_value()) memory.value(other.memory.value()); + if (other.fusion_max_qubit.has_value()) + fusion_max_qubit.value(other.fusion_max_qubit.value()); + if (other.fusion_threshold.has_value()) + fusion_threshold.value(other.fusion_threshold.value()); + if (other.accept_distributed_results.has_value()) + accept_distributed_results.value( + other.accept_distributed_results.value()); + if (other.memory.has_value()) + memory.value(other.memory.value()); // noise_model=None, - if (other.seed_simulator.has_value()) seed_simulator.value(other.seed_simulator.value()); + if (other.seed_simulator.has_value()) + seed_simulator.value(other.seed_simulator.value()); // # cuStateVec (cuQuantum) option - if (other.cuStateVec_enable.has_value()) cuStateVec_enable.value(other.cuStateVec_enable.value()); + if (other.cuStateVec_enable.has_value()) + cuStateVec_enable.value(other.cuStateVec_enable.value()); // # cache blocking for multi-GPUs/MPI options - if (other.blocking_qubits.has_value()) blocking_qubits.value(other.blocking_qubits.value()); + if (other.blocking_qubits.has_value()) + blocking_qubits.value(other.blocking_qubits.value()); blocking_enable = other.blocking_enable; - if (other.chunk_swap_buffer_qubits.has_value()) chunk_swap_buffer_qubits.value(other.chunk_swap_buffer_qubits.value()); + if (other.chunk_swap_buffer_qubits.has_value()) + chunk_swap_buffer_qubits.value(other.chunk_swap_buffer_qubits.value()); // # multi-shots optimization options (GPU only) batched_shots_gpu = other.batched_shots_gpu; batched_shots_gpu_max_qubits = other.batched_shots_gpu_max_qubits; - if (other.num_threads_per_device.has_value()) num_threads_per_device.value(other.num_threads_per_device.value()); + if (other.num_threads_per_device.has_value()) + num_threads_per_device.value(other.num_threads_per_device.value()); // # statevector options statevector_parallel_threshold = other.statevector_parallel_threshold; statevector_sample_measure_opt = other.statevector_sample_measure_opt; // # stabilizer options - stabilizer_max_snapshot_probabilities = other.stabilizer_max_snapshot_probabilities; + stabilizer_max_snapshot_probabilities = + other.stabilizer_max_snapshot_probabilities; // # extended stabilizer options - extended_stabilizer_sampling_method = other.extended_stabilizer_sampling_method; - extended_stabilizer_metropolis_mixing_time = other.extended_stabilizer_metropolis_mixing_time; - extended_stabilizer_approximation_error = other.extended_stabilizer_approximation_error; - extended_stabilizer_norm_estimation_samples = other.extended_stabilizer_norm_estimation_samples; - extended_stabilizer_norm_estimation_repetitions = other.extended_stabilizer_norm_estimation_repetitions; - extended_stabilizer_parallel_threshold = other.extended_stabilizer_parallel_threshold; - extended_stabilizer_probabilities_snapshot_samples = other.extended_stabilizer_probabilities_snapshot_samples; + extended_stabilizer_sampling_method = + other.extended_stabilizer_sampling_method; + extended_stabilizer_metropolis_mixing_time = + other.extended_stabilizer_metropolis_mixing_time; + extended_stabilizer_approximation_error = + other.extended_stabilizer_approximation_error; + extended_stabilizer_norm_estimation_samples = + other.extended_stabilizer_norm_estimation_samples; + extended_stabilizer_norm_estimation_repetitions = + other.extended_stabilizer_norm_estimation_repetitions; + extended_stabilizer_parallel_threshold = + other.extended_stabilizer_parallel_threshold; + extended_stabilizer_probabilities_snapshot_samples = + other.extended_stabilizer_probabilities_snapshot_samples; // # MPS options - matrix_product_state_truncation_threshold = other.matrix_product_state_truncation_threshold; - if (other.matrix_product_state_max_bond_dimension.has_value()) matrix_product_state_max_bond_dimension.value(other.matrix_product_state_max_bond_dimension.value()); + matrix_product_state_truncation_threshold = + other.matrix_product_state_truncation_threshold; + if (other.matrix_product_state_max_bond_dimension.has_value()) + matrix_product_state_max_bond_dimension.value( + other.matrix_product_state_max_bond_dimension.value()); mps_sample_measure_algorithm = other.mps_sample_measure_algorithm; mps_log_data = other.mps_log_data; mps_swap_direction = other.mps_swap_direction; @@ -322,40 +346,66 @@ struct Config { mps_parallel_threshold = other.mps_parallel_threshold; mps_omp_threads = other.mps_omp_threads; // # tensor network options - tensor_network_num_sampling_qubits = other.tensor_network_num_sampling_qubits; + tensor_network_num_sampling_qubits = + other.tensor_network_num_sampling_qubits; use_cuTensorNet_autotuning = other.use_cuTensorNet_autotuning; // system configurations param_table = other.param_table; library_dir = other.library_dir; - if (other.n_qubits.has_value()) n_qubits.value(other.n_qubits.value()); + if (other.n_qubits.has_value()) + n_qubits.value(other.n_qubits.value()); global_phase = other.global_phase; memory_slots = other.memory_slots; - if (other._parallel_experiments.has_value()) _parallel_experiments.value(other._parallel_experiments.value()); - if (other._parallel_shots.has_value()) _parallel_shots.value(other._parallel_shots.value()); - if (other._parallel_state_update.has_value()) _parallel_state_update.value(other._parallel_state_update.value()); - if (other._parallel_experiments.has_value()) _parallel_experiments.value(other._parallel_experiments.value()); - if (other.fusion_allow_kraus.has_value()) fusion_allow_kraus.value(other.fusion_allow_kraus.value()); - if (other.fusion_allow_superop.has_value()) fusion_allow_superop.value(other.fusion_allow_superop.value()); - if (other.fusion_parallelization_threshold.has_value()) fusion_parallelization_threshold.value(other.fusion_parallelization_threshold.value()); - if (other._fusion_enable_n_qubits.has_value()) _fusion_enable_n_qubits.value(other._fusion_enable_n_qubits.value()); - if (other._fusion_enable_n_qubits_1.has_value()) _fusion_enable_n_qubits_1.value(other._fusion_enable_n_qubits_1.value()); - if (other._fusion_enable_n_qubits_2.has_value()) _fusion_enable_n_qubits_2.value(other._fusion_enable_n_qubits_2.value()); - if (other._fusion_enable_n_qubits_3.has_value()) _fusion_enable_n_qubits_3.value(other._fusion_enable_n_qubits_3.value()); - if (other._fusion_enable_n_qubits_4.has_value()) _fusion_enable_n_qubits_4.value(other._fusion_enable_n_qubits_4.value()); - if (other._fusion_enable_n_qubits_5.has_value()) _fusion_enable_n_qubits_5.value(other._fusion_enable_n_qubits_5.value()); - if (other._fusion_enable_diagonal.has_value()) _fusion_enable_diagonal.value(other._fusion_enable_diagonal.value()); - if (other._fusion_min_qubit.has_value()) _fusion_min_qubit.value(other._fusion_min_qubit.value()); - if (other.fusion_cost_factor.has_value()) fusion_cost_factor.value(other.fusion_cost_factor.value()); + if (other._parallel_experiments.has_value()) + _parallel_experiments.value(other._parallel_experiments.value()); + if (other._parallel_shots.has_value()) + _parallel_shots.value(other._parallel_shots.value()); + if (other._parallel_state_update.has_value()) + _parallel_state_update.value(other._parallel_state_update.value()); + if (other._parallel_experiments.has_value()) + _parallel_experiments.value(other._parallel_experiments.value()); + if (other.fusion_allow_kraus.has_value()) + fusion_allow_kraus.value(other.fusion_allow_kraus.value()); + if (other.fusion_allow_superop.has_value()) + fusion_allow_superop.value(other.fusion_allow_superop.value()); + if (other.fusion_parallelization_threshold.has_value()) + fusion_parallelization_threshold.value( + other.fusion_parallelization_threshold.value()); + if (other._fusion_enable_n_qubits.has_value()) + _fusion_enable_n_qubits.value(other._fusion_enable_n_qubits.value()); + if (other._fusion_enable_n_qubits_1.has_value()) + _fusion_enable_n_qubits_1.value(other._fusion_enable_n_qubits_1.value()); + if (other._fusion_enable_n_qubits_2.has_value()) + _fusion_enable_n_qubits_2.value(other._fusion_enable_n_qubits_2.value()); + if (other._fusion_enable_n_qubits_3.has_value()) + _fusion_enable_n_qubits_3.value(other._fusion_enable_n_qubits_3.value()); + if (other._fusion_enable_n_qubits_4.has_value()) + _fusion_enable_n_qubits_4.value(other._fusion_enable_n_qubits_4.value()); + if (other._fusion_enable_n_qubits_5.has_value()) + _fusion_enable_n_qubits_5.value(other._fusion_enable_n_qubits_5.value()); + if (other._fusion_enable_diagonal.has_value()) + _fusion_enable_diagonal.value(other._fusion_enable_diagonal.value()); + if (other._fusion_min_qubit.has_value()) + _fusion_min_qubit.value(other._fusion_min_qubit.value()); + if (other.fusion_cost_factor.has_value()) + fusion_cost_factor.value(other.fusion_cost_factor.value()); - if (other.superoperator_parallel_threshold.has_value()) superoperator_parallel_threshold.value(other.superoperator_parallel_threshold.value()); - if (other.unitary_parallel_threshold.has_value()) unitary_parallel_threshold.value(other.unitary_parallel_threshold.value()); - if (other.memory_blocking_bits.has_value()) memory_blocking_bits.value(other.memory_blocking_bits.value()); - if (other.extended_stabilizer_norm_estimation_default_samples.has_value()) extended_stabilizer_norm_estimation_default_samples.value(other.extended_stabilizer_norm_estimation_default_samples.value()); + if (other.superoperator_parallel_threshold.has_value()) + superoperator_parallel_threshold.value( + other.superoperator_parallel_threshold.value()); + if (other.unitary_parallel_threshold.has_value()) + unitary_parallel_threshold.value( + other.unitary_parallel_threshold.value()); + if (other.memory_blocking_bits.has_value()) + memory_blocking_bits.value(other.memory_blocking_bits.value()); + if (other.extended_stabilizer_norm_estimation_default_samples.has_value()) + extended_stabilizer_norm_estimation_default_samples.value( + other.extended_stabilizer_norm_estimation_default_samples.value()); } }; // Json conversion function -inline void from_json(const json_t& js, Config &config) { +inline void from_json(const json_t &js, Config &config) { get_value(config.shots, "shots", js); get_value(config.method, "method", js); get_value(config.device, "device", js); @@ -374,7 +424,8 @@ inline void from_json(const json_t& js, Config &config) { get_value(config.fusion_verbose, "fusion_verbose", js); get_value(config.fusion_max_qubit, "fusion_max_qubit", js); get_value(config.fusion_threshold, "fusion_threshold", js); - get_value(config.accept_distributed_results, "accept_distributed_results", js); + get_value(config.accept_distributed_results, "accept_distributed_results", + js); get_value(config.memory, "memory", js); // noise_model=None, get_value(config.seed_simulator, "seed_simulator", js); @@ -386,33 +437,49 @@ inline void from_json(const json_t& js, Config &config) { get_value(config.chunk_swap_buffer_qubits, "chunk_swap_buffer_qubits", js); // # multi-shots optimization options (GPU only) get_value(config.batched_shots_gpu, "batched_shots_gpu", js); - get_value(config.batched_shots_gpu_max_qubits, "batched_shots_gpu_max_qubits", js); + get_value(config.batched_shots_gpu_max_qubits, "batched_shots_gpu_max_qubits", + js); get_value(config.num_threads_per_device, "num_threads_per_device", js); // # statevector options - get_value(config.statevector_parallel_threshold, "statevector_parallel_threshold", js); - get_value(config.statevector_sample_measure_opt, "statevector_sample_measure_opt", js); + get_value(config.statevector_parallel_threshold, + "statevector_parallel_threshold", js); + get_value(config.statevector_sample_measure_opt, + "statevector_sample_measure_opt", js); // # stabilizer options - get_value(config.stabilizer_max_snapshot_probabilities, "stabilizer_max_snapshot_probabilities", js); + get_value(config.stabilizer_max_snapshot_probabilities, + "stabilizer_max_snapshot_probabilities", js); // # extended stabilizer options - get_value(config.extended_stabilizer_sampling_method, "extended_stabilizer_sampling_method", js); - get_value(config.extended_stabilizer_metropolis_mixing_time, "extended_stabilizer_metropolis_mixing_time", js); - get_value(config.extended_stabilizer_approximation_error, "extended_stabilizer_approximation_error", js); - get_value(config.extended_stabilizer_norm_estimation_samples, "extended_stabilizer_norm_estimation_samples", js); - get_value(config.extended_stabilizer_norm_estimation_repetitions, "extended_stabilizer_norm_estimation_repetitions", js); - get_value(config.extended_stabilizer_parallel_threshold, "extended_stabilizer_parallel_threshold", js); - get_value(config.extended_stabilizer_probabilities_snapshot_samples, "extended_stabilizer_probabilities_snapshot_samples", js); + get_value(config.extended_stabilizer_sampling_method, + "extended_stabilizer_sampling_method", js); + get_value(config.extended_stabilizer_metropolis_mixing_time, + "extended_stabilizer_metropolis_mixing_time", js); + get_value(config.extended_stabilizer_approximation_error, + "extended_stabilizer_approximation_error", js); + get_value(config.extended_stabilizer_norm_estimation_samples, + "extended_stabilizer_norm_estimation_samples", js); + get_value(config.extended_stabilizer_norm_estimation_repetitions, + "extended_stabilizer_norm_estimation_repetitions", js); + get_value(config.extended_stabilizer_parallel_threshold, + "extended_stabilizer_parallel_threshold", js); + get_value(config.extended_stabilizer_probabilities_snapshot_samples, + "extended_stabilizer_probabilities_snapshot_samples", js); // # MPS options - get_value(config.matrix_product_state_truncation_threshold, "matrix_product_state_truncation_threshold", js); - get_value(config.matrix_product_state_max_bond_dimension, "matrix_product_state_max_bond_dimension", js); - get_value(config.mps_sample_measure_algorithm, "mps_sample_measure_algorithm", js); + get_value(config.matrix_product_state_truncation_threshold, + "matrix_product_state_truncation_threshold", js); + get_value(config.matrix_product_state_max_bond_dimension, + "matrix_product_state_max_bond_dimension", js); + get_value(config.mps_sample_measure_algorithm, "mps_sample_measure_algorithm", + js); get_value(config.mps_log_data, "mps_log_data", js); get_value(config.mps_swap_direction, "mps_swap_direction", js); get_value(config.chop_threshold, "chop_threshold", js); get_value(config.mps_parallel_threshold, "mps_parallel_threshold", js); get_value(config.mps_omp_threads, "mps_omp_threads", js); // # tensor network options - get_value(config.tensor_network_num_sampling_qubits, "tensor_network_num_sampling_qubits", js); - get_value(config.use_cuTensorNet_autotuning, "use_cuTensorNet_autotuning", js); + get_value(config.tensor_network_num_sampling_qubits, + "tensor_network_num_sampling_qubits", js); + get_value(config.use_cuTensorNet_autotuning, "use_cuTensorNet_autotuning", + js); // system configurations get_value(config.param_table, "parameterizations", js); get_value(config.library_dir, "library_dir", js); @@ -425,7 +492,8 @@ inline void from_json(const json_t& js, Config &config) { get_value(config.fusion_allow_kraus, "fusion_allow_kraus", js); get_value(config.fusion_allow_superop, "fusion_allow_superop", js); - get_value(config.fusion_parallelization_threshold, "fusion_parallelization_threshold", js); + get_value(config.fusion_parallelization_threshold, + "fusion_parallelization_threshold", js); get_value(config._fusion_enable_n_qubits, "_fusion_enable_n_qubits", js); get_value(config._fusion_enable_n_qubits_1, "_fusion_enable_n_qubits_1", js); get_value(config._fusion_enable_n_qubits_2, "_fusion_enable_n_qubits_2", js); @@ -436,12 +504,15 @@ inline void from_json(const json_t& js, Config &config) { get_value(config._fusion_min_qubit, "_fusion_min_qubit", js); get_value(config.fusion_cost_factor, "fusion_cost_factor", js); - get_value(config.superoperator_parallel_threshold, "superoperator_parallel_threshold", js); - get_value(config.unitary_parallel_threshold, "unitary_parallel_threshold", js); + get_value(config.superoperator_parallel_threshold, + "superoperator_parallel_threshold", js); + get_value(config.unitary_parallel_threshold, "unitary_parallel_threshold", + js); get_value(config.memory_blocking_bits, "memory_blocking_bits", js); - get_value(config.extended_stabilizer_norm_estimation_default_samples, "extended_stabilizer_norm_estimation_default_samples", js); + get_value(config.extended_stabilizer_norm_estimation_default_samples, + "extended_stabilizer_norm_estimation_default_samples", js); } -} +} // namespace AER #endif \ No newline at end of file diff --git a/src/framework/creg.hpp b/src/framework/creg.hpp old mode 100755 new mode 100644 index 2a79a0e871..1a0d012865 --- a/src/framework/creg.hpp +++ b/src/framework/creg.hpp @@ -16,8 +16,8 @@ #define _aer_framework_creg_hpp_ #include "framework/operations.hpp" -#include "framework/utils.hpp" #include "framework/rng.hpp" +#include "framework/utils.hpp" namespace AER { @@ -29,39 +29,39 @@ namespace AER { class ClassicalRegister { public: - // Return the current value of the memory as little-endian hex-string - inline std::string memory_hex() const {return Utils::bin2hex(creg_memory_);} + inline std::string memory_hex() const { return Utils::bin2hex(creg_memory_); } // Return the current value of the memory as little-endian bit-string - inline std::string memory_bin() const {return "0b" + creg_memory_;} + inline std::string memory_bin() const { return "0b" + creg_memory_; } // Return the current value of the memory as little-endian hex-string - inline std::string register_hex() const {return Utils::bin2hex(creg_register_);} + inline std::string register_hex() const { + return Utils::bin2hex(creg_register_); + } // Return the current value of the memory as little-endian bit-string - inline std::string register_bin() const {return "0b" + creg_register_;} + inline std::string register_bin() const { return "0b" + creg_register_; } // Return the size of the memory bits - size_t memory_size() const {return creg_memory_.size();} + size_t memory_size() const { return creg_memory_.size(); } // Return the size of the register bits - size_t register_size() const {return creg_register_.size();} + size_t register_size() const { return creg_register_.size(); } // Return a reference to the current value of the memory // this is a bit-string without the "0b" prefix. - inline auto& creg_memory() {return creg_memory_;} + inline auto &creg_memory() { return creg_memory_; } // Return a reference to the current value of the memory // this is a bit-string without the "0b" prefix. - inline auto& creg_register() {return creg_register_;} + inline auto &creg_register() { return creg_register_; } // Initialize the memory and register bits to default values (all 0) void initialize(size_t num_memory, size_t num_registers); // Initialize the memory and register bits to specific values - void initialize(size_t num_memory, - size_t num_registers, + void initialize(size_t num_memory, size_t num_registers, const std::string &memory_hex, const std::string ®ister_hex); @@ -76,17 +76,18 @@ class ClassicalRegister { // Apply readout error instruction to classical registers void apply_roerror(const Operations::Op &op, RngEngine &rng); - // Store a measurement outcome in the specified memory and register bit locations - void store_measure(const reg_t &outcome, const reg_t &memory, const reg_t ®isters); + // Store a measurement outcome in the specified memory and register bit + // locations + void store_measure(const reg_t &outcome, const reg_t &memory, + const reg_t ®isters); protected: - // Classical registers std::string creg_memory_; // standard classical bit memory std::string creg_register_; // optional classical bit register // Measurement config settings - bool return_hex_strings_ = true; // Set to false for bit-string output + bool return_hex_strings_ = true; // Set to false for bit-string output }; //============================================================================ @@ -99,9 +100,7 @@ void ClassicalRegister::initialize(size_t num_memory, size_t num_register) { creg_register_ = std::string(num_register, '0'); } - -void ClassicalRegister::initialize(size_t num_memory, - size_t num_register, +void ClassicalRegister::initialize(size_t num_memory, size_t num_register, const std::string &memory_hex, const std::string ®ister_hex) { // Convert to bit-string for internal storage @@ -109,74 +108,77 @@ void ClassicalRegister::initialize(size_t num_memory, creg_memory_ = std::move(Utils::padleft_inplace(memory_bin, '0', num_memory)); std::string register_bin = Utils::hex2bin(register_hex, false); - creg_register_ = std::move(Utils::padleft_inplace(memory_bin, '0', num_register)); + creg_register_ = + std::move(Utils::padleft_inplace(memory_bin, '0', num_register)); } - -void ClassicalRegister::store_measure(const reg_t &outcome, - const reg_t &memory, +void ClassicalRegister::store_measure(const reg_t &outcome, const reg_t &memory, const reg_t ®isters) { // Assumes memory and registers are either empty or same size as outcome! bool use_mem = !memory.empty(); bool use_reg = !registers.empty(); - for (size_t j=0; j < outcome.size(); j++) { + for (size_t j = 0; j < outcome.size(); j++) { if (use_mem) { // least significant bit first ordering - const size_t pos = creg_memory_.size() - memory[j] - 1; + const size_t pos = creg_memory_.size() - memory[j] - 1; creg_memory_[pos] = std::to_string(outcome[j])[0]; // int->string->char } if (use_reg) { // least significant bit first ordering - const size_t pos = creg_register_.size() - registers[j] - 1; - creg_register_[pos] = std::to_string(outcome[j])[0]; // int->string->char + const size_t pos = creg_register_.size() - registers[j] - 1; + creg_register_[pos] = std::to_string(outcome[j])[0]; // int->string->char } } } - bool ClassicalRegister::check_conditional(const Operations::Op &op) const { // Check if op is conditional if (op.conditional) - return (creg_register_[creg_register_.size() - op.conditional_reg - 1] == '1'); + return (creg_register_[creg_register_.size() - op.conditional_reg - 1] == + '1'); // Op is not conditional return true; } - void ClassicalRegister::apply_bfunc(const Operations::Op &op) { // Check input is boolean function op if (op.type != Operations::OpType::bfunc) { - throw std::invalid_argument("ClassicalRegister::apply_bfunc: Input is not a bfunc op."); + throw std::invalid_argument( + "ClassicalRegister::apply_bfunc: Input is not a bfunc op."); } const std::string &mask = op.string_params[0]; const std::string &target_val = op.string_params[1]; - int_t compared; // if equal this should be 0, if less than -1, if greater than +1 + int_t compared; // if equal this should be 0, if less than -1, if greater than + // +1 // Check if register size fits into a 64-bit integer if (creg_register_.size() <= 64) { - uint_t reg_int = std::stoull(creg_register_, nullptr, 2); // stored as bitstring + uint_t reg_int = + std::stoull(creg_register_, nullptr, 2); // stored as bitstring uint_t mask_int = std::stoull(mask, nullptr, 16); // stored as hexstring - uint_t target_int = std::stoull(target_val, nullptr, 16); // stored as hexstring + uint_t target_int = + std::stoull(target_val, nullptr, 16); // stored as hexstring compared = (reg_int & mask_int) - target_int; } else { - // We need to use big ints so we implement the bit-mask via the binary string - // representation rather than using a big integer class + // We need to use big ints so we implement the bit-mask via the binary + // string representation rather than using a big integer class std::string mask_bin = Utils::hex2bin(mask, false); size_t length = std::min(mask_bin.size(), creg_register_.size()); std::string masked_val = std::string(length, '0'); for (size_t rev_pos = 0; rev_pos < length; rev_pos++) { - masked_val[length - 1 - rev_pos] = (mask_bin[mask_bin.size() - 1 - rev_pos] - & creg_register_[creg_register_.size() - 1 - rev_pos]); + masked_val[length - 1 - rev_pos] = + (mask_bin[mask_bin.size() - 1 - rev_pos] & + creg_register_[creg_register_.size() - 1 - rev_pos]); } // remove leading 0's size_t end_i = masked_val.find('1'); if (end_i == std::string::npos) - masked_val = "0"; + masked_val = "0"; else - masked_val.erase(0, end_i); + masked_val.erase(0, end_i); masked_val = Utils::bin2hex(masked_val); // convert to hex string // Using string comparison to compare to target value @@ -185,51 +187,53 @@ void ClassicalRegister::apply_bfunc(const Operations::Op &op) { // check value of compared integer for different comparison operations bool outcome; switch (op.bfunc) { - case Operations::RegComparison::Equal: - outcome = (compared == 0); - break; - case Operations::RegComparison::NotEqual: - outcome = (compared != 0); - break; - case Operations::RegComparison::Less: - outcome = (compared < 0); - break; - case Operations::RegComparison::LessEqual: - outcome = (compared <= 0); - break; - case Operations::RegComparison::Greater: - outcome = (compared > 0); - break; - case Operations::RegComparison::GreaterEqual: - outcome = (compared >= 0); - break; - default: - // we shouldn't ever get here - throw std::invalid_argument("Invalid boolean function relation."); + case Operations::RegComparison::Equal: + outcome = (compared == 0); + break; + case Operations::RegComparison::NotEqual: + outcome = (compared != 0); + break; + case Operations::RegComparison::Less: + outcome = (compared < 0); + break; + case Operations::RegComparison::LessEqual: + outcome = (compared <= 0); + break; + case Operations::RegComparison::Greater: + outcome = (compared > 0); + break; + case Operations::RegComparison::GreaterEqual: + outcome = (compared >= 0); + break; + default: + // we shouldn't ever get here + throw std::invalid_argument("Invalid boolean function relation."); } // Store outcome in register if (op.registers.size() > 0) { - const size_t pos = creg_register_.size() - op.registers[0] - 1; + const size_t pos = creg_register_.size() - op.registers[0] - 1; creg_register_[pos] = (outcome) ? '1' : '0'; } // Optionally store outcome in memory if (op.memory.size() > 0) { - const size_t pos = creg_memory_.size() - op.memory[0] - 1; + const size_t pos = creg_memory_.size() - op.memory[0] - 1; creg_memory_[pos] = (outcome) ? '1' : '0'; } } // Apply readout error instruction to classical registers -void ClassicalRegister::apply_roerror(const Operations::Op &op, RngEngine &rng) { - +void ClassicalRegister::apply_roerror(const Operations::Op &op, + RngEngine &rng) { + // Check input is readout error op if (op.type != Operations::OpType::roerror) { - throw std::invalid_argument("ClassicalRegister::apply_roerror Input is not a readout error op."); + throw std::invalid_argument( + "ClassicalRegister::apply_roerror Input is not a readout error op."); } - + // Get current classical bit (and optionally register bit) values std::string mem_str; - + // Get values of bits as binary string // We iterate from the end of the list of memory bits for (auto it = op.memory.rbegin(); it < op.memory.rend(); ++it) { @@ -241,12 +245,14 @@ void ClassicalRegister::apply_roerror(const Operations::Op &op, RngEngine &rng) auto noise_str = Utils::int2string(outcome, 2, op.memory.size()); for (size_t pos = 0; pos < op.memory.size(); ++pos) { auto bit = op.memory[pos]; - creg_memory_[creg_memory_.size() - 1 - bit] = noise_str[noise_str.size() - 1 - pos]; + creg_memory_[creg_memory_.size() - 1 - bit] = + noise_str[noise_str.size() - 1 - pos]; } // and the same error to register classical bits if they are used for (size_t pos = 0; pos < op.registers.size(); ++pos) { auto bit = op.registers[pos]; - creg_register_[creg_register_.size() - 1 - bit] = noise_str[noise_str.size() - 1 - pos]; + creg_register_[creg_register_.size() - 1 - bit] = + noise_str[noise_str.size() - 1 - pos]; } } diff --git a/src/framework/json.hpp b/src/framework/json.hpp old mode 100755 new mode 100644 index f6f79113f3..172f4eb1ee --- a/src/framework/json.hpp +++ b/src/framework/json.hpp @@ -29,8 +29,8 @@ DISABLE_WARNING_PUSH #include DISABLE_WARNING_POP -#include "framework/matrix.hpp" #include "framework/linalg/vector.hpp" +#include "framework/matrix.hpp" namespace nl = nlohmann; using json_t = nlohmann::json; @@ -48,7 +48,7 @@ namespace JSON { * @param name: file name to load. * @returns: the loaded json. */ -inline json_t load(const std::string& name); +inline json_t load(const std::string &name); /** * Check if a key exists in a json_t object. @@ -56,7 +56,7 @@ inline json_t load(const std::string& name); * @param js: the json_t to search for key. * @returns: true if the key exists, false otherwise. */ -inline bool check_key(const std::string& key, const json_t &js); +inline bool check_key(const std::string &key, const json_t &js); /** * Check if all keys exists in a json_t object. @@ -64,7 +64,7 @@ inline bool check_key(const std::string& key, const json_t &js); * @param js: the json_t to search for keys. * @returns: true if all keys exists, false otherwise. */ -inline bool check_keys(const std::vector& keys, const json_t &js); +inline bool check_keys(const std::vector &keys, const json_t &js); /** * Load a json_t object value into a variable if the key name exists. @@ -73,9 +73,10 @@ inline bool check_keys(const std::vector& keys, const json_t &js); * @param js: the json_t to search for key. * @returns: true if the keys exists and val was set, false otherwise. */ -template bool get_value(T &var, const std::string& key, const json_t &js); +template +bool get_value(T &var, const std::string &key, const json_t &js); -const json_t& get_value(const std::string& key, const json_t &js); +const json_t &get_value(const std::string &key, const json_t &js); } // end namespace JSON @@ -90,7 +91,8 @@ namespace std { * @param js a json_t object to contain converted type. * @param z a complex number to convert. */ -template void to_json(json_t &js, const std::complex &z); +template +void to_json(json_t &js, const std::complex &z); /** * Convert a JSON value to a complex number z. If the json value is a float @@ -99,7 +101,8 @@ template void to_json(json_t &js, const std::complex &z); * @param js a json_t object to convert. * @param z a complex number to contain result. */ -template void from_json(const json_t &js, std::complex &z); +template +void from_json(const json_t &js, std::complex &z); /** * Convert a complex vector to a json list @@ -150,9 +153,9 @@ void to_json(json_t &js, const std::map &map); * @param js a json_t object to contain converted type. * @param mat a matrix to convert. */ -template +template void from_json(const json_t &js, matrix &mat); -template +template void to_json(json_t &js, const matrix &mat); /******************************************************************************* @@ -165,7 +168,7 @@ void to_json(json_t &js, const matrix &mat); // JSON Helper Functions //------------------------------------------------------------------------------ -json_t JSON::load(const std::string& name) { +json_t JSON::load(const std::string &name) { if (name == "") { json_t js; return js; // Return empty node if no config file @@ -186,7 +189,7 @@ json_t JSON::load(const std::string& name) { return js; } -bool JSON::check_key(const std::string& key, const json_t &js) { +bool JSON::check_key(const std::string &key, const json_t &js) { // returns false if the value is 'null' if (js.find(key) != js.end() && !js[key].is_null()) return true; @@ -194,15 +197,15 @@ bool JSON::check_key(const std::string& key, const json_t &js) { return false; } -bool JSON::check_keys(const std::vector& keys, const json_t &js) { +bool JSON::check_keys(const std::vector &keys, const json_t &js) { bool pass = true; - for (const auto& s : keys) + for (const auto &s : keys) pass &= check_key(s, js); return pass; } template -bool JSON::get_value(T &var, const std::string& key, const json_t &js) { +bool JSON::get_value(T &var, const std::string &key, const json_t &js) { if (check_key(key, js)) { var = js[key].get(); return true; @@ -211,8 +214,8 @@ bool JSON::get_value(T &var, const std::string& key, const json_t &js) { } } -const json_t& JSON::get_value(const std::string& key, const json_t &js){ - return js[key]; +const json_t &JSON::get_value(const std::string &key, const json_t &js) { + return js[key]; } //------------------------------------------------------------------------------ @@ -231,8 +234,7 @@ void std::from_json(const json_t &js, std::complex &z) { else if (js.is_array() && js.size() == 2) { z = std::complex{js[0].get(), js[1].get()}; } else { - throw std::invalid_argument( - std::string("JSON: invalid complex number")); + throw std::invalid_argument(std::string("JSON: invalid complex number")); } } @@ -246,16 +248,15 @@ void std::to_json(json_t &js, const std::vector> &vec) { } template -void std::from_json(const json_t &js, std::vector> &vec) { +void std::from_json(const json_t &js, + std::vector> &vec) { std::vector> ret; if (js.is_array()) { for (auto &elt : js) ret.push_back(elt); vec = ret; - } - else { - throw std::invalid_argument( - std::string("JSON: invalid complex vector.")); + } else { + throw std::invalid_argument(std::string("JSON: invalid complex vector.")); } } @@ -263,7 +264,7 @@ template void std::to_json(json_t &js, const AER::Vector> &vec) { std::vector> out; for (int64_t i = 0; i < vec.size(); ++i) { - auto& z = vec[i]; + auto &z = vec[i]; out.push_back(std::vector{real(z), imag(z)}); } js = out; @@ -294,7 +295,8 @@ void std::to_json(json_t &js, const std::map &map) { // Implementation: JSON Conversion //------------------------------------------------------------------------------ -template void to_json(json_t &js, const matrix &mat) { +template +void to_json(json_t &js, const matrix &mat) { js = json_t(); size_t rows = mat.GetRows(); size_t cols = mat.GetColumns(); @@ -306,15 +308,15 @@ template void to_json(json_t &js, const matrix &mat) { } } - -template void from_json(const json_t &js, matrix &mat) { +template +void from_json(const json_t &js, matrix &mat) { // Check JSON is an array - if(!js.is_array()) { + if (!js.is_array()) { throw std::invalid_argument( std::string("JSON: invalid matrix (not array).")); } // Check JSON isn't empty - if(js.empty()) { + if (js.empty()) { throw std::invalid_argument( std::string("JSON: invalid matrix (empty array).")); } @@ -325,7 +327,7 @@ template void from_json(const json_t &js, matrix &mat) { size_t nrows = js.size(); for (auto &row : js) rows_valid &= (row.is_array() && row.size() == ncols); - if(!rows_valid) { + if (!rows_valid) { throw std::invalid_argument( std::string("JSON: invalid matrix (rows different sizes).")); } diff --git a/src/framework/json_parser.hpp b/src/framework/json_parser.hpp index 882af3efe0..d9576338e8 100644 --- a/src/framework/json_parser.hpp +++ b/src/framework/json_parser.hpp @@ -17,8 +17,7 @@ #include "json.hpp" - -namespace AER{ +namespace AER { // This structure is to avoid overload resolving to the wron function, // as py::objects can always be implicitly converted to json, though // can break at runtime, or even worse trasnform to json and then to c++ @@ -28,61 +27,57 @@ struct Parser {}; template <> struct Parser { - Parser() = delete; + Parser() = delete; - template - static bool get_value(T &var, const std::string& key, const json_t &js){ - return JSON::get_value(var, key, js); - } + template + static bool get_value(T &var, const std::string &key, const json_t &js) { + return JSON::get_value(var, key, js); + } - static bool check_key(const std::string& key, const json_t &js){ - return JSON::check_key(key, js); - } + static bool check_key(const std::string &key, const json_t &js) { + return JSON::check_key(key, js); + } - static const json_t& get_value(const std::string& key, const json_t &js){ - return JSON::get_value(key, js); - } + static const json_t &get_value(const std::string &key, const json_t &js) { + return JSON::get_value(key, js); + } - static bool check_keys(const std::vector& keys, const json_t &js) { - return JSON::check_keys(keys, js); - } + static bool check_keys(const std::vector &keys, + const json_t &js) { + return JSON::check_keys(keys, js); + } - static bool is_array(const json_t &js){ - return js.is_array(); - } + static bool is_array(const json_t &js) { return js.is_array(); } - static bool is_array(const std::string& key, const json_t &js){ - return js[key].is_array(); - } + static bool is_array(const std::string &key, const json_t &js) { + return js[key].is_array(); + } - static const json_t& get_as_list(const json_t& js){ - if(!is_array(js)){ - throw std::runtime_error("Object is not a list!"); - } - return js; + static const json_t &get_as_list(const json_t &js) { + if (!is_array(js)) { + throw std::runtime_error("Object is not a list!"); } + return js; + } - static const json_t& get_list(const std::string& key, const json_t &js){ - if(!is_array(key, js)){ - throw std::runtime_error("Object " + key + "is not a list!"); - } - return JSON::get_value(key, js); + static const json_t &get_list(const std::string &key, const json_t &js) { + if (!is_array(key, js)) { + throw std::runtime_error("Object " + key + "is not a list!"); } + return JSON::get_value(key, js); + } + static bool is_number(const std::string &key, const json_t &js) { + return js[key].is_number(); + } - static bool is_number(const std::string& key, const json_t &js){ - return js[key].is_number(); - } + static std::string dump(const json_t &js) { return js.dump(); } - static std::string dump(const json_t& js){ - return js.dump(); - } - - template - static T get_list_elem(const json_t& js, unsigned int i){ - return js[i]; - } + template + static T get_list_elem(const json_t &js, unsigned int i) { + return js[i]; + } }; -} +} // namespace AER #endif // _aer_framework_json_parser_hpp_ diff --git a/src/framework/linalg/almost_equal.hpp b/src/framework/linalg/almost_equal.hpp old mode 100755 new mode 100644 index 387cd9d3bc..f4a645d455 --- a/src/framework/linalg/almost_equal.hpp +++ b/src/framework/linalg/almost_equal.hpp @@ -29,27 +29,28 @@ namespace Linalg { // If we have numbers closer to 0, then max_diff can be set to a value // way smaller than epsilon. For numbers larger than 1.0, epsilon will // scale (the bigger the number, the bigger the epsilon). -template ::value, T>::type > -bool almost_equal(T f1, T f2, - T max_diff = std::numeric_limits::epsilon(), +template ::value, T>::type> +bool almost_equal(T f1, T f2, T max_diff = std::numeric_limits::epsilon(), T max_relative_diff = std::numeric_limits::epsilon()) { - T diff = std::abs(f1 - f2); - if (diff <= max_diff) return true; + T diff = std::abs(f1 - f2); + if (diff <= max_diff) + return true; - return diff <= max_relative_diff * std::max(std::abs(f1), std::abs(f2)); + return diff <= max_relative_diff * std::max(std::abs(f1), std::abs(f2)); } template -bool almost_equal(const std::complex& f1, const std::complex& f2, +bool almost_equal(const std::complex &f1, const std::complex &f2, T max_diff = std::numeric_limits::epsilon(), T max_relative_diff = std::numeric_limits::epsilon()) { - return almost_equal(f1.real(), f2.real(), max_diff, max_relative_diff) - && almost_equal(f1.imag(), f2.imag(), max_diff, max_relative_diff); + return almost_equal(f1.real(), f2.real(), max_diff, max_relative_diff) && + almost_equal(f1.imag(), f2.imag(), max_diff, max_relative_diff); } //------------------------------------------------------------------------------ -} // namespace Linalg +} // namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif diff --git a/src/framework/linalg/eigensystem.hpp b/src/framework/linalg/eigensystem.hpp index da0cc10d58..3f7d463b31 100644 --- a/src/framework/linalg/eigensystem.hpp +++ b/src/framework/linalg/eigensystem.hpp @@ -15,66 +15,65 @@ #ifndef _aer_framework_linalg_eigensystem_hpp_ #define _aer_framework_linalg_eigensystem_hpp_ -#include #include "framework/blas_protos.hpp" #include "framework/matrix.hpp" - +#include /** * Returns the eigenvalues and eigenvectors * of a Hermitian matrix. * Uses the blas function ?heevx * @param hermitian_matrix: The Hermitian matrix. - * @param eigenvalues: On output: vector with the eignevalues of the matrix (input is overwritten) - * @param eigenvectors: On output: matrix with the eigenvectors stored as columns. + * @param eigenvalues: On output: vector with the eignevalues of the matrix + * (input is overwritten) + * @param eigenvectors: On output: matrix with the eigenvectors stored as + * columns. * * @returns: void */ template -void eigensystem_hermitian(const matrix>& hermitian_matrix, - /* out */ std::vector& eigenvalues, - /* out */ matrix>& eigenvectors); +void eigensystem_hermitian(const matrix> &hermitian_matrix, + /* out */ std::vector &eigenvalues, + /* out */ matrix> &eigenvectors); - -template +template struct HeevxFuncs; -template<> -struct HeevxFuncs{ +template <> +struct HeevxFuncs { HeevxFuncs() = delete; - static decltype(zheevx_)& heevx; - static decltype(dlamch_)& lamch; + static decltype(zheevx_) &heevx; + static decltype(dlamch_) &lamch; }; -decltype(zheevx_)& HeevxFuncs::heevx = zheevx_; -decltype(dlamch_)& HeevxFuncs::lamch = dlamch_; +decltype(zheevx_) &HeevxFuncs::heevx = zheevx_; +decltype(dlamch_) &HeevxFuncs::lamch = dlamch_; -template<> -struct HeevxFuncs{ +template <> +struct HeevxFuncs { HeevxFuncs() = delete; - static decltype(cheevx_)& heevx; - static decltype(slamch_)& lamch; + static decltype(cheevx_) &heevx; + static decltype(slamch_) &lamch; }; -decltype(cheevx_)& HeevxFuncs::heevx = cheevx_; -decltype(slamch_)& HeevxFuncs::lamch = slamch_; - +decltype(cheevx_) &HeevxFuncs::heevx = cheevx_; +decltype(slamch_) &HeevxFuncs::lamch = slamch_; template -void eigensystem_hermitian(const matrix>& hermitian_matrix, - std::vector& eigenvalues, - matrix>& eigenvectors) { - if ( hermitian_matrix.GetRows() != hermitian_matrix.GetColumns() ) { +void eigensystem_hermitian(const matrix> &hermitian_matrix, + std::vector &eigenvalues, + matrix> &eigenvectors) { + if (hermitian_matrix.GetRows() != hermitian_matrix.GetColumns()) { throw std::runtime_error("Input matrix in eigensystem_hermitian " "function is not a square matrix."); } int n = static_cast(hermitian_matrix.GetLD()); - int ldz{n}, lda{n}, lwork{2*n}; - int il{0}, iu{0}; // not referenced if range='A' + int ldz{n}, lda{n}, lwork{2 * n}; + int il{0}, iu{0}; // not referenced if range='A' T vl{0.0}, vu{0.0}; // not referenced if range='A' char cmach{'S'}; - T abstol{static_cast(2.0*HeevxFuncs::lamch(&cmach))}; + T abstol{static_cast(2.0 * HeevxFuncs::lamch(&cmach))}; int m{0}; // number of eigenvalues found int info{0}; @@ -83,18 +82,20 @@ void eigensystem_hermitian(const matrix>& hermitian_matrix, eigenvalues.resize(n); matrix> heevx_copy{hermitian_matrix}; auto work = std::vector>(lwork, {0.0, 0.0}); - auto rwork = std::vector(7*n, 0.0); - auto iwork = std::vector(5*n, 0); + auto rwork = std::vector(7 * n, 0.0); + auto iwork = std::vector(5 * n, 0); auto ifail = std::vector(n, 0); - HeevxFuncs::heevx(&AerBlas::Jobz[0], &AerBlas::Range[0], &AerBlas::UpLo[0], &n, - heevx_copy.data(), &lda, &vl, &vu, &il, &iu, - &abstol, &m, eigenvalues.data(), eigenvectors.data(), &ldz, work.data(), - &lwork, rwork.data(), iwork.data(), ifail.data(), &info); + HeevxFuncs::heevx(&AerBlas::Jobz[0], &AerBlas::Range[0], &AerBlas::UpLo[0], + &n, heevx_copy.data(), &lda, &vl, &vu, &il, &iu, &abstol, + &m, eigenvalues.data(), eigenvectors.data(), &ldz, + work.data(), &lwork, rwork.data(), iwork.data(), + ifail.data(), &info); - if(info){ - throw std::runtime_error("Something went wrong in heevx call within eigensystem_hermitian funcion. " - "Check that input matrix is really hermitian"); + if (info) { + throw std::runtime_error("Something went wrong in heevx call within " + "eigensystem_hermitian funcion. " + "Check that input matrix is really hermitian"); } } diff --git a/src/framework/linalg/linops/linops_aer_vector.hpp b/src/framework/linalg/linops/linops_aer_vector.hpp old mode 100755 new mode 100644 index ba6ebc190f..708d04cfae --- a/src/framework/linalg/linops/linops_aer_vector.hpp +++ b/src/framework/linalg/linops/linops_aer_vector.hpp @@ -31,23 +31,23 @@ namespace Linalg { // Linear operations //---------------------------------------------------------------------------- template > -Vector add(const Vector& lhs, const Vector& rhs) { +Vector add(const Vector &lhs, const Vector &rhs) { return lhs + rhs; } template > -Vector& iadd(Vector& lhs, const Vector& rhs) { +Vector &iadd(Vector &lhs, const Vector &rhs) { lhs += rhs; return lhs; } template > -Vector sub(const Vector& lhs, const Vector& rhs) { +Vector sub(const Vector &lhs, const Vector &rhs) { return lhs - rhs; } template > -Vector& isub(Vector& lhs, const Vector& rhs) { +Vector &isub(Vector &lhs, const Vector &rhs) { lhs -= rhs; return lhs; } @@ -57,30 +57,29 @@ Vector& isub(Vector& lhs, const Vector& rhs) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -Vector& iadd(Vector& data, const Scalar& val) { +Vector &iadd(Vector &data, const Scalar &val) { const T cast_val(val); std::for_each(data.data(), data.data() + data.size(), - [&cast_val](T& a)->T{ a += cast_val; }); + [&cast_val](T &a) -> T { a += cast_val; }); return data; } template , typename = enable_if_numeric_t> -Vector add(const Vector& data, const Scalar& val) { +Vector add(const Vector &data, const Scalar &val) { auto ret = data; return iadd(data, val); } - template , typename = enable_if_numeric_t> -Vector sub(const Vector& data, const Scalar& val) { +Vector sub(const Vector &data, const Scalar &val) { return add(data, -val); } template , typename = enable_if_numeric_t> -Vector& isub(Vector& data, const Scalar& val) { +Vector &isub(Vector &data, const Scalar &val) { return iadd(data, -val); } @@ -89,7 +88,7 @@ Vector& isub(Vector& data, const Scalar& val) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -Vector& imul(Vector& data, const Scalar& val) { +Vector &imul(Vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -99,14 +98,14 @@ Vector& imul(Vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -Vector mul(const Vector& data, const Scalar& val) { +Vector mul(const Vector &data, const Scalar &val) { auto ret = data; return imul(ret, val); } template , typename = enable_if_numeric_t> -Vector& idiv(Vector& data, const Scalar& val) { +Vector &idiv(Vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -114,17 +113,16 @@ Vector& idiv(Vector& data, const Scalar& val) { return data; } - template , typename = enable_if_numeric_t> -Vector div(const Vector& data, const Scalar& val) { +Vector div(const Vector &data, const Scalar &val) { auto ret = data; return idiv(ret, val); } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_array.hpp b/src/framework/linalg/linops/linops_array.hpp old mode 100755 new mode 100644 index 1211cb7dbe..4c1df3bac0 --- a/src/framework/linalg/linops/linops_array.hpp +++ b/src/framework/linalg/linops/linops_array.hpp @@ -31,27 +31,27 @@ namespace Linalg { // Linear operations //---------------------------------------------------------------------------- template > -std::array& iadd(std::array& lhs, const std::array& rhs) { +std::array &iadd(std::array &lhs, const std::array &rhs) { std::transform(lhs.begin(), lhs.end(), rhs.begin(), lhs.begin(), std::plus()); return lhs; } template > -std::array add(const std::array& lhs, const std::array& rhs) { +std::array add(const std::array &lhs, const std::array &rhs) { std::array result = lhs; return iadd(result, rhs); } template > -std::array& isub(std::array& lhs, const std::array& rhs) { +std::array &isub(std::array &lhs, const std::array &rhs) { std::transform(lhs.begin(), lhs.end(), rhs.begin(), lhs.begin(), std::minus()); return lhs; } template > -std::array sub(const std::array& lhs, const std::array& rhs) { +std::array sub(const std::array &lhs, const std::array &rhs) { std::array result = lhs; return isub(result, rhs); } @@ -61,7 +61,7 @@ std::array sub(const std::array& lhs, const std::array& rhs) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -std::array& iadd(std::array& data, const Scalar& val) { +std::array &iadd(std::array &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::plus(), std::placeholders::_1, val)); return data; @@ -69,14 +69,14 @@ std::array& iadd(std::array& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::array add(const std::array& data, const Scalar& val) { +std::array add(const std::array &data, const Scalar &val) { std::array result = data; return iadd(result, val); } template , typename = enable_if_numeric_t> -std::array& isub(std::array& data, const Scalar& val) { +std::array &isub(std::array &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::minus(), std::placeholders::_1, val)); return data; @@ -84,7 +84,7 @@ std::array& isub(std::array& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::array sub(const std::array& data, const Scalar& val) { +std::array sub(const std::array &data, const Scalar &val) { std::array result = data; return isub(result, val); } @@ -94,7 +94,7 @@ std::array sub(const std::array& data, const Scalar& val) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -std::array& imul(std::array& data, const Scalar& val) { +std::array &imul(std::array &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::multiplies(), std::placeholders::_1, val)); return data; @@ -102,14 +102,14 @@ std::array& imul(std::array& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::array mul(const std::array& data, const Scalar& val) { +std::array mul(const std::array &data, const Scalar &val) { std::array result = data; return imul(result, val); } template , typename = enable_if_numeric_t> -std::array& idiv(std::array& data, const Scalar& val) { +std::array &idiv(std::array &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::divides(), std::placeholders::_1, val)); return data; @@ -117,14 +117,14 @@ std::array& idiv(std::array& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::array div(const std::array& data, const Scalar& val) { +std::array div(const std::array &data, const Scalar &val) { std::array result = data; return idiv(result, val); } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_generic.hpp b/src/framework/linalg/linops/linops_generic.hpp old mode 100755 new mode 100644 index f85b04e370..11ae56cd04 --- a/src/framework/linalg/linops/linops_generic.hpp +++ b/src/framework/linalg/linops/linops_generic.hpp @@ -30,23 +30,23 @@ namespace Linalg { // Linear operations //---------------------------------------------------------------------------- template -T add(const T& lhs, const T& rhs) { +T add(const T &lhs, const T &rhs) { return std::plus()(lhs, rhs); } template -T& iadd(T& lhs, const T& rhs) { +T &iadd(T &lhs, const T &rhs) { lhs = std::plus()(lhs, rhs); return lhs; } template -T sub(const T& lhs, const T& rhs) { +T sub(const T &lhs, const T &rhs) { return std::minus()(lhs, rhs); } template -T& isub(T& lhs, const T& rhs) { +T &isub(T &lhs, const T &rhs) { lhs = std::minus()(lhs, rhs); return lhs; } @@ -55,23 +55,23 @@ T& isub(T& lhs, const T& rhs) { // Affine operations //---------------------------------------------------------------------------- template > -T add(const T& data, const Scalar& val) { +T add(const T &data, const Scalar &val) { return std::plus()(data, val); } template > -T& iadd(T& data, const Scalar& val) { +T &iadd(T &data, const Scalar &val) { data = std::plus()(data, val); return data; } template > -T sub(const T& data, const Scalar& val) { +T sub(const T &data, const Scalar &val) { return std::minus()(data, val); } template > -T& isub(T& data, const Scalar& val) { +T &isub(T &data, const Scalar &val) { data = std::minus()(data, val); return data; } @@ -80,7 +80,7 @@ T& isub(T& data, const Scalar& val) { // Scalar operations //---------------------------------------------------------------------------- template > -T mul(const T& data, const Scalar& val) { +T mul(const T &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -88,7 +88,7 @@ T mul(const T& data, const Scalar& val) { } template > -T& imul(T& data, const Scalar& val) { +T &imul(T &data, const Scalar &val) { if (!almost_equal(val, 1)) { data = std::multiplies()(data, val); } @@ -96,7 +96,7 @@ T& imul(T& data, const Scalar& val) { } template > -T div(const T& data, const Scalar& val) { +T div(const T &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -104,7 +104,7 @@ T div(const T& data, const Scalar& val) { } template > -T& idiv(T& data, const Scalar& val) { +T &idiv(T &data, const Scalar &val) { if (!almost_equal(val, 1)) { data = std::divides()(data, val); } @@ -112,8 +112,8 @@ T& idiv(T& data, const Scalar& val) { } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_json.hpp b/src/framework/linalg/linops/linops_json.hpp old mode 100755 new mode 100644 index 8473bfbb82..1d8adb3a6b --- a/src/framework/linalg/linops/linops_json.hpp +++ b/src/framework/linalg/linops/linops_json.hpp @@ -28,7 +28,7 @@ namespace Linalg { //---------------------------------------------------------------------------- // Linear operations //---------------------------------------------------------------------------- -inline json_t& iadd(json_t& lhs, const json_t& rhs) { +inline json_t &iadd(json_t &lhs, const json_t &rhs) { // Null case if (lhs.is_null()) { lhs = rhs; @@ -57,12 +57,12 @@ inline json_t& iadd(json_t& lhs, const json_t& rhs) { return lhs; } -inline json_t add(const json_t& lhs, const json_t& rhs) { +inline json_t add(const json_t &lhs, const json_t &rhs) { json_t result = lhs; return iadd(result, rhs); } -inline json_t& isub(json_t& lhs, const json_t& rhs) { +inline json_t &isub(json_t &lhs, const json_t &rhs) { // Null case if (rhs.is_null()) { return lhs; @@ -88,7 +88,7 @@ inline json_t& isub(json_t& lhs, const json_t& rhs) { } template -json_t sub(const T& lhs, const json_t& rhs) { +json_t sub(const T &lhs, const json_t &rhs) { json_t result = lhs; return isub(result, rhs); } @@ -97,7 +97,7 @@ json_t sub(const T& lhs, const json_t& rhs) { // Affine operations //---------------------------------------------------------------------------- template > -json_t& iadd(json_t& data, const Scalar& val) { +json_t &iadd(json_t &data, const Scalar &val) { // Null case if (val == 0) { return data; @@ -123,13 +123,13 @@ json_t& iadd(json_t& data, const Scalar& val) { } template > -json_t add(const json_t& data, const Scalar& val) { +json_t add(const json_t &data, const Scalar &val) { json_t result = data; return iadd(result, val); } template > -json_t& isub(json_t& data, const Scalar& val) { +json_t &isub(json_t &data, const Scalar &val) { // Null case if (val == 0) { return data; @@ -156,7 +156,7 @@ json_t& isub(json_t& data, const Scalar& val) { } template > -json_t sub(const json_t& data, const Scalar& val) { +json_t sub(const json_t &data, const Scalar &val) { json_t result = data; return isub(result, val); } @@ -166,7 +166,7 @@ json_t sub(const json_t& data, const Scalar& val) { //---------------------------------------------------------------------------- template > -json_t& imul(json_t& data, const Scalar& val) { +json_t &imul(json_t &data, const Scalar &val) { // Trival case if (almost_equal(val, 1)) { return data; @@ -194,14 +194,14 @@ json_t& imul(json_t& data, const Scalar& val) { } template > -json_t mul(const json_t& data, const Scalar& val) { +json_t mul(const json_t &data, const Scalar &val) { // Null case json_t result = data; return imul(result, val); } template > -json_t& idiv(json_t& data, const Scalar& val) { +json_t &idiv(json_t &data, const Scalar &val) { // Trival case if (almost_equal(val, 1)) { return data; @@ -228,15 +228,15 @@ json_t& idiv(json_t& data, const Scalar& val) { } template > -json_t div(const json_t& data, const Scalar& val) { +json_t div(const json_t &data, const Scalar &val) { // Null case json_t result = data; return idiv(result, val); } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_map.hpp b/src/framework/linalg/linops/linops_map.hpp old mode 100755 new mode 100644 index f42cb80c6c..f69c8a675d --- a/src/framework/linalg/linops/linops_map.hpp +++ b/src/framework/linalg/linops/linops_map.hpp @@ -15,10 +15,10 @@ #ifndef _aer_framework_linalg_linops_map_hpp_ #define _aer_framework_linalg_linops_map_hpp_ -#include -#include #include "framework/linalg/almost_equal.hpp" #include "framework/linalg/enable_if_numeric.hpp" +#include +#include namespace AER { namespace Linalg { @@ -31,8 +31,8 @@ namespace Linalg { //---------------------------------------------------------------------------- template > -std::map add(const std::map& lhs, - const std::map& rhs) { +std::map add(const std::map &lhs, + const std::map &rhs) { std::map result = lhs; for (const auto &pair : rhs) { result[pair.first] = std::plus()(result[pair.first], pair.second); @@ -42,8 +42,8 @@ std::map add(const std::map& lhs, template > -std::map& iadd(std::map& lhs, - const std::map& rhs) { +std::map &iadd(std::map &lhs, + const std::map &rhs) { for (const auto &pair : rhs) { lhs[pair.first] = std::plus()(lhs[pair.first], pair.second); } @@ -52,8 +52,8 @@ std::map& iadd(std::map& lhs, template > -std::map sub(const std::map& lhs, - const std::map& rhs) { +std::map sub(const std::map &lhs, + const std::map &rhs) { std::map result = lhs; for (const auto &pair : rhs) { result[pair.first] = std::minus()(result[pair.first], pair.second); @@ -63,8 +63,8 @@ std::map sub(const std::map& lhs, template > -std::map& isub(std::map& lhs, - const std::map& rhs) { +std::map &isub(std::map &lhs, + const std::map &rhs) { for (const auto &pair : rhs) { lhs[pair.first] = std::minus()(lhs[pair.first], pair.second); } @@ -77,8 +77,8 @@ std::map& isub(std::map& lhs, template , typename = enable_if_numeric_t> -std::map add(const std::map& data, - const Scalar& val) { +std::map add(const std::map &data, + const Scalar &val) { std::map result; for (const auto &pair : data) { result[pair.first] = std::plus()(pair.second, val); @@ -89,8 +89,8 @@ std::map add(const std::map& data, template , typename = enable_if_numeric_t> -std::map& iadd(std::map& data, - const Scalar& val) { +std::map &iadd(std::map &data, + const Scalar &val) { for (const auto &pair : data) { data[pair.first] = std::plus()(data[pair.first], val); } @@ -100,8 +100,8 @@ std::map& iadd(std::map& data, template , typename = enable_if_numeric_t> -std::map sub(const std::map& data, - const Scalar& val) { +std::map sub(const std::map &data, + const Scalar &val) { std::map result; for (const auto &pair : data) { result[pair.first] = std::minus()(pair.second, val); @@ -112,8 +112,8 @@ std::map sub(const std::map& data, template , typename = enable_if_numeric_t> -std::map& isub(std::map& data, - const Scalar& val) { +std::map &isub(std::map &data, + const Scalar &val) { for (const auto &pair : data) { data[pair.first] = std::plus()(data[pair.first], val); } @@ -127,8 +127,8 @@ std::map& isub(std::map& data, template , typename = enable_if_numeric_t> -std::map mul(const std::map& data, - const Scalar& val) { +std::map mul(const std::map &data, + const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -142,8 +142,8 @@ std::map mul(const std::map& data, template , typename = enable_if_numeric_t> -std::map& imul(std::map& data, - const Scalar& val) { +std::map &imul(std::map &data, + const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -156,8 +156,8 @@ std::map& imul(std::map& data, template , typename = enable_if_numeric_t> -std::map div(const std::map& data, - const Scalar& val) { +std::map div(const std::map &data, + const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -171,8 +171,8 @@ std::map div(const std::map& data, template , typename = enable_if_numeric_t> -std::map& idiv(std::map& data, - const Scalar& val) { +std::map &idiv(std::map &data, + const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -183,8 +183,8 @@ std::map& idiv(std::map& data, } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_matrix.hpp b/src/framework/linalg/linops/linops_matrix.hpp old mode 100755 new mode 100644 index 4b510389c3..7fdac60fd7 --- a/src/framework/linalg/linops/linops_matrix.hpp +++ b/src/framework/linalg/linops/linops_matrix.hpp @@ -31,23 +31,23 @@ namespace Linalg { // Linear operations //---------------------------------------------------------------------------- template > -matrix add(const matrix& lhs, const matrix& rhs) { +matrix add(const matrix &lhs, const matrix &rhs) { return lhs + rhs; } template > -matrix& iadd(matrix& lhs, const matrix& rhs) { +matrix &iadd(matrix &lhs, const matrix &rhs) { lhs = lhs + rhs; return lhs; } template > -matrix sub(const matrix& lhs, const matrix& rhs) { +matrix sub(const matrix &lhs, const matrix &rhs) { return lhs - rhs; } template > -matrix& isub(matrix& lhs, const matrix& rhs) { +matrix &isub(matrix &lhs, const matrix &rhs) { lhs = lhs - rhs; return lhs; } @@ -57,7 +57,7 @@ matrix& isub(matrix& lhs, const matrix& rhs) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -matrix& iadd(matrix& data, const Scalar& val) { +matrix &iadd(matrix &data, const Scalar &val) { if (val == 0) { return data; } @@ -69,20 +69,20 @@ matrix& iadd(matrix& data, const Scalar& val) { template , typename = enable_if_numeric_t> -matrix add(const matrix& data, const Scalar& val) { +matrix add(const matrix &data, const Scalar &val) { matrix result(data); return iadd(result, val); } template , typename = enable_if_numeric_t> -matrix sub(const matrix& data, const Scalar& val) { +matrix sub(const matrix &data, const Scalar &val) { return add(data, -val); } template , typename = enable_if_numeric_t> -matrix& isub(matrix& data, const Scalar& val) { +matrix &isub(matrix &data, const Scalar &val) { return iadd(data, -val); } @@ -92,7 +92,7 @@ matrix& isub(matrix& data, const Scalar& val) { template , typename = enable_if_numeric_t> -matrix& imul(matrix& data, const Scalar& val) { +matrix &imul(matrix &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -104,7 +104,7 @@ matrix& imul(matrix& data, const Scalar& val) { template , typename = enable_if_numeric_t> -matrix mul(const matrix& data, const Scalar& val) { +matrix mul(const matrix &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -115,7 +115,7 @@ matrix mul(const matrix& data, const Scalar& val) { template , typename = enable_if_numeric_t> -matrix& idiv(matrix& data, const Scalar& val) { +matrix &idiv(matrix &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -127,7 +127,7 @@ matrix& idiv(matrix& data, const Scalar& val) { template , typename = enable_if_numeric_t> -matrix div(const matrix& data, const Scalar& val) { +matrix div(const matrix &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -137,8 +137,8 @@ matrix div(const matrix& data, const Scalar& val) { } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_unordered_map.hpp b/src/framework/linalg/linops/linops_unordered_map.hpp old mode 100755 new mode 100644 index 7df754b5de..2245172f0c --- a/src/framework/linalg/linops/linops_unordered_map.hpp +++ b/src/framework/linalg/linops/linops_unordered_map.hpp @@ -31,9 +31,9 @@ namespace Linalg { //---------------------------------------------------------------------------- template > -std::unordered_map add( - const std::unordered_map& lhs, - const std::unordered_map& rhs) { +std::unordered_map +add(const std::unordered_map &lhs, + const std::unordered_map &rhs) { std::unordered_map result = lhs; for (const auto &pair : rhs) { result[pair.first] = std::plus()(result[pair.first], pair.second); @@ -43,9 +43,9 @@ std::unordered_map add( template > -std::unordered_map& iadd( - std::unordered_map& lhs, - const std::unordered_map& rhs) { +std::unordered_map & +iadd(std::unordered_map &lhs, + const std::unordered_map &rhs) { for (const auto &pair : rhs) { lhs[pair.first] = std::plus()(lhs[pair.first], pair.second); } @@ -54,9 +54,9 @@ std::unordered_map& iadd( template > -std::unordered_map sub( - const std::unordered_map& lhs, - const std::unordered_map& rhs) { +std::unordered_map +sub(const std::unordered_map &lhs, + const std::unordered_map &rhs) { std::unordered_map result = lhs; for (const auto &pair : rhs) { result[pair.first] = std::minus()(result[pair.first], pair.second); @@ -66,9 +66,9 @@ std::unordered_map sub( template > -std::unordered_map& isub( - std::unordered_map& lhs, - const std::unordered_map& rhs) { +std::unordered_map & +isub(std::unordered_map &lhs, + const std::unordered_map &rhs) { for (const auto &pair : rhs) { lhs[pair.first] = std::minus()(lhs[pair.first], pair.second); } @@ -81,8 +81,8 @@ std::unordered_map& isub( template , typename = enable_if_numeric_t> -std::unordered_map add( - const std::unordered_map& data, const Scalar& val) { +std::unordered_map +add(const std::unordered_map &data, const Scalar &val) { std::unordered_map result; for (const auto &pair : data) { result[pair.first] = std::plus()(pair.second, val); @@ -93,8 +93,8 @@ std::unordered_map add( template , typename = enable_if_numeric_t> -std::unordered_map& iadd( - std::unordered_map& data, const Scalar& val) { +std::unordered_map & +iadd(std::unordered_map &data, const Scalar &val) { for (const auto &pair : data) { data[pair.first] = std::plus()(data[pair.first], val); } @@ -104,8 +104,8 @@ std::unordered_map& iadd( template , typename = enable_if_numeric_t> -std::unordered_map sub( - const std::unordered_map& data, const Scalar& val) { +std::unordered_map +sub(const std::unordered_map &data, const Scalar &val) { std::unordered_map result; for (const auto &pair : data) { result[pair.first] = std::minus()(pair.second, val); @@ -116,8 +116,8 @@ std::unordered_map sub( template , typename = enable_if_numeric_t> -std::unordered_map& isub( - std::unordered_map& data, const Scalar& val) { +std::unordered_map & +isub(std::unordered_map &data, const Scalar &val) { for (const auto &pair : data) { data[pair.first] = std::plus()(data[pair.first], val); } @@ -131,8 +131,8 @@ std::unordered_map& isub( template , typename = enable_if_numeric_t> -std::unordered_map mul( - const std::unordered_map& data, const Scalar& val) { +std::unordered_map +mul(const std::unordered_map &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -146,8 +146,8 @@ std::unordered_map mul( template , typename = enable_if_numeric_t> -std::unordered_map& imul( - std::unordered_map& data, const Scalar& val) { +std::unordered_map & +imul(std::unordered_map &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -160,8 +160,8 @@ std::unordered_map& imul( template , typename = enable_if_numeric_t> -std::unordered_map div( - const std::unordered_map& data, const Scalar& val) { +std::unordered_map +div(const std::unordered_map &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -175,8 +175,8 @@ std::unordered_map div( template , typename = enable_if_numeric_t> -std::unordered_map& idiv( - std::unordered_map& data, const Scalar& val) { +std::unordered_map & +idiv(std::unordered_map &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -187,8 +187,8 @@ std::unordered_map& idiv( } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/linops/linops_vector.hpp b/src/framework/linalg/linops/linops_vector.hpp old mode 100755 new mode 100644 index 759442d9f4..c1b86d79d8 --- a/src/framework/linalg/linops/linops_vector.hpp +++ b/src/framework/linalg/linops/linops_vector.hpp @@ -31,7 +31,7 @@ namespace Linalg { // Linear operations //---------------------------------------------------------------------------- template > -std::vector add(const std::vector& lhs, const std::vector& rhs) { +std::vector add(const std::vector &lhs, const std::vector &rhs) { if (lhs.size() != rhs.size()) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -43,7 +43,7 @@ std::vector add(const std::vector& lhs, const std::vector& rhs) { } template > -std::vector& iadd(std::vector& lhs, const std::vector& rhs) { +std::vector &iadd(std::vector &lhs, const std::vector &rhs) { if (lhs.size() != rhs.size()) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -53,7 +53,7 @@ std::vector& iadd(std::vector& lhs, const std::vector& rhs) { } template > -std::vector sub(const std::vector& lhs, const std::vector& rhs) { +std::vector sub(const std::vector &lhs, const std::vector &rhs) { if (lhs.size() != rhs.size()) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -65,7 +65,7 @@ std::vector sub(const std::vector& lhs, const std::vector& rhs) { } template > -std::vector& isub(std::vector& lhs, const std::vector& rhs) { +std::vector &isub(std::vector &lhs, const std::vector &rhs) { if (lhs.size() != rhs.size()) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -79,7 +79,7 @@ std::vector& isub(std::vector& lhs, const std::vector& rhs) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -std::vector add(const std::vector& data, const Scalar& val) { +std::vector add(const std::vector &data, const Scalar &val) { std::vector result; result.reserve(data.size()); std::transform(data.begin(), data.end(), std::back_inserter(result), @@ -89,7 +89,7 @@ std::vector add(const std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector& iadd(std::vector& data, const Scalar& val) { +std::vector &iadd(std::vector &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::plus(), std::placeholders::_1, val)); return data; @@ -97,7 +97,7 @@ std::vector& iadd(std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector sub(const std::vector& data, const Scalar& val) { +std::vector sub(const std::vector &data, const Scalar &val) { std::vector result; result.reserve(data.size()); std::transform(data.begin(), data.end(), std::back_inserter(result), @@ -107,7 +107,7 @@ std::vector sub(const std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector& isub(std::vector& data, const Scalar& val) { +std::vector &isub(std::vector &data, const Scalar &val) { std::transform(data.begin(), data.end(), data.begin(), std::bind(std::minus(), std::placeholders::_1, val)); return data; @@ -118,7 +118,7 @@ std::vector& isub(std::vector& data, const Scalar& val) { //---------------------------------------------------------------------------- template , typename = enable_if_numeric_t> -std::vector mul(const std::vector& data, const Scalar& val) { +std::vector mul(const std::vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -131,7 +131,7 @@ std::vector mul(const std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector& imul(std::vector& data, const Scalar& val) { +std::vector &imul(std::vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -142,7 +142,7 @@ std::vector& imul(std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector div(const std::vector& data, const Scalar& val) { +std::vector div(const std::vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -155,7 +155,7 @@ std::vector div(const std::vector& data, const Scalar& val) { template , typename = enable_if_numeric_t> -std::vector& idiv(std::vector& data, const Scalar& val) { +std::vector &idiv(std::vector &data, const Scalar &val) { if (almost_equal(val, 1)) { return data; } @@ -165,8 +165,8 @@ std::vector& idiv(std::vector& data, const Scalar& val) { } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/matrix_utils.hpp b/src/framework/linalg/matrix_utils.hpp old mode 100755 new mode 100644 index bd6903dde4..d557abd898 --- a/src/framework/linalg/matrix_utils.hpp +++ b/src/framework/linalg/matrix_utils.hpp @@ -16,7 +16,7 @@ #define _aer_framework_linalg_matrix_utils_hpp_ #include "framework/linalg/matrix_utils/matrix_defs.hpp" -#include "framework/linalg/matrix_utils/vmatrix_defs.hpp" #include "framework/linalg/matrix_utils/smatrix_defs.hpp" +#include "framework/linalg/matrix_utils/vmatrix_defs.hpp" #endif \ No newline at end of file diff --git a/src/framework/linalg/matrix_utils/matrix_defs.hpp b/src/framework/linalg/matrix_utils/matrix_defs.hpp old mode 100755 new mode 100644 index 9e82e9dd4d..db67b95163 --- a/src/framework/linalg/matrix_utils/matrix_defs.hpp +++ b/src/framework/linalg/matrix_utils/matrix_defs.hpp @@ -33,18 +33,18 @@ namespace Linalg { class Matrix { public: // Single-qubit gates - const static cmatrix_t I; // name: "id" - const static cmatrix_t X; // name: "x" - const static cmatrix_t Y; // name: "y" - const static cmatrix_t Z; // name: "z" - const static cmatrix_t H; // name: "h" - const static cmatrix_t S; // name: "s" - const static cmatrix_t SDG; // name: "sdg" - const static cmatrix_t T; // name: "t" - const static cmatrix_t TDG; // name: "tdg" - const static cmatrix_t SX; // name: "sx" - const static cmatrix_t SXDG;// name: "sxdg" - const static cmatrix_t X90; // name: "x90" + const static cmatrix_t I; // name: "id" + const static cmatrix_t X; // name: "x" + const static cmatrix_t Y; // name: "y" + const static cmatrix_t Z; // name: "z" + const static cmatrix_t H; // name: "h" + const static cmatrix_t S; // name: "s" + const static cmatrix_t SDG; // name: "sdg" + const static cmatrix_t T; // name: "t" + const static cmatrix_t TDG; // name: "tdg" + const static cmatrix_t SX; // name: "sx" + const static cmatrix_t SXDG; // name: "sxdg" + const static cmatrix_t X90; // name: "x90" // Two-qubit gates const static cmatrix_t CX; // name: "cx" @@ -92,8 +92,10 @@ class Matrix { static cmatrix_t u3(complex_t theta, complex_t phi, complex_t lam) { return u3(std::real(theta), std::real(phi), std::real(lam)); }; - static cmatrix_t u4(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return u4(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cmatrix_t u4(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return u4(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); }; static cmatrix_t r(complex_t theta, complex_t phi) { return r(std::real(theta), std::real(phi)); @@ -106,11 +108,17 @@ class Matrix { static cmatrix_t rzz(complex_t theta) { return rzz(std::real(theta)); } static cmatrix_t rzx(complex_t theta) { return rzx(std::real(theta)); } static cmatrix_t phase(complex_t theta) { return phase(std::real(theta)); } - static cmatrix_t phase_diag(complex_t theta) { return phase_diag(std::real(theta)); } + static cmatrix_t phase_diag(complex_t theta) { + return phase_diag(std::real(theta)); + } static cmatrix_t cphase(complex_t theta) { return cphase(std::real(theta)); } - static cmatrix_t cphase_diag(complex_t theta) { return cphase_diag(std::real(theta)); } - static cmatrix_t cu(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return cu(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cmatrix_t cphase_diag(complex_t theta) { + return cphase_diag(std::real(theta)); + } + static cmatrix_t cu(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return cu(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); } // Return the matrix for a named matrix string @@ -183,7 +191,7 @@ const cmatrix_t Matrix::CY = {{0, 0}, {0, 0}, {0, 0}, {0, -1}}, {{0, 0}, {0, 0}, {1, 0}, {0, 0}}, {{0, 0}, {0, 1}, {0, 0}, {0, 0}}}); - + const cmatrix_t Matrix::CZ = Utils::make_matrix({{{1, 0}, {0, 0}, {0, 0}, {0, 0}}, {{0, 0}, {1, 0}, {0, 0}, {0, 0}}, @@ -196,20 +204,20 @@ const cmatrix_t Matrix::SWAP = {{0, 0}, {1, 0}, {0, 0}, {0, 0}}, {{0, 0}, {0, 0}, {0, 0}, {1, 0}}}); -const cmatrix_t Matrix::ECR = - Utils::make_matrix({{{0, 0}, {1. / std::sqrt(2.), 0}, {0, 0}, {0, 1. / std::sqrt(2.)}}, - {{1. / std::sqrt(2.), 0}, {0, 0}, {0, -1. / std::sqrt(2.)}, {0, 0}}, - {{0, 0}, {0, 1. / std::sqrt(2.)}, {0, 0}, {1. / std::sqrt(2.), 0}}, - {{0, -1. / std::sqrt(2.)}, {0, 0}, {1. / std::sqrt(2.), 0}, {0, 0}}}); +const cmatrix_t Matrix::ECR = Utils::make_matrix( + {{{0, 0}, {1. / std::sqrt(2.), 0}, {0, 0}, {0, 1. / std::sqrt(2.)}}, + {{1. / std::sqrt(2.), 0}, {0, 0}, {0, -1. / std::sqrt(2.)}, {0, 0}}, + {{0, 0}, {0, 1. / std::sqrt(2.)}, {0, 0}, {1. / std::sqrt(2.), 0}}, + {{0, -1. / std::sqrt(2.)}, {0, 0}, {1. / std::sqrt(2.), 0}, {0, 0}}}); // Lookup table const stringmap_t Matrix::label_map_ = { - {"id", &Matrix::I}, {"x", &Matrix::X}, {"y", &Matrix::Y}, - {"z", &Matrix::Z}, {"h", &Matrix::H}, {"s", &Matrix::S}, - {"sdg", &Matrix::SDG}, {"t", &Matrix::T}, {"tdg", &Matrix::TDG}, - {"x90", &Matrix::X90}, {"cx", &Matrix::CX}, {"cy", &Matrix::CY}, - {"cz", &Matrix::CZ}, {"swap", &Matrix::SWAP}, {"sx", &Matrix::SX}, - {"sxdg", &Matrix::SXDG}, {"delay", &Matrix::I}, {"ecr", &Matrix::ECR}}; + {"id", &Matrix::I}, {"x", &Matrix::X}, {"y", &Matrix::Y}, + {"z", &Matrix::Z}, {"h", &Matrix::H}, {"s", &Matrix::S}, + {"sdg", &Matrix::SDG}, {"t", &Matrix::T}, {"tdg", &Matrix::TDG}, + {"x90", &Matrix::X90}, {"cx", &Matrix::CX}, {"cy", &Matrix::CY}, + {"cz", &Matrix::CZ}, {"swap", &Matrix::SWAP}, {"sx", &Matrix::SX}, + {"sxdg", &Matrix::SXDG}, {"delay", &Matrix::I}, {"ecr", &Matrix::ECR}}; cmatrix_t Matrix::identity(size_t dim) { cmatrix_t mat(dim, dim); @@ -218,9 +226,7 @@ cmatrix_t Matrix::identity(size_t dim) { return mat; } -cmatrix_t Matrix::u1(double lambda) { - return phase(lambda); -} +cmatrix_t Matrix::u1(double lambda) { return phase(lambda); } cmatrix_t Matrix::u2(double phi, double lambda) { cmatrix_t mat(2, 2); diff --git a/src/framework/linalg/matrix_utils/smatrix_defs.hpp b/src/framework/linalg/matrix_utils/smatrix_defs.hpp old mode 100755 new mode 100644 index 83598fe21d..0ecaf1bde3 --- a/src/framework/linalg/matrix_utils/smatrix_defs.hpp +++ b/src/framework/linalg/matrix_utils/smatrix_defs.hpp @@ -34,18 +34,18 @@ namespace Linalg { class SMatrix { public: // Single-qubit gates - const static cmatrix_t I; // name: "id" - const static cmatrix_t X; // name: "x" - const static cmatrix_t Y; // name: "y" - const static cmatrix_t Z; // name: "z" - const static cmatrix_t H; // name: "h" - const static cmatrix_t S; // name: "s" - const static cmatrix_t SDG; // name: "sdg" - const static cmatrix_t T; // name: "t" - const static cmatrix_t TDG; // name: "tdg" - const static cmatrix_t SX; // name: "sx" - const static cmatrix_t SXDG;// name: "sxdg" - const static cmatrix_t X90; // name: "x90" + const static cmatrix_t I; // name: "id" + const static cmatrix_t X; // name: "x" + const static cmatrix_t Y; // name: "y" + const static cmatrix_t Z; // name: "z" + const static cmatrix_t H; // name: "h" + const static cmatrix_t S; // name: "s" + const static cmatrix_t SDG; // name: "sdg" + const static cmatrix_t T; // name: "t" + const static cmatrix_t TDG; // name: "tdg" + const static cmatrix_t SX; // name: "sx" + const static cmatrix_t SXDG; // name: "sxdg" + const static cmatrix_t X90; // name: "x90" // Two-qubit gates const static cmatrix_t CX; // name: "cx" @@ -93,8 +93,10 @@ class SMatrix { static cmatrix_t u3(complex_t theta, complex_t phi, complex_t lam) { return u3(std::real(theta), std::real(phi), std::real(lam)); }; - static cmatrix_t u4(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return u4(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cmatrix_t u4(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return u4(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); } static cmatrix_t r(complex_t theta, complex_t phi) { return r(std::real(theta), std::real(phi)); @@ -107,11 +109,17 @@ class SMatrix { static cmatrix_t rzz(complex_t theta) { return rzz(std::real(theta)); } static cmatrix_t rzx(complex_t theta) { return rzx(std::real(theta)); } static cmatrix_t phase(complex_t theta) { return phase(std::real(theta)); } - static cmatrix_t phase_diag(complex_t theta) { return phase_diag(std::real(theta)); } + static cmatrix_t phase_diag(complex_t theta) { + return phase_diag(std::real(theta)); + } static cmatrix_t cphase(complex_t theta) { return cphase(std::real(theta)); } - static cmatrix_t cphase_diag(complex_t theta) { return cphase_diag(std::real(theta)); } - static cmatrix_t cu(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return cu(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cmatrix_t cphase_diag(complex_t theta) { + return cphase_diag(std::real(theta)); + } + static cmatrix_t cu(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return cu(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); } // Return superoperator matrix for reset instruction @@ -174,22 +182,18 @@ const cmatrix_t SMatrix::SWAP = Utils::unitary_superop(Matrix::SWAP); const cmatrix_t SMatrix::ECR = Utils::unitary_superop(Matrix::ECR); - - // Lookup table const stringmap_t SMatrix::label_map_ = { - {"id", &SMatrix::I}, {"x", &SMatrix::X}, {"y", &SMatrix::Y}, - {"z", &SMatrix::Z}, {"h", &SMatrix::H}, {"s", &SMatrix::S}, - {"sdg", &SMatrix::SDG}, {"t", &SMatrix::T}, {"tdg", &SMatrix::TDG}, - {"x90", &SMatrix::X90}, {"cx", &SMatrix::CX}, {"cy", &SMatrix::CY}, - {"cz", &SMatrix::CZ}, {"swap", &SMatrix::SWAP}, {"sx", &SMatrix::SX}, - {"sxdg", &SMatrix::SXDG}, {"delay", &SMatrix::I}, {"ecr", &SMatrix::ECR}}; + {"id", &SMatrix::I}, {"x", &SMatrix::X}, {"y", &SMatrix::Y}, + {"z", &SMatrix::Z}, {"h", &SMatrix::H}, {"s", &SMatrix::S}, + {"sdg", &SMatrix::SDG}, {"t", &SMatrix::T}, {"tdg", &SMatrix::TDG}, + {"x90", &SMatrix::X90}, {"cx", &SMatrix::CX}, {"cy", &SMatrix::CY}, + {"cz", &SMatrix::CZ}, {"swap", &SMatrix::SWAP}, {"sx", &SMatrix::SX}, + {"sxdg", &SMatrix::SXDG}, {"delay", &SMatrix::I}, {"ecr", &SMatrix::ECR}}; cmatrix_t SMatrix::identity(size_t dim) { return Matrix::identity(dim * dim); } -cmatrix_t SMatrix::u1(double lambda) { - return phase(lambda); -} +cmatrix_t SMatrix::u1(double lambda) { return phase(lambda); } cmatrix_t SMatrix::u2(double phi, double lambda) { return Utils::tensor_product(Matrix::u2(-phi, -lambda), diff --git a/src/framework/linalg/matrix_utils/vmatrix_defs.hpp b/src/framework/linalg/matrix_utils/vmatrix_defs.hpp old mode 100755 new mode 100644 index 24248de12c..3c5c3ab513 --- a/src/framework/linalg/matrix_utils/vmatrix_defs.hpp +++ b/src/framework/linalg/matrix_utils/vmatrix_defs.hpp @@ -34,18 +34,18 @@ namespace Linalg { class VMatrix { public: // Single-qubit gates - const static cvector_t I; // name: "id" - const static cvector_t X; // name: "x" - const static cvector_t Y; // name: "y" - const static cvector_t Z; // name: "z" - const static cvector_t H; // name: "h" - const static cvector_t S; // name: "s" - const static cvector_t SDG; // name: "sdg" - const static cvector_t T; // name: "t" - const static cvector_t TDG; // name: "tdg" - const static cvector_t SX; // name: "sx" - const static cvector_t SXDG;// name: "sxdg" - const static cvector_t X90; // name: "x90" + const static cvector_t I; // name: "id" + const static cvector_t X; // name: "x" + const static cvector_t Y; // name: "y" + const static cvector_t Z; // name: "z" + const static cvector_t H; // name: "h" + const static cvector_t S; // name: "s" + const static cvector_t SDG; // name: "sdg" + const static cvector_t T; // name: "t" + const static cvector_t TDG; // name: "tdg" + const static cvector_t SX; // name: "sx" + const static cvector_t SXDG; // name: "sxdg" + const static cvector_t X90; // name: "x90" // Two-qubit gates const static cvector_t CX; // name: "cx" @@ -74,7 +74,7 @@ class VMatrix { static cvector_t rxx(double theta); static cvector_t ryy(double theta); static cvector_t rzz(double theta); - static cvector_t rzx(double theta); // rotation around Tensor(X, Z) + static cvector_t rzx(double theta); // rotation around Tensor(X, Z) static cvector_t rzz_diag(double theta); // return the matrix diagonal // Phase Gates @@ -95,8 +95,10 @@ class VMatrix { static cvector_t u3(complex_t theta, complex_t phi, complex_t lam) { return u3(std::real(theta), std::real(phi), std::real(lam)); }; - static cvector_t u4(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return u4(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cvector_t u4(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return u4(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); }; static cvector_t r(complex_t theta, complex_t phi) { return r(std::real(theta), std::real(phi)); @@ -104,18 +106,28 @@ class VMatrix { static cvector_t rx(complex_t theta) { return rx(std::real(theta)); } static cvector_t ry(complex_t theta) { return ry(std::real(theta)); } static cvector_t rz(complex_t theta) { return rz(std::real(theta)); } - static cvector_t rz_diag(complex_t theta) { return rz_diag(std::real(theta)); } + static cvector_t rz_diag(complex_t theta) { + return rz_diag(std::real(theta)); + } static cvector_t rxx(complex_t theta) { return rxx(std::real(theta)); } static cvector_t ryy(complex_t theta) { return ryy(std::real(theta)); } static cvector_t rzz(complex_t theta) { return rzz(std::real(theta)); } - static cvector_t rzz_diag(complex_t theta) { return rzz_diag(std::real(theta)); } + static cvector_t rzz_diag(complex_t theta) { + return rzz_diag(std::real(theta)); + } static cvector_t rzx(complex_t theta) { return rzx(std::real(theta)); } static cvector_t phase(complex_t theta) { return phase(std::real(theta)); } - static cvector_t phase_diag(complex_t theta) { return phase_diag(std::real(theta)); } + static cvector_t phase_diag(complex_t theta) { + return phase_diag(std::real(theta)); + } static cvector_t cphase(complex_t theta) { return cphase(std::real(theta)); } - static cvector_t cphase_diag(complex_t theta) { return cphase_diag(std::real(theta)); } - static cvector_t cu(complex_t theta, complex_t phi, complex_t lam, complex_t gamma) { - return cu(std::real(theta), std::real(phi), std::real(lam), std::real(gamma)); + static cvector_t cphase_diag(complex_t theta) { + return cphase_diag(std::real(theta)); + } + static cvector_t cu(complex_t theta, complex_t phi, complex_t lam, + complex_t gamma) { + return cu(std::real(theta), std::real(phi), std::real(lam), + std::real(gamma)); } // Return the matrix for a named matrix string @@ -173,15 +185,14 @@ const cvector_t VMatrix::SWAP = Utils::vectorize_matrix(Matrix::SWAP); const cvector_t VMatrix::ECR = Utils::vectorize_matrix(Matrix::ECR); - // Lookup table const stringmap_t VMatrix::label_map_ = { - {"id", &VMatrix::I}, {"x", &VMatrix::X}, {"y", &VMatrix::Y}, - {"z", &VMatrix::Z}, {"h", &VMatrix::H}, {"s", &VMatrix::S}, - {"sdg", &VMatrix::SDG}, {"t", &VMatrix::T}, {"tdg", &VMatrix::TDG}, - {"x90", &VMatrix::X90}, {"cx", &VMatrix::CX}, {"cy", &VMatrix::CY}, - {"cz", &VMatrix::CZ}, {"swap", &VMatrix::SWAP}, {"sx", &VMatrix::SX}, - {"sxdg", &VMatrix::SXDG}, {"delay", &VMatrix::I}, {"ecr", &VMatrix::ECR}}; + {"id", &VMatrix::I}, {"x", &VMatrix::X}, {"y", &VMatrix::Y}, + {"z", &VMatrix::Z}, {"h", &VMatrix::H}, {"s", &VMatrix::S}, + {"sdg", &VMatrix::SDG}, {"t", &VMatrix::T}, {"tdg", &VMatrix::TDG}, + {"x90", &VMatrix::X90}, {"cx", &VMatrix::CX}, {"cy", &VMatrix::CY}, + {"cz", &VMatrix::CZ}, {"swap", &VMatrix::SWAP}, {"sx", &VMatrix::SX}, + {"sxdg", &VMatrix::SXDG}, {"delay", &VMatrix::I}, {"ecr", &VMatrix::ECR}}; cvector_t VMatrix::identity(size_t dim) { cvector_t mat(dim * dim); @@ -190,9 +201,7 @@ cvector_t VMatrix::identity(size_t dim) { return mat; } -cvector_t VMatrix::u1(double lambda) { - return phase(lambda); -} +cvector_t VMatrix::u1(double lambda) { return phase(lambda); } cvector_t VMatrix::u2(double phi, double lambda) { cvector_t mat(2 * 2); @@ -316,7 +325,7 @@ cvector_t VMatrix::rzz_diag(double theta) { const complex_t i(0., 1.); const complex_t exp_p = std::exp(i * 0.5 * theta); const complex_t exp_m = std::exp(-i * 0.5 * theta); - return cvector_t({exp_m, exp_p, exp_p, exp_m}); + return cvector_t({exp_m, exp_p, exp_p, exp_m}); } cvector_t VMatrix::rzx(double theta) { diff --git a/src/framework/linalg/square.hpp b/src/framework/linalg/square.hpp old mode 100755 new mode 100644 index 6baae7054f..4c2b28d180 --- a/src/framework/linalg/square.hpp +++ b/src/framework/linalg/square.hpp @@ -22,10 +22,10 @@ #include #include "framework/json.hpp" -#include "framework/matrix.hpp" +#include "framework/linalg/enable_if_numeric.hpp" #include "framework/linalg/vector.hpp" +#include "framework/matrix.hpp" #include "framework/types.hpp" -#include "framework/linalg/enable_if_numeric.hpp" namespace AER { namespace Linalg { @@ -39,14 +39,14 @@ namespace Linalg { // Return entrywise square of a vector template > -std::array square(const std::array& data) { +std::array square(const std::array &data) { std::array result = data; return isquare(result); } // Return inplace entrywise square of a vector template > -std::array& isquare(std::array& data) { +std::array &isquare(std::array &data) { std::transform(data.begin(), data.end(), data.begin(), data.begin(), std::multiplies()); return data; @@ -58,17 +58,17 @@ std::array& isquare(std::array& data) { // Return entrywise square of a vector template > -std::vector square(const std::vector& data) { +std::vector square(const std::vector &data) { std::vector result; result.reserve(data.size()); - std::transform(data.begin(), data.end(), data.begin(), std::back_inserter(result), - std::multiplies()); + std::transform(data.begin(), data.end(), data.begin(), + std::back_inserter(result), std::multiplies()); return result; } // Return inplace entrywise square of a vector template > -std::vector& isquare(std::vector& data) { +std::vector &isquare(std::vector &data) { std::transform(data.begin(), data.end(), data.begin(), data.begin(), std::multiplies()); return data; @@ -78,18 +78,20 @@ std::vector& isquare(std::vector& data) { // Entrywise square of std::map //---------------------------------------------------------------------------- -template > -std::map square(const std::map& data) { +template > +std::map square(const std::map &data) { std::map result; - for (const auto& pair : data) { + for (const auto &pair : data) { result[pair.first] = pair.second * pair.second; } return result; } -template > -std::map& isquare(std::map& data) { - for (auto& pair : data) { +template > +std::map &isquare(std::map &data) { + for (auto &pair : data) { pair.second *= pair.second; } return data; @@ -99,19 +101,22 @@ std::map& isquare(std::map& data) { // Entrywise square of std::unordered_map //---------------------------------------------------------------------------- -template > -std::unordered_map square( - const std::unordered_map& data) { +template > +std::unordered_map +square(const std::unordered_map &data) { std::unordered_map result; - for (const auto& pair : data) { + for (const auto &pair : data) { result[pair.first] = pair.second * pair.second; } return result; } -template > -std::unordered_map& isquare(std::unordered_map& data) { - for (auto& pair : data) { +template > +std::unordered_map & +isquare(std::unordered_map &data) { + for (auto &pair : data) { pair.second *= pair.second; } return data; @@ -122,7 +127,7 @@ std::unordered_map& isquare(std::unordered_map> -matrix& isquare(matrix& data) { +matrix &isquare(matrix &data) { for (size_t j = 0; j < data.size(); j++) { data[j] *= data[j]; } @@ -130,7 +135,7 @@ matrix& isquare(matrix& data) { } template > -matrix square(const matrix& data) { +matrix square(const matrix &data) { matrix result = data; return isquare(result); } @@ -140,16 +145,17 @@ matrix square(const matrix& data) { //---------------------------------------------------------------------------- template > -Vector& isquare(Vector& vec) { - std::for_each(vec.data(), vec.data() + vec.size(), [](T&val) { val *= val; }); +Vector &isquare(Vector &vec) { + std::for_each(vec.data(), vec.data() + vec.size(), + [](T &val) { val *= val; }); return vec; } template > -Vector square(const Vector& vec) { +Vector square(const Vector &vec) { Vector ret(vec.size(), false); std::transform(vec.data(), vec.data() + vec.size(), ret.data(), - [](const T&val) { return val * val; }); + [](const T &val) { return val * val; }); return ret; } @@ -157,7 +163,7 @@ Vector square(const Vector& vec) { // Entrywise square of JSON //---------------------------------------------------------------------------- -inline json_t& isquare(json_t& data) { +inline json_t &isquare(json_t &data) { // Terminating case if (data.is_number()) { double val = data; @@ -170,7 +176,7 @@ inline json_t& isquare(json_t& data) { isquare(data[pos]); } return data; - } + } if (data.is_object()) { for (auto it = data.begin(); it != data.end(); ++it) { isquare(data[it.key()]); @@ -180,7 +186,7 @@ inline json_t& isquare(json_t& data) { throw std::invalid_argument("Input JSONs cannot be squared."); } -inline json_t square(const json_t& data) { +inline json_t square(const json_t &data) { json_t result = data; return isquare(result); } @@ -190,19 +196,19 @@ inline json_t square(const json_t& data) { //---------------------------------------------------------------------------- template -T square(const T& data) { +T square(const T &data) { return data * data; } template -T& isquare(T& data) { +T &isquare(T &data) { data *= data; return data; } //------------------------------------------------------------------------------ -} // end namespace Linalg +} // end namespace Linalg //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif \ No newline at end of file diff --git a/src/framework/linalg/vector.hpp b/src/framework/linalg/vector.hpp old mode 100755 new mode 100644 index 6098943a52..985b64f5b3 --- a/src/framework/linalg/vector.hpp +++ b/src/framework/linalg/vector.hpp @@ -29,15 +29,18 @@ namespace AER { -template T *malloc_data(size_t size) { +template +T *malloc_data(size_t size) { return reinterpret_cast(malloc(sizeof(T) * size)); } -template T *calloc_data(size_t size) { +template +T *calloc_data(size_t size) { return reinterpret_cast(calloc(size, sizeof(T))); } -template class Vector { +template +class Vector { public: //----------------------------------------------------------------------- @@ -60,8 +63,7 @@ template class Vector { Vector(Vector &&other) noexcept; // Destructor - virtual ~Vector() { - free(data_); } + virtual ~Vector() { free(data_); } //----------------------------------------------------------------------- // Assignment @@ -74,7 +76,8 @@ template class Vector { Vector &operator=(Vector &&other) noexcept; // Copy and cast assignment - template Vector &operator=(const Vector &other); + template + Vector &operator=(const Vector &other); //----------------------------------------------------------------------- // Buffer conversion @@ -192,7 +195,8 @@ template class Vector { template Vector::Vector(size_t sz, bool fill) - : size_(sz), data_((fill) ? calloc_data(size_) : malloc_data(size_)) {} + : size_(sz), data_((fill) ? calloc_data(size_) : malloc_data(size_)) { +} template Vector::Vector(const Vector &other) @@ -211,7 +215,8 @@ Vector::Vector(Vector &&other) noexcept // Assignment //----------------------------------------------------------------------- -template Vector &Vector::operator=(Vector &&other) noexcept { +template +Vector &Vector::operator=(Vector &&other) noexcept { free(data_); size_ = other.size_; data_ = other.data_; @@ -220,7 +225,8 @@ template Vector &Vector::operator=(Vector &&other) noexcept { return *this; } -template Vector &Vector::operator=(const Vector &other) { +template +Vector &Vector::operator=(const Vector &other) { if (size_ != other.size_) { free(data_); size_ = other.size_; @@ -265,13 +271,15 @@ Vector Vector::move_from_buffer(size_t sz, T *buffer) { return ret; } -template T *Vector::copy_to_buffer() const { +template +T *Vector::copy_to_buffer() const { T *buffer = malloc_data(size_); std::copy(data_, data_ + size_, buffer); return buffer; } -template T *Vector::move_to_buffer() { +template +T *Vector::move_to_buffer() { T *buffer = data_; data_ = nullptr; size_ = 0; @@ -282,17 +290,20 @@ template T *Vector::move_to_buffer() { // Operations //----------------------------------------------------------------------- -template void Vector::clear() noexcept { +template +void Vector::clear() noexcept { free(data_); size_ = 0; } -template void Vector::swap(Vector &other) { +template +void Vector::swap(Vector &other) { std::swap(size_, other.size_); std::swap(data_, other.data_); } -template void Vector::resize(size_t sz) { +template +void Vector::resize(size_t sz) { if (size_ == sz) return; T *tmp = calloc_data(sz); @@ -302,7 +313,8 @@ template void Vector::resize(size_t sz) { data_ = tmp; } -template void Vector::fill(const T &val) { +template +void Vector::fill(const T &val) { std::fill(data_, data_ + size_, val); } @@ -323,7 +335,8 @@ Vector Vector::operator+(const Vector &other) const { return result; } -template Vector &Vector::operator+=(const Vector &other) { +template +Vector &Vector::operator+=(const Vector &other) { if (size_ != other.size_) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -345,7 +358,8 @@ Vector Vector::operator-(const Vector &other) const { return result; } -template Vector &Vector::operator-=(const Vector &other) { +template +Vector &Vector::operator-=(const Vector &other) { if (size_ != other.size_) { throw std::runtime_error("Cannot add two vectors of different sizes."); } @@ -354,7 +368,8 @@ template Vector &Vector::operator-=(const Vector &other) { return *this; } -template Vector Vector::operator*(const T &other) const { +template +Vector Vector::operator*(const T &other) const { Vector ret; ret.size_ = size_; ret.data_ = malloc_data(size_); @@ -363,13 +378,14 @@ template Vector Vector::operator*(const T &other) const { return ret; } -template Vector &Vector::operator*=(const T &other) { - std::for_each(data_, data_ + size_, - [&other](T &a) { a *= other; }); +template +Vector &Vector::operator*=(const T &other) { + std::for_each(data_, data_ + size_, [&other](T &a) { a *= other; }); return *this; } -template Vector Vector::operator/(const T &other) const { +template +Vector Vector::operator/(const T &other) const { Vector ret; ret.size_ = size_; ret.data_ = malloc_data(size_); @@ -378,12 +394,12 @@ template Vector Vector::operator/(const T &other) const { return ret; } -template Vector &Vector::operator/=(const T &other) { +template +Vector &Vector::operator/=(const T &other) { std::for_each(data_, data_ + size_, [&other](T &a) { a /= other; }); return *this; } - //------------------------------------------------------------------------------ } // end Namespace AER //------------------------------------------------------------------------------ diff --git a/src/framework/linalg/vector_json.hpp b/src/framework/linalg/vector_json.hpp old mode 100755 new mode 100644 index 8f3f8d2a67..3a03339c61 --- a/src/framework/linalg/vector_json.hpp +++ b/src/framework/linalg/vector_json.hpp @@ -24,23 +24,23 @@ namespace AER { // Implementation: JSON Conversion //------------------------------------------------------------------------------ - -template void to_json(nlohmann::json &js, const Vector &vec) { +template +void to_json(nlohmann::json &js, const Vector &vec) { js = nlohmann::json(); for (size_t i = 0; i < vec.size(); i++) { js.push_back(vec[i]); } } - -template void from_json(const nlohmann::json &js, Vector &vec) { +template +void from_json(const nlohmann::json &js, Vector &vec) { // Check JSON is an array - if(!js.is_array()) { + if (!js.is_array()) { throw std::invalid_argument( std::string("JSON: invalid Vector (not array).")); } // Check if JSON is empty - if(js.empty()) { + if (js.empty()) { return; } diff --git a/src/framework/matrix.hpp b/src/framework/matrix.hpp old mode 100755 new mode 100644 index bc22bc9306..6a0a0ffe06 --- a/src/framework/matrix.hpp +++ b/src/framework/matrix.hpp @@ -31,10 +31,10 @@ Multiplication is done with the C wrapper of the fortran blas library. #ifndef _aer_framework_matrix_hpp #define _aer_framework_matrix_hpp +#include #include #include #include -#include #include "framework/blas_protos.hpp" #include "framework/linalg/enable_if_numeric.hpp" @@ -46,16 +46,15 @@ Multiplication is done with the C wrapper of the fortran blas library. ******************************************************************************/ template -T* malloc_array(size_t size) { - return reinterpret_cast(malloc(sizeof(T) * size)); +T *malloc_array(size_t size) { + return reinterpret_cast(malloc(sizeof(T) * size)); } template -T* calloc_array(size_t size) { - return reinterpret_cast(calloc(size, sizeof(T))); +T *calloc_array(size_t size) { + return reinterpret_cast(calloc(size, sizeof(T))); } - template // define a class template class matrix { // friend functions get to use the private variables of the class as well as @@ -136,13 +135,13 @@ class matrix { matrix(size_t rows, size_t cols, bool fill = true); // Construct a matrix of specified size and an array - matrix(size_t rows, size_t cols, T* data); + matrix(size_t rows, size_t cols, T *data); // Copy construct a matrix matrix(const matrix &other); // Move construct a matrix - matrix(matrix&& other) noexcept; + matrix(matrix &&other) noexcept; // Destructor virtual ~matrix() { free(data_); } @@ -167,33 +166,33 @@ class matrix { // Copy construct a matrix from C-array buffer // The buffer should have size = rows * cols. - static matrix copy_from_buffer(size_t rows, size_t cols, const T* buffer); + static matrix copy_from_buffer(size_t rows, size_t cols, const T *buffer); // Move construct a matrix from C-array buffer // The buffer should have size = rows * cols. - static matrix move_from_buffer(size_t rows, size_t cols, T* buffer); + static matrix move_from_buffer(size_t rows, size_t cols, T *buffer); // Copy matrix to a new C-array - T* copy_to_buffer() const; + T *copy_to_buffer() const; // Move matrix to a C-array - T* move_to_buffer(); + T *move_to_buffer(); //----------------------------------------------------------------------- // Element access //----------------------------------------------------------------------- // Addressing elements by vector representation - T& operator[](size_t element); - const T& operator[](size_t element) const; + T &operator[](size_t element); + const T &operator[](size_t element) const; // Addressing elements by matrix representation - T& operator()(size_t row, size_t col); - const T& operator()(size_t row, size_t col) const; + T &operator()(size_t row, size_t col); + const T &operator()(size_t row, size_t col) const; // Access the array data pointer - const T* data() const noexcept { return data_; } - T* data() noexcept { return data_; } + const T *data() const noexcept { return data_; } + T *data() noexcept { return data_; } //----------------------------------------------------------------------- // Other methods @@ -209,7 +208,7 @@ class matrix { void clear(); // Fill with constant value - void fill(const T& val); + void fill(const T &val); // Resize the matrix and reset to zero if different size void initialize(size_t row, size_t col); @@ -245,7 +244,7 @@ class matrix { // to rows // the ptr to the vector containing the matrix - T* data_ = nullptr; + T *data_ = nullptr; }; /******************************************************************************* @@ -264,28 +263,28 @@ matrix::matrix(size_t rows, size_t cols, bool fill) data_((fill) ? calloc_array(size_) : malloc_array(size_)) {} template -matrix::matrix(const matrix &other) : matrix(other.rows_, other.cols_, false) { +matrix::matrix(const matrix &other) + : matrix(other.rows_, other.cols_, false) { std::copy(other.data_, other.data_ + other.size_, data_); } template -matrix::matrix(matrix&& other) noexcept - : rows_(other.rows_), cols_(other.cols_), size_(other.size_), LD_(rows_), - data_(other.data_) { +matrix::matrix(matrix &&other) noexcept + : rows_(other.rows_), cols_(other.cols_), size_(other.size_), LD_(rows_), + data_(other.data_) { other.data_ = nullptr; } template -matrix::matrix(size_t rows, size_t cols, T* data) - : rows_(rows), cols_(cols), size_(rows * cols), LD_(rows), - data_(data) {} +matrix::matrix(size_t rows, size_t cols, T *data) + : rows_(rows), cols_(cols), size_(rows * cols), LD_(rows), data_(data) {} //----------------------------------------------------------------------- // Assignment //----------------------------------------------------------------------- template -matrix& matrix::operator=(matrix&& other) noexcept { +matrix &matrix::operator=(matrix &&other) noexcept { free(data_); rows_ = other.rows_; cols_ = other.cols_; @@ -316,8 +315,7 @@ template template inline matrix &matrix::operator=(const matrix &other) { - if (rows_ != other.GetRows() || - cols_ != other.GetColumns()) { + if (rows_ != other.GetRows() || cols_ != other.GetColumns()) { free(data_); rows_ = other.GetRows(); cols_ = other.GetColumns(); @@ -336,7 +334,8 @@ inline matrix &matrix::operator=(const matrix &other) { //----------------------------------------------------------------------- template -matrix matrix::copy_from_buffer(size_t rows, size_t cols, const T* buffer) { +matrix matrix::copy_from_buffer(size_t rows, size_t cols, + const T *buffer) { matrix ret; ret.size_ = rows * cols; ret.rows_ = rows; @@ -348,7 +347,7 @@ matrix matrix::copy_from_buffer(size_t rows, size_t cols, const T* buffer) } template -matrix matrix::move_from_buffer(size_t rows, size_t cols, T* buffer) { +matrix matrix::move_from_buffer(size_t rows, size_t cols, T *buffer) { matrix ret; ret.size_ = rows * cols; ret.rows_ = rows; @@ -359,15 +358,15 @@ matrix matrix::move_from_buffer(size_t rows, size_t cols, T* buffer) { } template -T* matrix::copy_to_buffer() const { - T* buffer = malloc_array(size_); +T *matrix::copy_to_buffer() const { + T *buffer = malloc_array(size_); std::copy(data_, data_ + size_, buffer); return buffer; } template -T* matrix::move_to_buffer() { - T* buffer = data_; +T *matrix::move_to_buffer() { + T *buffer = data_; data_ = nullptr; size_ = 0; rows_ = 0; @@ -380,7 +379,7 @@ T* matrix::move_to_buffer() { //----------------------------------------------------------------------- template -T& matrix::operator[](size_t p) { +T &matrix::operator[](size_t p) { #ifdef DEBUG if (p >= size_) { std::cerr @@ -392,7 +391,7 @@ T& matrix::operator[](size_t p) { return data_[p]; } template -const T& matrix::operator[](size_t p) const { +const T &matrix::operator[](size_t p) const { #ifdef DEBUG if (p >= size_) { std::cerr << "Error: matrix class operator [] const: Matrix subscript out " @@ -405,7 +404,7 @@ const T& matrix::operator[](size_t p) const { } template -T& matrix::operator()(size_t i, size_t j) { +T &matrix::operator()(size_t i, size_t j) { #ifdef DEBUG if (i >= rows_ || j >= cols_) { std::cerr @@ -418,7 +417,7 @@ T& matrix::operator()(size_t i, size_t j) { } template -const T& matrix::operator()(size_t i, size_t j) const { +const T &matrix::operator()(size_t i, size_t j) const { #ifdef DEBUG if (i >= rows_ || j >= cols_) { std::cerr << "Error: matrix class operator () const: Matrices subscript " @@ -438,7 +437,8 @@ void matrix::clear() { free(data_); } -template inline void matrix::initialize(size_t rows, size_t cols) { +template +inline void matrix::initialize(size_t rows, size_t cols) { if (rows_ != rows || cols_ != cols) { free(data_); rows_ = rows; @@ -450,7 +450,7 @@ template inline void matrix::initialize(size_t rows, size_t cols) { } template -void matrix::fill(const T& val) { +void matrix::fill(const T &val) { std::fill(data_, data_ + size_, val); } @@ -473,52 +473,58 @@ void matrix::resize(size_t rows, size_t cols) { } // Addressing elements by row or column -template inline std::vector matrix::row_index(size_t row) const { +template +inline std::vector matrix::row_index(size_t row) const { #ifdef DEBUG if (row >= rows_) { - std::cerr << "Error: matrix class operator row_index out of bounds " - << row << " >= " << rows_ << std::endl; + std::cerr << "Error: matrix class operator row_index out of bounds " << row + << " >= " << rows_ << std::endl; exit(1); } #endif std::vector ret; ret.reserve(cols_); - for(size_t i = 0; i < cols_; i++) + for (size_t i = 0; i < cols_; i++) ret.emplace_back(data_[i * rows_ + row]); // Allow for Named Return Value Optimization (NRVO) by not using std::move return ret; } -template inline std::vector matrix::col_index(size_t col) const { +template +inline std::vector matrix::col_index(size_t col) const { #ifdef DEBUG if (col >= cols_) { - std::cerr << "Error: matrix class operator col_index out of bounds " - << col << " >= " << cols_ << std::endl; + std::cerr << "Error: matrix class operator col_index out of bounds " << col + << " >= " << cols_ << std::endl; exit(1); } #endif std::vector ret; ret.reserve(rows_); // we want the elements for all rows i..rows_ and column col - for(size_t i = 0; i < rows_; i++) + for (size_t i = 0; i < rows_; i++) ret.emplace_back(data_[col * rows_ + i]); // Allow for Named Return Value Optimization (NRVO) by not using std::move return ret; } -template inline size_t matrix::GetRows() const { +template +inline size_t matrix::GetRows() const { // returns the rows of the matrix return rows_; } -template inline size_t matrix::GetColumns() const { +template +inline size_t matrix::GetColumns() const { // returns the colums of the matrix return cols_; } -template inline size_t matrix::GetLD() const { +template +inline size_t matrix::GetLD() const { // returns the leading dimension return LD_; } -template inline matrix matrix::operator+(const matrix &A) { +template +inline matrix matrix::operator+(const matrix &A) { // overloads the + for matrix addition, can this be more efficient #ifdef DEBUG if (rows_ != A.rows_ || cols_ != A.cols_) { @@ -534,7 +540,8 @@ template inline matrix matrix::operator+(const matrix &A) { } return temp; } -template inline matrix matrix::operator-(const matrix &A) { +template +inline matrix matrix::operator-(const matrix &A) { // overloads the - for matrix substraction, can this be more efficient #ifdef DEBUG if (rows_ != A.rows_ || cols_ != A.cols_) { @@ -585,7 +592,8 @@ inline matrix matrix::operator-(const matrix &A) const { } return temp; } -template inline matrix &matrix::operator+=(const matrix &A) { +template +inline matrix &matrix::operator+=(const matrix &A) { // overloads the += for matrix addition and assignment, can this be more // efficient #ifdef DEBUG @@ -601,7 +609,8 @@ template inline matrix &matrix::operator+=(const matrix &A) { } return *this; } -template inline matrix &matrix::operator-=(const matrix &A) { +template +inline matrix &matrix::operator-=(const matrix &A) { // overloads the -= for matrix subtraction and assignement, can this be more // efficient #ifdef DEBUG @@ -686,8 +695,8 @@ inline matrix operator*(const matrix &A, // C-> alpha*op(A)*op(B) +beta C matrix C(A.rows_, B.cols_); double alpha = 1.0, beta = 0.0; - dgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + dgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, B.cols_, // A.cols_, 1.0, A.data_, A.LD_, B.data_, B.LD_, 0.0, C.data_, C.LD_); return C; @@ -698,8 +707,8 @@ inline matrix operator*(const matrix &A, const matrix &B) { // C-> alpha*op(A)*op(B) +beta C matrix C(A.rows_, B.cols_); float alpha = 1.0, beta = 0.0; - sgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + sgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_dgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, B.cols_, // A.cols_, 1.0, A.data_, A.LD_, B.data_, B.LD_, 0.0, C.data_, C.LD_); return C; @@ -712,8 +721,8 @@ operator*(const matrix> &A, // C-> alpha*op(A)*op(B) +beta C matrix> C(A.rows_, B.cols_); std::complex alpha = 1.0, beta = 0.0; - cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, B.cols_, // A.cols_, &alpha, A.data_, A.LD_, B.data_, B.LD_, &beta, C.data_, C.LD_); return C; @@ -726,8 +735,8 @@ operator*(const matrix> &A, // C-> alpha*op(A)*op(B) +beta C matrix> C(A.rows_, B.cols_); std::complex alpha = 1.0, beta = 0.0; - zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &B.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, B.cols_, // A.cols_, &alpha, A.data_, A.LD_, B.data_, B.LD_, &beta, C.data_, C.LD_); return C; @@ -740,8 +749,8 @@ operator*(const matrix &A, const matrix> &B) { matrix> C(A.rows_, B.cols_), Ac(A.rows_, A.cols_); Ac = A; std::complex alpha = 1.0, beta = 0.0; - cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &Ac.rows_, &B.cols_, &Ac.cols_, &alpha, Ac.data_, - &Ac.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &Ac.rows_, &B.cols_, &Ac.cols_, + &alpha, Ac.data_, &Ac.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, Ac.rows_, B.cols_, // Ac.cols_, &alpha, Ac.data_, Ac.LD_, B.data_, B.LD_, &beta, C.data_, C.LD_); return C; @@ -754,8 +763,8 @@ operator*(const matrix &A, const matrix> &B) { matrix> C(A.rows_, B.cols_), Ac(A.rows_, A.cols_); Ac = A; std::complex alpha = 1.0, beta = 0.0; - zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &Ac.rows_, &B.cols_, &Ac.cols_, &alpha, Ac.data_, - &Ac.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); + zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &Ac.rows_, &B.cols_, &Ac.cols_, + &alpha, Ac.data_, &Ac.LD_, B.data_, &B.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, Ac.rows_, B.cols_, // Ac.cols_, &alpha, Ac.data_, Ac.LD_, B.data_, B.LD_, &beta, C.data_, C.LD_); return C; @@ -768,8 +777,8 @@ operator*(const matrix> &A, const matrix &B) { matrix> C(A.rows_, B.cols_), Bc(B.rows_, B.cols_); Bc = B; std::complex alpha = 1.0, beta = 0.0; - cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &Bc.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, Bc.data_, &Bc.LD_, &beta, C.data_, &C.LD_); + cgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &Bc.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, Bc.data_, &Bc.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, Bc.cols_, // A.cols_, &alpha, A.data_, A.LD_, Bc.data_, Bc.LD_, &beta, C.data_, C.LD_); return C; @@ -782,8 +791,8 @@ operator*(const matrix> &A, const matrix &B) { matrix> C(A.rows_, B.cols_), Bc(B.rows_, B.cols_); Bc = B; std::complex alpha = 1.0, beta = 0.0; - zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &Bc.cols_, &A.cols_, &alpha, A.data_, - &A.LD_, Bc.data_, &Bc.LD_, &beta, C.data_, &C.LD_); + zgemm_(&AerBlas::Trans[0], &AerBlas::Trans[0], &A.rows_, &Bc.cols_, &A.cols_, + &alpha, A.data_, &A.LD_, Bc.data_, &Bc.LD_, &beta, C.data_, &C.LD_); // cblas_zgemm(CblasColMajor, CblasNoTrans, CblasNoTrans, A.rows_, Bc.cols_, // A.cols_, &alpha, A.data_, A.LD_, Bc.data_, Bc.LD_, &beta, C.data_, C.LD_); return C; @@ -796,8 +805,8 @@ inline std::vector operator*(const matrix &A, std::vector y(A.rows_); float alpha = 1.0, beta = 0.0; const size_t incx = 1, incy = 1; - sgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, x.data(), &incx, - &beta, y.data(), &incy); + sgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, + x.data(), &incx, &beta, y.data(), &incy); return y; } // Double-Precision Real @@ -807,8 +816,8 @@ inline std::vector operator*(const matrix &A, std::vector y(A.rows_); double alpha = 1.0, beta = 0.0; const size_t incx = 1, incy = 1; - dgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, x.data(), &incx, - &beta, y.data(), &incy); + dgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, + x.data(), &incx, &beta, y.data(), &incy); return y; } // Single-Precision Complex @@ -819,8 +828,8 @@ operator*(const matrix> &A, std::vector> y(A.rows_); std::complex alpha = 1.0, beta = 0.0; const size_t incx = 1, incy = 1; - cgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, x.data(), &incx, - &beta, y.data(), &incy); + cgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, + x.data(), &incx, &beta, y.data(), &incy); return y; } // Double-Precision Complex @@ -831,8 +840,8 @@ operator*(const matrix> &A, std::vector> y(A.rows_); std::complex alpha = 1.0, beta = 0.0; const size_t incx = 1, incy = 1; - zgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, x.data(), &incx, - &beta, y.data(), &incy); + zgemv_(&AerBlas::Trans[0], &A.rows_, &A.cols_, &alpha, A.data_, &A.LD_, + x.data(), &incx, &beta, y.data(), &incy); return y; } diff --git a/src/framework/noise_utils.hpp b/src/framework/noise_utils.hpp index eb3947fbd3..5694bd88ba 100644 --- a/src/framework/noise_utils.hpp +++ b/src/framework/noise_utils.hpp @@ -26,28 +26,30 @@ namespace Utils { // Transform a superoperator matrix to a Choi matrix template -matrix superop2choi(const matrix& superop, size_t dim); +matrix superop2choi(const matrix &superop, size_t dim); // Transform a superoperator matrix to a set of Kraus matrices template -std::vector> superop2kraus(const matrix& superop, size_t dim, double threshold = 1e-10); +std::vector> superop2kraus(const matrix &superop, size_t dim, + double threshold = 1e-10); // Transform a Choi matrix to a superoperator matrix template -matrix choi2superop(const matrix& choi, size_t dim); +matrix choi2superop(const matrix &choi, size_t dim); // Transform a Choi matrix to a set of Kraus matrices template std::vector>> -choi2kraus(const matrix>& choi, size_t dim, double threshold = 1e-10); +choi2kraus(const matrix> &choi, size_t dim, + double threshold = 1e-10); // Transform a set of Kraus matrices to a Choi matrix template -matrix kraus2choi(const std::vector>& kraus, size_t dim); +matrix kraus2choi(const std::vector> &kraus, size_t dim); // Transform a set of Kraus matrices to a superoperator matrix template -matrix kraus2superop(const std::vector>& kraus, size_t dim); +matrix kraus2superop(const std::vector> &kraus, size_t dim); // Reshuffle transformation // Transforms a matrix with dimension (d0 * d1, d2 * d3) @@ -55,15 +57,15 @@ matrix kraus2superop(const std::vector>& kraus, size_t dim); // by transposition of bipartite indices // M[(i, j), (k, l)] -> M[(i, j), (k, i)] template -matrix reshuffle(const matrix &mat, size_t d0, size_t d1, size_t d2, size_t d3); - +matrix reshuffle(const matrix &mat, size_t d0, size_t d1, size_t d2, + size_t d3); //========================================================================= // Implementations //========================================================================= template -matrix kraus2superop(const std::vector>& kraus, size_t dim) { +matrix kraus2superop(const std::vector> &kraus, size_t dim) { matrix superop(dim * dim, dim * dim); for (const auto mat : kraus) { superop += Utils::tensor_product(Utils::conjugate(mat), mat); @@ -72,28 +74,29 @@ matrix kraus2superop(const std::vector>& kraus, size_t dim) { } template -matrix kraus2choi(const std::vector>& kraus, size_t dim) { +matrix kraus2choi(const std::vector> &kraus, size_t dim) { return superop2choi(kraus2superop(kraus, dim), dim); } template -matrix superop2choi(const matrix& superop, size_t dim) { +matrix superop2choi(const matrix &superop, size_t dim) { return reshuffle(superop, dim, dim, dim, dim); } template -std::vector> superop2kraus(const matrix& superop, size_t dim, double threshold) { +std::vector> superop2kraus(const matrix &superop, size_t dim, + double threshold) { return choi2kraus(superop2choi(superop, dim), dim, threshold); } template -matrix choi2superop(const matrix& choi, size_t dim) { +matrix choi2superop(const matrix &choi, size_t dim) { return reshuffle(choi, dim, dim, dim, dim); } template std::vector>> -choi2kraus(const matrix>& choi, size_t dim, double threshold) { +choi2kraus(const matrix> &choi, size_t dim, double threshold) { size_t dim2 = dim * dim; std::vector evals; @@ -108,7 +111,7 @@ choi2kraus(const matrix>& choi, size_t dim, double threshold) { const size_t idx = dim2 - 1 - i; const T eval = evals[idx]; if (eval > 0.0 && !Linalg::almost_equal(eval, 0.0, threshold)) { - std::complex coeff(std::sqrt(eval), 0.0); + std::complex coeff(std::sqrt(eval), 0.0); matrix> kmat(dim, dim); for (size_t col = 0; col < dim; col++) for (size_t row = 0; row < dim; row++) { @@ -121,19 +124,20 @@ choi2kraus(const matrix>& choi, size_t dim, double threshold) { } template -matrix reshuffle(const matrix &mat, size_t d0, size_t d1, size_t d2, size_t d3) { +matrix reshuffle(const matrix &mat, size_t d0, size_t d1, size_t d2, + size_t d3) { matrix ret(d1 * d3, d0 * d2); for (size_t i0 = 0; i0 < d0; ++i0) for (size_t i1 = 0; i1 < d1; ++i1) for (size_t i2 = 0; i2 < d2; ++i2) for (size_t i3 = 0; i3 < d3; ++i3) { - ret(d1 * i3 + i1, d0 * i2 + i0) = mat(d1 * i0 + i1, d3 * i2 + i3); + ret(d1 * i3 + i1, d0 * i2 + i0) = mat(d1 * i0 + i1, d3 * i2 + i3); } return ret; } //------------------------------------------------------------------------- -} // end namespace Noise +} // namespace Utils //------------------------------------------------------------------------- } // end namespace AER //------------------------------------------------------------------------- diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp old mode 100755 new mode 100644 index c1eb65c2fe..da1f575054 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -16,15 +16,15 @@ #define _aer_framework_operations_hpp_ #include -#include #include #include +#include #include -#include "framework/types.hpp" #include "framework/json_parser.hpp" -#include "framework/utils.hpp" #include "framework/linalg/almost_equal.hpp" +#include "framework/types.hpp" +#include "framework/utils.hpp" #include "simulators/stabilizer/clifford.hpp" namespace AER { @@ -33,39 +33,81 @@ namespace Operations { // Comparisons enum class used for Boolean function operation. // these are used to compare two hexadecimal strings and return a bool // for now we only have one comparison Equal, but others will be added -enum class RegComparison {Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual}; +enum class RegComparison { + Equal, + NotEqual, + Less, + LessEqual, + Greater, + GreaterEqual +}; // Enum class for operation types enum class OpType { - gate, measure, reset, bfunc, barrier, qerror_loc, - matrix, diagonal_matrix, multiplexer, initialize, sim_op, nop, + gate, + measure, + reset, + bfunc, + barrier, + qerror_loc, + matrix, + diagonal_matrix, + multiplexer, + initialize, + sim_op, + nop, // Noise instructions - kraus, superop, roerror, noise_switch, + kraus, + superop, + roerror, + noise_switch, // Save instructions - save_state, save_expval, save_expval_var, save_statevec, save_statevec_dict, - save_densmat, save_probs, save_probs_ket, save_amps, save_amps_sq, - save_stabilizer, save_clifford, save_unitary, save_mps, save_superop, + save_state, + save_expval, + save_expval_var, + save_statevec, + save_statevec_dict, + save_densmat, + save_probs, + save_probs_ket, + save_amps, + save_amps_sq, + save_stabilizer, + save_clifford, + save_unitary, + save_mps, + save_superop, // Set instructions - set_statevec, set_densmat, set_unitary, set_superop, - set_stabilizer, set_mps, + set_statevec, + set_densmat, + set_unitary, + set_superop, + set_stabilizer, + set_mps, // Control Flow - jump, mark + jump, + mark }; enum class DataSubType { - single, c_single, list, c_list, accum, c_accum, average, c_average + single, + c_single, + list, + c_list, + accum, + c_accum, + average, + c_average }; static const std::unordered_set SAVE_TYPES = { - OpType::save_state, OpType::save_expval, OpType::save_expval_var, - OpType::save_statevec, OpType::save_statevec_dict, - OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, - OpType::save_amps, OpType::save_amps_sq, OpType::save_stabilizer, - OpType::save_clifford, - OpType::save_unitary, OpType::save_mps, OpType::save_superop -}; + OpType::save_state, OpType::save_expval, OpType::save_expval_var, + OpType::save_statevec, OpType::save_statevec_dict, OpType::save_densmat, + OpType::save_probs, OpType::save_probs_ket, OpType::save_amps, + OpType::save_amps_sq, OpType::save_stabilizer, OpType::save_clifford, + OpType::save_unitary, OpType::save_mps, OpType::save_superop}; -inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { +inline std::ostream &operator<<(std::ostream &stream, const OpType &type) { switch (type) { case OpType::gate: stream << "gate"; @@ -189,40 +231,39 @@ inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { return stream; } - -inline std::ostream& operator<<(std::ostream& stream, const DataSubType& subtype) { +inline std::ostream &operator<<(std::ostream &stream, + const DataSubType &subtype) { switch (subtype) { - case DataSubType::single: - stream << "single"; - break; - case DataSubType::c_single: - stream << "c_single"; - break; - case DataSubType::list: - stream << "list"; - break; - case DataSubType::c_list: - stream << "c_list"; - break; - case DataSubType::accum: - stream << "accum"; - break; - case DataSubType::c_accum: - stream << "c_accum"; - break; - case DataSubType::average: - stream << "average"; - break; - case DataSubType::c_average: - stream << "c_average"; - break; - default: - stream << "unknown"; + case DataSubType::single: + stream << "single"; + break; + case DataSubType::c_single: + stream << "c_single"; + break; + case DataSubType::list: + stream << "list"; + break; + case DataSubType::c_list: + stream << "c_list"; + break; + case DataSubType::accum: + stream << "accum"; + break; + case DataSubType::c_accum: + stream << "c_accum"; + break; + case DataSubType::average: + stream << "average"; + break; + case DataSubType::c_average: + stream << "c_average"; + break; + default: + stream << "unknown"; } return stream; } - //------------------------------------------------------------------------------ // Op Class //------------------------------------------------------------------------------ @@ -234,17 +275,19 @@ struct Op { reg_t qubits; // qubits operation acts on std::vector regs; // list of qubits for matrixes std::vector params; // real or complex params for gates - std::vector int_params; // integer parameters - std::vector string_params; // used for label, control-flow, and boolean functions + std::vector int_params; // integer parameters + std::vector + string_params; // used for label, control-flow, and boolean functions // Conditional Operations bool conditional = false; // is gate conditional gate - uint_t conditional_reg; // (opt) the (single) register location to look up for conditional - RegComparison bfunc; // (opt) boolean function relation + uint_t conditional_reg; // (opt) the (single) register location to look up for + // conditional + RegComparison bfunc; // (opt) boolean function relation // Measurement - reg_t memory; // (opt) register operation it acts on (measure) - reg_t registers; // (opt) register locations it acts on (measure, conditional) + reg_t memory; // (opt) register operation it acts on (measure) + reg_t registers; // (opt) register locations it acts on (measure, conditional) // Mat and Kraus std::vector mats; @@ -263,22 +306,25 @@ struct Op { DataSubType save_type = DataSubType::single; }; -inline std::ostream& operator<<(std::ostream& s, const Op& op) { +inline std::ostream &operator<<(std::ostream &s, const Op &op) { s << op.name << "["; bool first = true; - for (size_t qubit: op.qubits) { - if (!first) s << ","; + for (size_t qubit : op.qubits) { + if (!first) + s << ","; s << qubit; first = false; } s << "],["; first = true; - for (reg_t reg: op.regs) { - if (!first) s << ","; + for (reg_t reg : op.regs) { + if (!first) + s << ","; s << "["; bool first0 = true; - for (size_t qubit: reg) { - if (!first0) s << ","; + for (size_t qubit : reg) { + if (!first0) + s << ","; s << qubit; first0 = false; } @@ -296,28 +342,36 @@ inline std::ostream& operator<<(std::ostream& s, const Op& op) { // Raise an exception if name string is empty inline void check_empty_name(const Op &op) { if (op.name.empty()) - throw std::invalid_argument(R"(Invalid qobj instruction ("name" is empty).)"); + throw std::invalid_argument( + R"(Invalid qobj instruction ("name" is empty).)"); } // Raise an exception if qubits list is empty inline void check_empty_qubits(const Op &op) { if (op.qubits.empty()) - throw std::invalid_argument(R"(Invalid qobj ")" + op.name + - R"(" instruction ("qubits" is empty).)"); + throw std::invalid_argument(R"(Invalid operation ")" + op.name + + R"(" ("qubits" is empty).)"); } // Raise an exception if params is empty inline void check_empty_params(const Op &op) { if (op.params.empty()) - throw std::invalid_argument(R"(Invalid qobj ")" + op.name + - R"(" instruction ("params" is empty).)"); + throw std::invalid_argument(R"(Invalid operation ")" + op.name + + R"(" ("params" is empty).)"); +} + +// Raise an exception if qubits is more than expected +inline void check_length_qubits(const Op &op, const size_t size) { + if (op.qubits.size() < size) + throw std::invalid_argument(R"(Invalid operation ")" + op.name + + R"(" ("qubits" is incorrect length).)"); } // Raise an exception if params is empty inline void check_length_params(const Op &op, const size_t size) { - if (op.params.size() != size) - throw std::invalid_argument(R"(Invalid qobj ")" + op.name + - R"(" instruction ("params" is incorrect length).)"); + if (op.params.size() < size) + throw std::invalid_argument(R"(Invalid operation ")" + op.name + + R"(" ("params" is incorrect length).)"); } // Raise an exception if qubits list contains duplications @@ -325,15 +379,50 @@ inline void check_duplicate_qubits(const Op &op) { auto cpy = op.qubits; std::unique(cpy.begin(), cpy.end()); if (cpy != op.qubits) - throw std::invalid_argument(R"(Invalid qobj ")" + op.name + - R"(" instruction ("qubits" are not unique).)"); + throw std::invalid_argument(R"(Invalid operation ")" + op.name + + R"(" ("qubits" are not unique).)"); +} + +inline void check_gate_params(const Op &op) { + const stringmap_t> param_tables( + {{"u1", {1, 1}}, {"u2", {1, 2}}, {"u3", {1, 3}}, + {"u", {1, 3}}, {"U", {1, 3}}, {"CX", {2, 0}}, + {"cx", {2, 0}}, {"cz", {2, 0}}, {"cy", {2, 0}}, + {"cp", {2, 1}}, {"cu1", {2, 1}}, {"cu2", {2, 2}}, + {"cu3", {2, 3}}, {"swap", {2, 0}}, {"id", {0, 0}}, + {"p", {1, 1}}, {"x", {1, 0}}, {"y", {1, 0}}, + {"z", {1, 0}}, {"h", {1, 0}}, {"s", {1, 0}}, + {"sdg", {1, 0}}, {"t", {1, 0}}, {"tdg", {1, 0}}, + {"r", {1, 2}}, {"rx", {1, 1}}, {"ry", {1, 1}}, + {"rz", {1, 1}}, {"rxx", {2, 1}}, {"ryy", {2, 1}}, + {"rzz", {2, 1}}, {"rzx", {2, 1}}, {"ccx", {3, 0}}, + {"cswap", {3, 0}}, {"mcx", {1, 0}}, {"mcy", {1, 0}}, + {"mcz", {1, 0}}, {"mcu1", {1, 1}}, {"mcu2", {1, 2}}, + {"mcu3", {1, 3}}, {"mcswap", {2, 0}}, {"mcphase", {1, 1}}, + {"mcr", {1, 1}}, {"mcrx", {1, 1}}, {"mcry", {1, 1}}, + {"mcrz", {1, 1}}, {"sx", {1, 0}}, {"sxdg", {1, 0}}, + {"csx", {2, 0}}, {"mcsx", {1, 0}}, {"csxdg", {2, 0}}, + {"mcsxdg", {1, 0}}, {"delay", {1, 0}}, {"pauli", {1, 0}}, + {"mcx_gray", {1, 0}}, {"cu", {2, 4}}, {"mcu", {1, 4}}, + {"mcp", {1, 1}}, {"ecr", {2, 0}}}); + + auto it = param_tables.find(op.name); + if (it == param_tables.end()) { + std::stringstream msg; + msg << "Invalid gate name :\"" << op.name << "\"." << std::endl; + throw std::invalid_argument(msg.str()); + } else { + check_length_qubits(op, std::get<0>(it->second)); + check_length_params(op, std::get<1>(it->second)); + } } //------------------------------------------------------------------------------ // Generator functions //------------------------------------------------------------------------------ -inline Op make_initialize(const reg_t &qubits, const std::vector &init_data) { +inline Op make_initialize(const reg_t &qubits, + const std::vector &init_data) { Op op; op.type = OpType::initialize; op.name = "initialize"; @@ -342,7 +431,8 @@ inline Op make_initialize(const reg_t &qubits, const std::vector &ini return op; } -inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, const int_t conditional = -1, std::string label = "") { +inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, + const int_t conditional = -1, std::string label = "") { Op op; op.type = OpType::matrix; op.name = "unitary"; @@ -357,7 +447,8 @@ inline Op make_unitary(const reg_t &qubits, const cmatrix_t &mat, const int_t co return op; } -inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat, std::string label = "") { +inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat, + std::string label = "") { Op op; op.type = OpType::matrix; op.name = "unitary"; @@ -369,7 +460,8 @@ inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat, std::string label = return op; } -inline Op make_diagonal(const reg_t &qubits, const cvector_t &vec, const std::string label = "") { +inline Op make_diagonal(const reg_t &qubits, const cvector_t &vec, + const std::string label = "") { Op op; op.type = OpType::diagonal_matrix; op.name = "diagonal"; @@ -382,7 +474,8 @@ inline Op make_diagonal(const reg_t &qubits, const cvector_t &vec, const std::st return op; } -inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, const std::string label = "") { +inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, + const std::string label = "") { Op op; op.type = OpType::diagonal_matrix; op.name = "diagonal"; @@ -395,7 +488,8 @@ inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, const std::string return op; } -inline Op make_superop(const reg_t &qubits, const cmatrix_t &mat, const int_t conditional = -1) { +inline Op make_superop(const reg_t &qubits, const cmatrix_t &mat, + const int_t conditional = -1) { Op op; op.type = OpType::superop; op.name = "superop"; @@ -418,7 +512,8 @@ inline Op make_superop(const reg_t &qubits, cmatrix_t &&mat) { return op; } -inline Op make_kraus(const reg_t &qubits, const std::vector &mats, const int_t conditional = -1) { +inline Op make_kraus(const reg_t &qubits, const std::vector &mats, + const int_t conditional = -1) { Op op; op.type = OpType::kraus; op.name = "kraus"; @@ -440,7 +535,8 @@ inline Op make_kraus(const reg_t &qubits, std::vector &&mats) { return op; } -inline Op make_roerror(const reg_t &memory, const std::vector &probs) { +inline Op make_roerror(const reg_t &memory, + const std::vector &probs) { Op op; op.type = OpType::roerror; op.name = "roerror"; @@ -458,7 +554,8 @@ inline Op make_roerror(const reg_t &memory, std::vector &&probs) { return op; } -inline Op make_bfunc(const std::string &mask, const std::string &val, const std::string &relation, const uint_t regidx) { +inline Op make_bfunc(const std::string &mask, const std::string &val, + const std::string &relation, const uint_t regidx) { Op op; op.type = OpType::bfunc; op.name = "bfunc"; @@ -469,39 +566,37 @@ inline Op make_bfunc(const std::string &mask, const std::string &val, const std: // Load single register op.registers.push_back(regidx); - + // Format hex strings Utils::format_hex_inplace(op.string_params[0]); Utils::format_hex_inplace(op.string_params[1]); const stringmap_t comp_table({ - {"==", RegComparison::Equal}, - {"!=", RegComparison::NotEqual}, - {"<", RegComparison::Less}, - {"<=", RegComparison::LessEqual}, - {">", RegComparison::Greater}, - {">=", RegComparison::GreaterEqual}, + {"==", RegComparison::Equal}, + {"!=", RegComparison::NotEqual}, + {"<", RegComparison::Less}, + {"<=", RegComparison::LessEqual}, + {">", RegComparison::Greater}, + {">=", RegComparison::GreaterEqual}, }); auto it = comp_table.find(relation); if (it == comp_table.end()) { std::stringstream msg; - msg << "Invalid bfunc relation string :\"" << it->first << "\"." << std::endl; + msg << "Invalid bfunc relation string :\"" << it->first << "\"." + << std::endl; throw std::invalid_argument(msg.str()); } else { op.bfunc = it->second; } return op; - } -Op make_gate(const std::string &name, - const reg_t &qubits, +Op make_gate(const std::string &name, const reg_t &qubits, const std::vector ¶ms, const std::vector &string_params, - const int_t conditional, - const std::string &label) { + const int_t conditional, const std::string &label) { Op op; op.type = OpType::gate; op.name = name; @@ -510,7 +605,7 @@ Op make_gate(const std::string &name, if (string_params.size() > 0) op.string_params = string_params; - else if (label != "") + else if (label != "") op.string_params = {label}; else op.string_params = {op.name}; @@ -556,7 +651,7 @@ inline Op make_u3(uint_t qubit, T theta, T phi, T lam) { return op; } -inline Op make_reset(const reg_t & qubits, uint_t state = 0) { +inline Op make_reset(const reg_t &qubits, uint_t state = 0) { Op op; op.type = OpType::reset; op.name = "reset"; @@ -623,31 +718,29 @@ inline Op make_multiplexer(const reg_t &qubits, return op; } -inline Op make_save_state(const reg_t &qubits, - const std::string &name, +inline Op make_save_state(const reg_t &qubits, const std::string &name, const std::string &snapshot_type, const std::string &label) { Op op; op.name = name; // Get subtype - static const std::unordered_map types { - {"save_state", OpType::save_state}, - {"save_statevector", OpType::save_statevec}, - {"save_statevector_dict", OpType::save_statevec_dict}, - {"save_amplitudes", OpType::save_amps}, - {"save_amplitudes_sq", OpType::save_amps_sq}, - {"save_clifford", OpType::save_clifford}, - {"save_probabilities", OpType::save_probs}, - {"save_probabilities_dict", OpType::save_probs_ket}, - {"save_matrix_product_state", OpType::save_mps}, - {"save_unitary", OpType::save_unitary}, - {"save_superop", OpType::save_superop}, - {"save_density_matrix", OpType::save_densmat}, - {"save_stabilizer", OpType::save_stabilizer}, - {"save_expval", OpType::save_expval}, - {"save_expval_var", OpType::save_expval_var} - }; + static const std::unordered_map types{ + {"save_state", OpType::save_state}, + {"save_statevector", OpType::save_statevec}, + {"save_statevector_dict", OpType::save_statevec_dict}, + {"save_amplitudes", OpType::save_amps}, + {"save_amplitudes_sq", OpType::save_amps_sq}, + {"save_clifford", OpType::save_clifford}, + {"save_probabilities", OpType::save_probs}, + {"save_probabilities_dict", OpType::save_probs_ket}, + {"save_matrix_product_state", OpType::save_mps}, + {"save_unitary", OpType::save_unitary}, + {"save_superop", OpType::save_superop}, + {"save_density_matrix", OpType::save_densmat}, + {"save_stabilizer", OpType::save_stabilizer}, + {"save_expval", OpType::save_expval}, + {"save_expval_var", OpType::save_expval_var}}; auto type_it = types.find(name); if (type_it == types.end()) { @@ -657,15 +750,11 @@ inline Op make_save_state(const reg_t &qubits, op.type = type_it->second; // Get subtype - static const std::unordered_map subtypes { - {"single", DataSubType::single}, - {"c_single", DataSubType::c_single}, - {"average", DataSubType::average}, - {"c_average", DataSubType::c_average}, - {"list", DataSubType::list}, - {"c_list", DataSubType::c_list}, - {"accum", DataSubType::accum}, - {"c_accum", DataSubType::c_accum}, + static const std::unordered_map subtypes{ + {"single", DataSubType::single}, {"c_single", DataSubType::c_single}, + {"average", DataSubType::average}, {"c_average", DataSubType::c_average}, + {"list", DataSubType::list}, {"c_list", DataSubType::c_list}, + {"accum", DataSubType::accum}, {"c_accum", DataSubType::c_accum}, }; auto subtype_it = subtypes.find(snapshot_type); @@ -674,7 +763,7 @@ inline Op make_save_state(const reg_t &qubits, "\" in save data instruction."); } op.save_type = subtype_it->second; - + op.string_params.emplace_back(label); op.qubits = qubits; @@ -682,8 +771,7 @@ inline Op make_save_state(const reg_t &qubits, return op; } -inline Op make_save_amplitudes(const reg_t &qubits, - const std::string &name, +inline Op make_save_amplitudes(const reg_t &qubits, const std::string &name, const std::vector &base_type, const std::string &snapshot_type, const std::string &label) { @@ -692,8 +780,7 @@ inline Op make_save_amplitudes(const reg_t &qubits, return op; } -inline Op make_save_expval(const reg_t &qubits, - const std::string &name, +inline Op make_save_expval(const reg_t &qubits, const std::string &name, const std::vector pauli_strings, const std::vector coeff_reals, const std::vector coeff_imags, @@ -706,7 +793,8 @@ inline Op make_save_expval(const reg_t &qubits, auto op = make_save_state(qubits, name, snapshot_type, label); for (uint_t i = 0; i < pauli_strings.size(); ++i) - op.expval_params.emplace_back(pauli_strings[i], coeff_reals[i], coeff_imags[i]); + op.expval_params.emplace_back(pauli_strings[i], coeff_reals[i], + coeff_imags[i]); if (op.expval_params.empty()) { std::string pauli(op.qubits.size(), 'I'); @@ -715,12 +803,13 @@ inline Op make_save_expval(const reg_t &qubits, return op; } -template -inline Op make_set_vector(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { +template +inline Op make_set_vector(const reg_t &qubits, const std::string &name, + const inputdata_t ¶ms) { Op op; // Get type - static const std::unordered_map types { - {"set_statevector", OpType::set_statevec}, + static const std::unordered_map types{ + {"set_statevector", OpType::set_statevec}, }; auto type_it = types.find(name); if (type_it == types.end()) { @@ -730,19 +819,21 @@ inline Op make_set_vector(const reg_t &qubits, const std::string &name, const in op.type = type_it->second; op.name = name; op.qubits = qubits; - op.params = Parser::template get_list_elem>(params, 0); + op.params = + Parser::template get_list_elem>( + params, 0); return op; } -template -inline Op make_set_matrix(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { +template +inline Op make_set_matrix(const reg_t &qubits, const std::string &name, + const inputdata_t ¶ms) { Op op; // Get type - static const std::unordered_map types { - {"set_density_matrix", OpType::set_densmat}, - {"set_unitary", OpType::set_unitary}, - {"set_superop", OpType::set_superop} - }; + static const std::unordered_map types{ + {"set_density_matrix", OpType::set_densmat}, + {"set_unitary", OpType::set_unitary}, + {"set_superop", OpType::set_superop}}; auto type_it = types.find(name); if (type_it == types.end()) { throw std::runtime_error("Invalid data type \"" + name + @@ -751,38 +842,45 @@ inline Op make_set_matrix(const reg_t &qubits, const std::string &name, const in op.type = type_it->second; op.name = name; op.qubits = qubits; - op.mats.push_back(Parser::template get_list_elem(params, 0)); + op.mats.push_back( + Parser::template get_list_elem(params, 0)); return op; } -template -inline Op make_set_mps(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { +template +inline Op make_set_mps(const reg_t &qubits, const std::string &name, + const inputdata_t ¶ms) { Op op; op.type = OpType::set_mps; op.name = name; op.qubits = qubits; - op.mps = Parser::template get_list_elem(params, 0); + op.mps = + Parser::template get_list_elem(params, 0); return op; } -template -inline Op make_set_clifford(const reg_t &qubits, const std::string &name, const inputdata_t ¶ms) { +template +inline Op make_set_clifford(const reg_t &qubits, const std::string &name, + const inputdata_t ¶ms) { Op op; op.type = OpType::set_stabilizer; op.name = name; op.qubits = qubits; - op.clifford = Parser::template get_list_elem(params, 0); + op.clifford = Parser::template get_list_elem( + params, 0); return op; } -inline Op make_jump(const reg_t &qubits, const std::vector ¶ms, const int_t conditional) { +inline Op make_jump(const reg_t &qubits, const std::vector ¶ms, + const int_t conditional) { Op op; op.type = OpType::jump; op.name = "jump"; op.qubits = qubits; op.string_params = params; if (op.string_params.empty()) - throw std::invalid_argument(std::string("Invalid jump (\"params\" field missing).")); + throw std::invalid_argument( + std::string("Invalid jump (\"params\" field missing).")); if (conditional >= 0) { op.conditional = true; @@ -792,19 +890,30 @@ inline Op make_jump(const reg_t &qubits, const std::vector ¶ms, return op; } -inline Op make_mark(const reg_t &qubits, const std::vector ¶ms) { +inline Op make_mark(const reg_t &qubits, + const std::vector ¶ms) { Op op; op.type = OpType::mark; op.name = "mark"; op.qubits = qubits; op.string_params = params; if (op.string_params.empty()) - throw std::invalid_argument(std::string("Invalid mark (\"params\" field missing).")); + throw std::invalid_argument( + std::string("Invalid mark (\"params\" field missing).")); return op; } -inline Op make_measure(const reg_t &qubits, const reg_t &memory, const reg_t ®isters) { +inline Op make_barrier(const reg_t &qubits) { + Op op; + op.type = OpType::barrier; + op.name = "barrier"; + op.qubits = qubits; + return op; +} + +inline Op make_measure(const reg_t &qubits, const reg_t &memory, + const reg_t ®isters) { Op op; op.type = OpType::measure; op.name = "measure"; @@ -814,7 +923,8 @@ inline Op make_measure(const reg_t &qubits, const reg_t &memory, const reg_t &re return op; } -inline Op make_qerror_loc(const reg_t &qubits, const std::string &label, const int_t conditional = -1) { +inline Op make_qerror_loc(const reg_t &qubits, const std::string &label, + const int_t conditional = -1) { Op op; op.type = OpType::qerror_loc; op.name = label; @@ -826,99 +936,97 @@ inline Op make_qerror_loc(const reg_t &qubits, const std::string &label, const i return op; } - //------------------------------------------------------------------------------ // JSON conversion //------------------------------------------------------------------------------ // Main deserialization functions -template -Op input_to_op(const inputdata_t& input); // Partial TODO -json_t op_to_json(const Op &op); // Partial TODO +template +Op input_to_op(const inputdata_t &input); // Partial TODO +json_t op_to_json(const Op &op); // Partial TODO -inline void from_json(const json_t &js, Op &op) {op = input_to_op(js);} +inline void from_json(const json_t &js, Op &op) { op = input_to_op(js); } -inline void to_json(json_t &js, const Op &op) { js = op_to_json(op);} +inline void to_json(json_t &js, const Op &op) { js = op_to_json(op); } -void to_json(json_t &js, const DataSubType& type); +void to_json(json_t &js, const DataSubType &type); // Standard operations -template -Op input_to_op_gate(const inputdata_t& input); -template -Op input_to_op_barrier(const inputdata_t& input); -template -Op input_to_op_measure(const inputdata_t& input); -template -Op input_to_op_reset(const inputdata_t& input); -template -Op input_to_op_bfunc(const inputdata_t& input); -template -Op input_to_op_initialize(const inputdata_t& input); -template -Op input_to_op_pauli(const inputdata_t& input); +template +Op input_to_op_gate(const inputdata_t &input); +template +Op input_to_op_barrier(const inputdata_t &input); +template +Op input_to_op_measure(const inputdata_t &input); +template +Op input_to_op_reset(const inputdata_t &input); +template +Op input_to_op_bfunc(const inputdata_t &input); +template +Op input_to_op_initialize(const inputdata_t &input); +template +Op input_to_op_pauli(const inputdata_t &input); // Set state -template -Op input_to_op_set_vector(const inputdata_t& input, OpType op_type); +template +Op input_to_op_set_vector(const inputdata_t &input, OpType op_type); -template -Op input_to_op_set_matrix(const inputdata_t& input, OpType op_type); +template +Op input_to_op_set_matrix(const inputdata_t &input, OpType op_type); -template -Op input_to_op_set_clifford(const inputdata_t& input, OpType op_type); +template +Op input_to_op_set_clifford(const inputdata_t &input, OpType op_type); -template -Op input_to_op_set_mps(const inputdata_t& input, OpType op_type); +template +Op input_to_op_set_mps(const inputdata_t &input, OpType op_type); // Save data -template -Op input_to_op_save_default(const inputdata_t& input, OpType op_type); -template -Op input_to_op_save_expval(const inputdata_t& input, bool variance); -template -Op input_to_op_save_amps(const inputdata_t& input, bool squared); +template +Op input_to_op_save_default(const inputdata_t &input, OpType op_type); +template +Op input_to_op_save_expval(const inputdata_t &input, bool variance); +template +Op input_to_op_save_amps(const inputdata_t &input, bool squared); // Control-Flow -template -Op input_to_op_jump(const inputdata_t& input); -template -Op input_to_op_mark(const inputdata_t& input); +template +Op input_to_op_jump(const inputdata_t &input); +template +Op input_to_op_mark(const inputdata_t &input); // Matrices -template -Op input_to_op_unitary(const inputdata_t& input); -template -Op input_to_op_diagonal(const inputdata_t& input); -template -Op input_to_op_superop(const inputdata_t& input); -template -Op input_to_op_multiplexer(const inputdata_t& input); -template -Op input_to_op_kraus(const inputdata_t& input); -template -Op input_to_op_noise_switch(const inputdata_t& input); -template -Op input_to_op_qerror_loc(const inputdata_t& input); +template +Op input_to_op_unitary(const inputdata_t &input); +template +Op input_to_op_diagonal(const inputdata_t &input); +template +Op input_to_op_superop(const inputdata_t &input); +template +Op input_to_op_multiplexer(const inputdata_t &input); +template +Op input_to_op_kraus(const inputdata_t &input); +template +Op input_to_op_noise_switch(const inputdata_t &input); +template +Op input_to_op_qerror_loc(const inputdata_t &input); // Classical bits -template -Op input_to_op_roerror(const inputdata_t& input); +template +Op input_to_op_roerror(const inputdata_t &input); // Optional instruction parameters -enum class Allowed {Yes, No}; - -template -void add_conditional(const Allowed val, Op& op, const inputdata_t& input); +enum class Allowed { Yes, No }; +template +void add_conditional(const Allowed val, Op &op, const inputdata_t &input); //------------------------------------------------------------------------------ // Implementation: JSON deserialization //------------------------------------------------------------------------------ // TODO: convert if-else to switch -template -Op input_to_op(const inputdata_t& input) { +template +Op input_to_op(const inputdata_t &input) { // load operation identifier std::string name; Parser::get_value(name, "name", input); @@ -1001,7 +1109,7 @@ Op input_to_op(const inputdata_t& input) { if (name == "pauli") return input_to_op_pauli(input); - //Control-flow + // Control-flow if (name == "jump") return input_to_op_jump(input); if (name == "mark") @@ -1032,32 +1140,30 @@ json_t op_to_json(const Op &op) { return ret; } - -void to_json(json_t &js, const OpType& type) { +void to_json(json_t &js, const OpType &type) { std::stringstream ss; ss << type; js = ss.str(); } - -void to_json(json_t &js, const DataSubType& subtype) { +void to_json(json_t &js, const DataSubType &subtype) { std::stringstream ss; ss << subtype; js = ss.str(); } - //------------------------------------------------------------------------------ // Implementation: Gates, measure, reset deserialization //------------------------------------------------------------------------------ -template -void add_conditional(const Allowed allowed, Op& op, const inputdata_t& input) { +template +void add_conditional(const Allowed allowed, Op &op, const inputdata_t &input) { // Check conditional if (Parser::check_key("conditional", input)) { // 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."); + throw std::invalid_argument("Invalid instruction: \"" + op.name + + "\" cannot be conditional."); } // If instruction is allowed to be conditional add parameters Parser::get_value(op.conditional_reg, "conditional", input); @@ -1065,8 +1171,8 @@ void add_conditional(const Allowed allowed, Op& op, const inputdata_t& input) { } } -template -Op input_to_op_gate(const inputdata_t& input) { +template +Op input_to_op_gate(const inputdata_t &input) { Op op; op.type = OpType::gate; Parser::get_value(op.name, "name", input); @@ -1077,7 +1183,7 @@ Op input_to_op_gate(const inputdata_t& input) { // If label is not specified record the gate name as the label std::string label; Parser::get_value(label, "label", input); - if (label != "") + if (label != "") op.string_params = {label}; else op.string_params = {op.name}; @@ -1089,17 +1195,13 @@ Op input_to_op_gate(const inputdata_t& input) { check_empty_name(op); check_empty_qubits(op); check_duplicate_qubits(op); - if (op.name == "u1") - check_length_params(op, 1); - else if (op.name == "u2") - check_length_params(op, 2); - else if (op.name == "u3") - check_length_params(op, 3); + check_gate_params(op); + return op; } -template -Op input_to_op_qerror_loc(const inputdata_t& input) { +template +Op input_to_op_qerror_loc(const inputdata_t &input) { Op op; op.type = OpType::qerror_loc; Parser::get_value(op.name, "label", input); @@ -1108,7 +1210,7 @@ Op input_to_op_qerror_loc(const inputdata_t& input) { return op; } -template +template Op input_to_op_barrier(const inputdata_t &input) { Op op; op.type = OpType::barrier; @@ -1119,8 +1221,8 @@ Op input_to_op_barrier(const inputdata_t &input) { return op; } -template -Op input_to_op_measure(const inputdata_t& input) { +template +Op input_to_op_measure(const inputdata_t &input) { Op op; op.type = OpType::measure; op.name = "measure"; @@ -1135,16 +1237,19 @@ Op input_to_op_measure(const inputdata_t& input) { check_empty_qubits(op); check_duplicate_qubits(op); if (op.memory.empty() == false && op.memory.size() != op.qubits.size()) { - throw std::invalid_argument(R"(Invalid measure operation: "memory" and "qubits" are different lengths.)"); + throw std::invalid_argument( + R"(Invalid measure operation: "memory" and "qubits" are different lengths.)"); } - if (op.registers.empty() == false && op.registers.size() != op.qubits.size()) { - throw std::invalid_argument(R"(Invalid measure operation: "register" and "qubits" are different lengths.)"); + if (op.registers.empty() == false && + op.registers.size() != op.qubits.size()) { + throw std::invalid_argument( + R"(Invalid measure operation: "register" and "qubits" are different lengths.)"); } return op; } -template -Op input_to_op_reset(const inputdata_t& input) { +template +Op input_to_op_reset(const inputdata_t &input) { Op op; op.type = OpType::reset; op.name = "reset"; @@ -1159,8 +1264,8 @@ Op input_to_op_reset(const inputdata_t& input) { return op; } -template -Op input_to_op_initialize(const inputdata_t& input) { +template +Op input_to_op_initialize(const inputdata_t &input) { Op op; op.type = OpType::initialize; op.name = "initialize"; @@ -1176,8 +1281,8 @@ Op input_to_op_initialize(const inputdata_t& input) { check_length_params(op, 1ULL << op.qubits.size()); return op; } -template -Op input_to_op_pauli(const inputdata_t& input){ +template +Op input_to_op_pauli(const inputdata_t &input) { Op op; op.type = OpType::gate; op.name = "pauli"; @@ -1188,7 +1293,7 @@ Op input_to_op_pauli(const inputdata_t& input){ // If label is not specified record the gate name as the label std::string label; Parser::get_value(label, "label", input); - if (label != "") + if (label != "") op.string_params.push_back(label); else op.string_params.push_back(op.name); @@ -1206,16 +1311,19 @@ Op input_to_op_pauli(const inputdata_t& input){ //------------------------------------------------------------------------------ // Implementation: Boolean Functions //------------------------------------------------------------------------------ -template -Op input_to_op_bfunc(const inputdata_t& input) { +template +Op input_to_op_bfunc(const inputdata_t &input) { Op op; op.type = OpType::bfunc; op.name = "bfunc"; op.string_params.resize(2); std::string relation; - Parser::get_value(op.string_params[0], "mask", input); // mask hexadecimal string - Parser::get_value(op.string_params[1], "val", input); // value hexadecimal string - Parser::get_value(relation, "relation", input); // relation string + Parser::get_value(op.string_params[0], "mask", + input); // mask hexadecimal string + Parser::get_value(op.string_params[1], "val", + input); // value hexadecimal string + Parser::get_value(relation, "relation", + input); // relation string // Load single register / memory bit for storing result uint_t tmp; if (Parser::get_value(tmp, "register", input)) { @@ -1224,24 +1332,25 @@ Op input_to_op_bfunc(const inputdata_t& input) { if (Parser::get_value(tmp, "memory", input)) { op.memory.push_back(tmp); } - + // Format hex strings Utils::format_hex_inplace(op.string_params[0]); Utils::format_hex_inplace(op.string_params[1]); const stringmap_t comp_table({ - {"==", RegComparison::Equal}, - {"!=", RegComparison::NotEqual}, - {"<", RegComparison::Less}, - {"<=", RegComparison::LessEqual}, - {">", RegComparison::Greater}, - {">=", RegComparison::GreaterEqual}, + {"==", RegComparison::Equal}, + {"!=", RegComparison::NotEqual}, + {"<", RegComparison::Less}, + {"<=", RegComparison::LessEqual}, + {">", RegComparison::Greater}, + {">=", RegComparison::GreaterEqual}, }); auto it = comp_table.find(relation); if (it == comp_table.end()) { std::stringstream msg; - msg << "Invalid bfunc relation string :\"" << it->first << "\"." << std::endl; + msg << "Invalid bfunc relation string :\"" << it->first << "\"." + << std::endl; throw std::invalid_argument(msg.str()); } else { op.bfunc = it->second; @@ -1252,13 +1361,14 @@ Op input_to_op_bfunc(const inputdata_t& input) { // Validation if (op.registers.empty()) { - throw std::invalid_argument("Invalid measure operation: \"register\" is empty."); + throw std::invalid_argument( + "Invalid measure operation: \"register\" is empty."); } return op; } -template -Op input_to_op_roerror(const inputdata_t& input) { +template +Op input_to_op_roerror(const inputdata_t &input) { Op op; op.type = OpType::roerror; op.name = "roerror"; @@ -1273,8 +1383,8 @@ Op input_to_op_roerror(const inputdata_t& input) { //------------------------------------------------------------------------------ // Implementation: Matrix and Kraus deserialization //------------------------------------------------------------------------------ -template -Op input_to_op_unitary(const inputdata_t& input) { +template +Op input_to_op_unitary(const inputdata_t &input) { Op op; op.type = OpType::matrix; op.name = "unitary"; @@ -1300,8 +1410,8 @@ Op input_to_op_unitary(const inputdata_t& input) { add_conditional(Allowed::Yes, op, input); return op; } -template -Op input_to_op_diagonal(const inputdata_t& input) { +template +Op input_to_op_diagonal(const inputdata_t &input) { Op op; op.type = OpType::diagonal_matrix; op.name = "diagonal"; @@ -1329,8 +1439,8 @@ Op input_to_op_diagonal(const inputdata_t& input) { add_conditional(Allowed::Yes, op, input); return op; } -template -Op input_to_op_superop(const inputdata_t& input) { +template +Op input_to_op_superop(const inputdata_t &input) { // Warning: we don't check superoperator is valid! Op op; op.type = OpType::superop; @@ -1347,8 +1457,8 @@ Op input_to_op_superop(const inputdata_t& input) { } return op; } -template -Op input_to_op_multiplexer(const inputdata_t& input) { +template +Op input_to_op_multiplexer(const inputdata_t &input) { // Parse parameters reg_t qubits; std::vector mats; @@ -1362,8 +1472,8 @@ Op input_to_op_multiplexer(const inputdata_t& input) { add_conditional(Allowed::Yes, op, input); return op; } -template -Op input_to_op_kraus(const inputdata_t& input) { +template +Op input_to_op_kraus(const inputdata_t &input) { Op op; op.type = OpType::kraus; op.name = "kraus"; @@ -1378,8 +1488,8 @@ Op input_to_op_kraus(const inputdata_t& input) { return op; } -template -Op input_to_op_noise_switch(const inputdata_t& input) { +template +Op input_to_op_noise_switch(const inputdata_t &input) { Op op; op.type = OpType::noise_switch; op.name = "noise_switch"; @@ -1392,48 +1502,53 @@ Op input_to_op_noise_switch(const inputdata_t& input) { //------------------------------------------------------------------------------ // Implementation: Set state //------------------------------------------------------------------------------ -template +template Op input_to_op_set_vector(const inputdata_t &input, OpType op_type) { Op op; op.type = op_type; - const inputdata_t& params = Parser::get_value("params", input); - op.params = Parser::template get_list_elem>(params, 0); + const inputdata_t ¶ms = Parser::get_value("params", input); + op.params = + Parser::template get_list_elem>( + params, 0); Parser::get_value(op.name, "name", input); Parser::get_value(op.qubits, "qubits", input); add_conditional(Allowed::No, op, input); return op; } -template +template Op input_to_op_set_matrix(const inputdata_t &input, OpType op_type) { Op op; op.type = op_type; - const inputdata_t& params = Parser::get_value("params", input); - op.mats.push_back(Parser::template get_list_elem(params, 0)); + const inputdata_t ¶ms = Parser::get_value("params", input); + op.mats.push_back( + Parser::template get_list_elem(params, 0)); Parser::get_value(op.name, "name", input); Parser::get_value(op.qubits, "qubits", input); add_conditional(Allowed::No, op, input); return op; } -template +template Op input_to_op_set_clifford(const inputdata_t &input, OpType op_type) { Op op; op.type = op_type; - const inputdata_t& params = Parser::get_value("params", input); - op.clifford = Parser::template get_list_elem(params, 0); + const inputdata_t ¶ms = Parser::get_value("params", input); + op.clifford = Parser::template get_list_elem( + params, 0); Parser::get_value(op.name, "name", input); Parser::get_value(op.qubits, "qubits", input); add_conditional(Allowed::No, op, input); return op; } -template +template Op input_to_op_set_mps(const inputdata_t &input, OpType op_type) { Op op; op.type = op_type; - const inputdata_t& params = Parser::get_value("params", input); - op.mps = Parser::template get_list_elem(params, 0); + const inputdata_t ¶ms = Parser::get_value("params", input); + op.mps = + Parser::template get_list_elem(params, 0); Parser::get_value(op.name, "name", input); Parser::get_value(op.qubits, "qubits", input); @@ -1444,22 +1559,18 @@ Op input_to_op_set_mps(const inputdata_t &input, OpType op_type) { //------------------------------------------------------------------------------ // Implementation: Save data deserialization //------------------------------------------------------------------------------ -template -Op input_to_op_save_default(const inputdata_t& input, OpType op_type) { +template +Op input_to_op_save_default(const inputdata_t &input, OpType op_type) { Op op; op.type = op_type; Parser::get_value(op.name, "name", input); // Get subtype - static const std::unordered_map subtypes { - {"single", DataSubType::single}, - {"c_single", DataSubType::c_single}, - {"average", DataSubType::average}, - {"c_average", DataSubType::c_average}, - {"list", DataSubType::list}, - {"c_list", DataSubType::c_list}, - {"accum", DataSubType::accum}, - {"c_accum", DataSubType::c_accum}, + static const std::unordered_map subtypes{ + {"single", DataSubType::single}, {"c_single", DataSubType::c_single}, + {"average", DataSubType::average}, {"c_average", DataSubType::c_average}, + {"list", DataSubType::list}, {"c_list", DataSubType::c_list}, + {"accum", DataSubType::accum}, {"c_accum", DataSubType::c_accum}, }; std::string subtype; Parser::get_value(subtype, "snapshot_type", input); @@ -1469,7 +1580,7 @@ Op input_to_op_save_default(const inputdata_t& input, OpType op_type) { "\" in save data instruction."); } op.save_type = subtype_it->second; - + // Get data key op.string_params.emplace_back(""); Parser::get_value(op.string_params[0], "label", input); @@ -1478,26 +1589,30 @@ Op input_to_op_save_default(const inputdata_t& input, OpType op_type) { Parser::get_value(op.qubits, "qubits", input); return op; } -template -Op input_to_op_save_expval(const inputdata_t& input, bool variance) { +template +Op input_to_op_save_expval(const inputdata_t &input, bool variance) { // Initialized default save instruction params - auto op_type = (variance) ? OpType::save_expval_var - : OpType::save_expval; + auto op_type = (variance) ? OpType::save_expval_var : OpType::save_expval; Op op = input_to_op_save_default(input, op_type); // Parse Pauli operator components const auto threshold = 1e-12; // drop small components // Get components - if (Parser::check_key("params", input) && Parser::is_array("params", input)) { + if (Parser::check_key("params", input) && + Parser::is_array("params", input)) { for (const auto &comp_ : Parser::get_value("params", input)) { - const auto& comp = Parser::get_as_list(comp_); + const auto &comp = Parser::get_as_list(comp_); // Get complex coefficient - std::vector coeffs = Parser::template get_list_elem>(comp, 1); + std::vector coeffs = + Parser::template get_list_elem>(comp, + 1); if (std::abs(coeffs[0]) > threshold || std::abs(coeffs[1]) > threshold) { - std::string pauli = Parser::template get_list_elem(comp, 0); + std::string pauli = + Parser::template get_list_elem(comp, 0); if (pauli.size() != op.qubits.size()) { - throw std::invalid_argument(std::string("Invalid expectation value save instruction ") + - "(Pauli label does not match qubit number.)."); + throw std::invalid_argument( + std::string("Invalid expectation value save instruction ") + + "(Pauli label does not match qubit number.)."); } op.expval_params.emplace_back(pauli, coeffs[0], coeffs[1]); } @@ -1507,8 +1622,8 @@ Op input_to_op_save_expval(const inputdata_t& input, bool variance) { } // Check edge case of all coefficients being empty - // In this case the operator had all coefficients zero, or sufficiently close - // to zero that they were all truncated. + // In this case the operator had all coefficients zero, or sufficiently + // close to zero that they were all truncated. if (op.expval_params.empty()) { std::string pauli(op.qubits.size(), 'I'); op.expval_params.emplace_back(pauli, 0., 0.); @@ -1516,17 +1631,16 @@ Op input_to_op_save_expval(const inputdata_t& input, bool variance) { return op; } -template -Op input_to_op_save_amps(const inputdata_t& input, bool squared) { +template +Op input_to_op_save_amps(const inputdata_t &input, bool squared) { // Initialized default save instruction params - auto op_type = (squared) ? OpType::save_amps_sq - : OpType::save_amps; + auto op_type = (squared) ? OpType::save_amps_sq : OpType::save_amps; Op op = input_to_op_save_default(input, op_type); Parser::get_value(op.int_params, "params", input); return op; } -template +template Op input_to_op_jump(const inputdata_t &input) { Op op; op.type = OpType::jump; @@ -1534,7 +1648,8 @@ Op input_to_op_jump(const inputdata_t &input) { Parser::get_value(op.qubits, "qubits", input); Parser::get_value(op.string_params, "params", input); if (op.string_params.empty()) - throw std::invalid_argument(std::string("Invalid jump (\"params\" field missing).")); + throw std::invalid_argument( + std::string("Invalid jump (\"params\" field missing).")); // Conditional add_conditional(Allowed::Yes, op, input); @@ -1542,7 +1657,7 @@ Op input_to_op_jump(const inputdata_t &input) { return op; } -template +template Op input_to_op_mark(const inputdata_t &input) { Op op; op.type = OpType::mark; @@ -1550,7 +1665,8 @@ Op input_to_op_mark(const inputdata_t &input) { Parser::get_value(op.qubits, "qubits", input); Parser::get_value(op.string_params, "params", input); if (op.string_params.empty()) - throw std::invalid_argument(std::string("Invalid mark (\"params\" field missing).")); + throw std::invalid_argument( + std::string("Invalid mark (\"params\" field missing).")); // Conditional add_conditional(Allowed::No, op, input); @@ -1558,7 +1674,6 @@ Op input_to_op_mark(const inputdata_t &input) { return op; } - //------------------------------------------------------------------------------ } // end namespace Operations //------------------------------------------------------------------------------ diff --git a/src/framework/opset.hpp b/src/framework/opset.hpp old mode 100755 new mode 100644 index 4516c1c0e5..92cc3e2b15 --- a/src/framework/opset.hpp +++ b/src/framework/opset.hpp @@ -15,8 +15,8 @@ #ifndef _aer_framework_opset_hpp_ #define _aer_framework_opset_hpp_ -#include #include "framework/operations.hpp" +#include namespace AER { namespace Operations { @@ -31,7 +31,8 @@ class OpSet { // Hash function so that we can use an enum class as a std::unordered_set // key on older C++11 compilers like GCC 5. struct EnumClassHash { - template size_t operator()(T t) const { + template + size_t operator()(T t) const { return static_cast(t); } }; @@ -41,8 +42,8 @@ class OpSet { using optypeset_t = std::unordered_set; // Public data members - optypeset_t optypes; // A set of op types - stringset_t gates; // A set of names for OpType::gates + optypeset_t optypes; // A set of op types + stringset_t gates; // A set of names for OpType::gates OpSet() = default; diff --git a/src/framework/pybind_basics.hpp b/src/framework/pybind_basics.hpp old mode 100755 new mode 100644 index e32f214482..cfcb1f2fe5 --- a/src/framework/pybind_basics.hpp +++ b/src/framework/pybind_basics.hpp @@ -33,36 +33,48 @@ namespace AerToPy { // Template specialization is used with this function for adding custom // conversion for other types // NOTE: Can this function be replaced by overload py::cast for custom types? -template py::object to_python(T &&obj); +template +py::object to_python(T &&obj); // Move a matrix to Python via conversion to Numpy array -template py::object to_python(matrix &&obj); +template +py::object to_python(matrix &&obj); // Move a Vector to Python via conversion to Numpy array -template py::object to_python(AER::Vector &&obj); +template +py::object to_python(AER::Vector &&obj); // Move a Vector to Python via recusivly calling to_python on elements -template py::object to_python(std::vector &&obj); +template +py::object to_python(std::vector &&obj); -// Move an Unordered string map to Python object by calling to_python on elements -template py::object to_python(std::unordered_map &&obj); +// Move an Unordered string map to Python object by calling to_python on +// elements +template +py::object to_python(std::unordered_map &&obj); // Move an Unordered string map into an existing Python dict template void add_to_python(py::dict &pydata, std::unordered_map &&obj); - // Template specialization for moving numeric std::vectors to Numpy arrays -template <> py::object to_python(std::vector &&obj); -template <> py::object to_python(std::vector &&obj); -template <> py::object to_python(std::vector &&obj); -template <> py::object to_python(std::vector &&obj); -template <> py::object to_python(std::vector> &&obj); -template <> py::object to_python(std::vector> &&obj); +template <> +py::object to_python(std::vector &&obj); +template <> +py::object to_python(std::vector &&obj); +template <> +py::object to_python(std::vector &&obj); +template <> +py::object to_python(std::vector &&obj); +template <> +py::object to_python(std::vector> &&obj); +template <> +py::object to_python(std::vector> &&obj); // Template specialization for JSON // NOTE: this copies rather than moves -template <> py::object to_python(json_t &&obj); +template <> +py::object to_python(json_t &&obj); //------------------------------------------------------------------------------ // Convert To Numpy Arrays @@ -109,7 +121,7 @@ py::object to_python(std::unordered_map &&obj) { template void add_to_python(py::dict &pydata, std::unordered_map &&obj) { - for(auto& elt : obj) { + for (auto &elt : obj) { pydata[elt.first.data()] = to_python(std::move(elt.second)); } } @@ -117,7 +129,7 @@ void add_to_python(py::dict &pydata, std::unordered_map &&obj) { template py::object to_python(std::vector &&obj) { py::list pydata; - for(auto& elt : obj) { + for (auto &elt : obj) { pydata.append(to_python(std::move(elt))); } return std::move(pydata); @@ -169,39 +181,39 @@ py::object to_python(std::vector> &&obj) { template py::array_t to_numpy(matrix &&src) { - std::array shape {static_cast(src.GetRows()), - static_cast(src.GetColumns())}; - matrix* src_ptr = new matrix(std::move(src)); - auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); + std::array shape{static_cast(src.GetRows()), + static_cast(src.GetColumns())}; + matrix *src_ptr = new matrix(std::move(src)); + auto capsule = py::capsule( + src_ptr, [](void *p) { delete reinterpret_cast *>(p); }); return py::array_t(shape, src_ptr->data(), capsule); } template py::array_t to_numpy(AER::Vector &&src) { - AER::Vector* src_ptr = new AER::Vector(std::move(src)); - auto capsule = py::capsule(src_ptr, [](void* p) { - delete reinterpret_cast*>(p); - }); + AER::Vector *src_ptr = new AER::Vector(std::move(src)); + auto capsule = py::capsule( + src_ptr, [](void *p) { delete reinterpret_cast *>(p); }); return py::array_t( - src_ptr->size(), // shape of array - src_ptr->data(), // c-style contiguous strides for vector - capsule // numpy array references this parent + src_ptr->size(), // shape of array + src_ptr->data(), // c-style contiguous strides for vector + capsule // numpy array references this parent ); } - template py::array_t to_numpy(std::vector &&src) { - std::vector* src_ptr = new std::vector(std::move(src)); - auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); + std::vector *src_ptr = new std::vector(std::move(src)); + auto capsule = py::capsule( + src_ptr, [](void *p) { delete reinterpret_cast *>(p); }); return py::array_t( - src_ptr->size(), // shape of array - src_ptr->data(), // c-style contiguous strides for vector - capsule // numpy array references this parent + src_ptr->size(), // shape of array + src_ptr->data(), // c-style contiguous strides for vector + capsule // numpy array references this parent ); } //------------------------------------------------------------------------------ -} // end namespace AerToPy +} // end namespace AerToPy //------------------------------------------------------------------------------ #endif diff --git a/src/framework/pybind_casts.hpp b/src/framework/pybind_casts.hpp index e74b6d22f6..7e18ee30b4 100644 --- a/src/framework/pybind_casts.hpp +++ b/src/framework/pybind_casts.hpp @@ -16,60 +16,69 @@ #define _aer_framework_pybind_casts_hpp_ #include "../simulators/stabilizer/clifford.hpp" +#include namespace py = pybind11; namespace pybind11 { namespace detail { -template struct type_caster>{ +template +struct type_caster> { using base = type_caster_base>; + public: PYBIND11_TYPE_CASTER(matrix, _("matrix_t")); // Conversion part 1 (Python->C++): - bool load(py::handle src, bool convert){ - // TODO: Check if make sense have to flavors of matrix: F-style and C-style - auto py_matrix = py::cast>(src); - auto c_order = py_matrix.attr("flags").attr("carray").template cast(); - if(py_matrix.ndim() != 2){ - throw std::invalid_argument(std::string("Python: invalid matrix (empty array).")); - } - size_t nrows = py_matrix.shape(0); - size_t ncols = py_matrix.shape(1); - // Matrix looks ok, now we parse it - auto raw_mat = py_matrix.template unchecked<2>(); - if(c_order){ - value = matrix(nrows, ncols, false); - for (size_t r = 0; r < nrows; r++) { - for (size_t c = 0; c < ncols; c++) { - value(r, c) = raw_mat(r, c); - } - } - } else { - value = matrix::copy_from_buffer(nrows, ncols, static_cast(py_matrix.request().ptr)); + bool load(py::handle src, bool convert) { + // TODO: Check if make sense have to flavors of matrix: F-style and C-style + auto py_matrix = py::cast>(src); + auto c_order = py_matrix.attr("flags").attr("carray").template cast(); + if (py_matrix.ndim() != 2) { + throw std::invalid_argument( + std::string("Python: invalid matrix (empty array).")); + } + size_t nrows = py_matrix.shape(0); + size_t ncols = py_matrix.shape(1); + // Matrix looks ok, now we parse it + auto raw_mat = py_matrix.template unchecked<2>(); + if (c_order) { + value = matrix(nrows, ncols, false); + for (size_t r = 0; r < nrows; r++) { + for (size_t c = 0; c < ncols; c++) { + value(r, c) = raw_mat(r, c); + } } - return true; + } else { + value = matrix::copy_from_buffer( + nrows, ncols, static_cast(py_matrix.request().ptr)); + } + return true; } // Conversion part 2 (C++ -> Python): - static py::handle cast(matrix, py::return_value_policy policy, py::handle parent){ - throw std::runtime_error("Casting from matrix to python not supported."); + static py::handle cast(matrix, py::return_value_policy policy, + py::handle parent) { + throw std::runtime_error("Casting from matrix to python not supported."); } }; -template <> struct type_caster{ - using base = type_caster_base; +template <> +struct type_caster { + using base = type_caster_base; + public: - PYBIND11_TYPE_CASTER(AER::Clifford::Clifford, _("clifford")); - // Conversion part 1 (Python->C++): - bool load(py::handle src, bool convert){ - AER::Clifford::build_from(src, value); - return true; - } - // Conversion part 2 (C++ -> Python): - static py::handle cast(AER::Clifford::Clifford, py::return_value_policy policy, py::handle parent){ - throw std::runtime_error("Casting from Clifford to python not supported."); - } + PYBIND11_TYPE_CASTER(AER::Clifford::Clifford, _("clifford")); + // Conversion part 1 (Python->C++): + bool load(py::handle src, bool convert) { + AER::Clifford::build_from(src, value); + return true; + } + // Conversion part 2 (C++ -> Python): + static py::handle cast(AER::Clifford::Clifford, + py::return_value_policy policy, py::handle parent) { + throw std::runtime_error("Casting from Clifford to python not supported."); + } }; -} -} +} // namespace detail +} // namespace pybind11 #endif // _aer_framework_pybind_casts_hpp_ \ No newline at end of file diff --git a/src/framework/pybind_json.hpp b/src/framework/pybind_json.hpp old mode 100755 new mode 100644 index 6aa9dc89da..7ac889c3c2 --- a/src/framework/pybind_json.hpp +++ b/src/framework/pybind_json.hpp @@ -26,18 +26,17 @@ #include #include #include -#include #include -#include #include +#include #include "misc/warnings.hpp" DISABLE_WARNING_PUSH -#include #include -#include #include #include +#include +#include #include DISABLE_WARNING_POP @@ -109,9 +108,9 @@ json_t numpy_to_json_2d(py::array_t arr); template json_t numpy_to_json_3d(py::array_t arr); -json_t iterable_to_json_list(const py::handle& obj); +json_t iterable_to_json_list(const py::handle &obj); -} //end namespace JSON +} // end namespace JSON /******************************************************************************* * @@ -125,190 +124,195 @@ json_t iterable_to_json_list(const py::handle& obj); template json_t JSON::numpy_to_json_1d(py::array_t arr) { - py::buffer_info buf = arr.request(); - if (buf.ndim != 1) { - throw std::runtime_error("Number of dims must be 1"); - } + py::buffer_info buf = arr.request(); + if (buf.ndim != 1) { + throw std::runtime_error("Number of dims must be 1"); + } - T *ptr = (T *) buf.ptr; - size_t D0 = buf.shape[0]; + T *ptr = (T *)buf.ptr; + size_t D0 = buf.shape[0]; - std::vector tbr; // to be returned - for (size_t n0 = 0; n0 < D0; n0++) - tbr.push_back(ptr[n0]); + std::vector tbr; // to be returned + for (size_t n0 = 0; n0 < D0; n0++) + tbr.push_back(ptr[n0]); - return std::move(tbr); + return std::move(tbr); } template json_t JSON::numpy_to_json_2d(py::array_t arr) { - py::buffer_info buf = arr.request(); - if (buf.ndim != 2) { - throw std::runtime_error("Number of dims must be 2"); - } - - T *ptr = (T *) buf.ptr; - size_t D0 = buf.shape[0]; - size_t D1 = buf.shape[1]; - - std::vector > tbr; // to be returned - for (size_t n0 = 0; n0 < D0; n0++) { - std::vector tbr1; - for (size_t n1 = 0; n1 < D1; n1++) { - tbr1.push_back(ptr[n1 + D1*n0]); - } - tbr.push_back(tbr1); + py::buffer_info buf = arr.request(); + if (buf.ndim != 2) { + throw std::runtime_error("Number of dims must be 2"); + } + + T *ptr = (T *)buf.ptr; + size_t D0 = buf.shape[0]; + size_t D1 = buf.shape[1]; + + std::vector> tbr; // to be returned + for (size_t n0 = 0; n0 < D0; n0++) { + std::vector tbr1; + for (size_t n1 = 0; n1 < D1; n1++) { + tbr1.push_back(ptr[n1 + D1 * n0]); } + tbr.push_back(tbr1); + } - return std::move(tbr); - + return std::move(tbr); } template json_t JSON::numpy_to_json_3d(py::array_t arr) { - py::buffer_info buf = arr.request(); - if (buf.ndim != 3) { - throw std::runtime_error("Number of dims must be 3"); - } - T *ptr = (T *) buf.ptr; - size_t D0 = buf.shape[0]; - size_t D1 = buf.shape[1]; - size_t D2 = buf.shape[2]; - - // to be returned - std::vector > > tbr; - for (size_t n0 = 0; n0 < D0; n0++) { - std::vector > tbr1; - for (size_t n1 = 0; n1 < D1; n1++) { - std::vector tbr2; - for (size_t n2 = 0; n2 < D2; n2++) { - tbr2.push_back(ptr[n2 + D2*(n1 + D1*n0)]); - } - tbr1.push_back(tbr2); - } - tbr.push_back(tbr1); + py::buffer_info buf = arr.request(); + if (buf.ndim != 3) { + throw std::runtime_error("Number of dims must be 3"); + } + T *ptr = (T *)buf.ptr; + size_t D0 = buf.shape[0]; + size_t D1 = buf.shape[1]; + size_t D2 = buf.shape[2]; + + // to be returned + std::vector>> tbr; + for (size_t n0 = 0; n0 < D0; n0++) { + std::vector> tbr1; + for (size_t n1 = 0; n1 < D1; n1++) { + std::vector tbr2; + for (size_t n2 = 0; n2 < D2; n2++) { + tbr2.push_back(ptr[n2 + D2 * (n1 + D1 * n0)]); + } + tbr1.push_back(tbr2); } + tbr.push_back(tbr1); + } - return std::move(tbr); - + return std::move(tbr); } template json_t JSON::numpy_to_json(py::array_t arr) { - py::buffer_info buf = arr.request(); - - if (buf.ndim == 1) { - return JSON::numpy_to_json_1d(arr); - } else if (buf.ndim == 2) { - return JSON::numpy_to_json_2d(arr); - } else if (buf.ndim == 3) { - return JSON::numpy_to_json_3d(arr); - } else { - throw std::runtime_error("Invalid number of dimensions!"); - } - json_t tbr; - return tbr; + py::buffer_info buf = arr.request(); + + if (buf.ndim == 1) { + return JSON::numpy_to_json_1d(arr); + } else if (buf.ndim == 2) { + return JSON::numpy_to_json_2d(arr); + } else if (buf.ndim == 3) { + return JSON::numpy_to_json_3d(arr); + } else { + throw std::runtime_error("Invalid number of dimensions!"); + } + json_t tbr; + return tbr; } -json_t JSON::iterable_to_json_list(const py::handle& obj){ - json_t js = nl::json::array(); - for (py::handle value: obj) { - js.push_back(value); - } - return js; +json_t JSON::iterable_to_json_list(const py::handle &obj) { + json_t js = nl::json::array(); + for (py::handle value : obj) { + js.push_back(value); + } + return js; } void std::to_json(json_t &js, const py::handle &obj) { - static py::object PyNoiseModel = py::module::import("qiskit_aer.noise.noise_model").attr("NoiseModel"); - static py::object PyQasmQobj = py::module::import("qiskit.qobj.qasm_qobj").attr("QasmQobj"); - static py::object PyQasmQobjHeader = py::module::import("qiskit.qobj.common").attr("QobjExperimentHeader"); - if (py::isinstance(obj)) { - js = obj.cast(); - } else if (py::isinstance(obj)) { - js = obj.cast(); - } else if (py::isinstance(obj)) { - js = obj.cast(); - } else if (py::isinstance(obj)) { - js = obj.cast(); - } else if (py::isinstance(obj) || py::isinstance(obj)) { - js = JSON::iterable_to_json_list(obj); - } else if (py::isinstance(obj)) { - for (auto item : py::cast(obj)) { - js[item.first.cast()] = item.second; - } - } else if (py::isinstance >(obj)) { - js = JSON::numpy_to_json(obj.cast >()); - } else if (py::isinstance > >(obj)) { - js = JSON::numpy_to_json(obj.cast, py::array::c_style> >()); - } else if (obj.is_none()) { - return; - } else if (py::isinstance(obj, PyNoiseModel)){ - std::to_json(js, obj.attr("to_dict")()); - } else if (py::isinstance(obj, PyQasmQobj)){ - std::to_json(js, obj.attr("to_dict")()); - } else if (py::isinstance(obj, PyQasmQobjHeader)){ - std::to_json(js, obj.attr("to_dict")()); + static py::object PyNoiseModel = + py::module::import("qiskit_aer.noise.noise_model").attr("NoiseModel"); + static py::object PyQasmQobj = + py::module::import("qiskit.qobj.qasm_qobj").attr("QasmQobj"); + static py::object PyQasmQobjHeader = + py::module::import("qiskit.qobj.common").attr("QobjExperimentHeader"); + if (py::isinstance(obj)) { + js = obj.cast(); + } else if (py::isinstance(obj)) { + js = obj.cast(); + } else if (py::isinstance(obj)) { + js = obj.cast(); + } else if (py::isinstance(obj)) { + js = obj.cast(); + } else if (py::isinstance(obj) || py::isinstance(obj)) { + js = JSON::iterable_to_json_list(obj); + } else if (py::isinstance(obj)) { + for (auto item : py::cast(obj)) { + js[item.first.cast()] = item.second; + } + } else if (py::isinstance>(obj)) { + js = JSON::numpy_to_json( + obj.cast>()); + } else if (py::isinstance>>(obj)) { + js = JSON::numpy_to_json( + obj.cast, py::array::c_style>>()); + } else if (obj.is_none()) { + return; + } else if (py::isinstance(obj, PyNoiseModel)) { + std::to_json(js, obj.attr("to_dict")()); + } else if (py::isinstance(obj, PyQasmQobj)) { + std::to_json(js, obj.attr("to_dict")()); + } else if (py::isinstance(obj, PyQasmQobjHeader)) { + std::to_json(js, obj.attr("to_dict")()); + } else { + auto type_str = std::string(py::str(obj.get_type())); + if (type_str == "" || + type_str == "" || + type_str == "" || + type_str == "") { + auto tmp = obj.cast>(); + js.push_back(tmp.real()); + js.push_back(tmp.imag()); + } else if (type_str == "" || + type_str == "" || + type_str == "" || + type_str == "") { + js = obj.cast(); + } else if (type_str == "" || + type_str == "") { + js = obj.cast(); + } else if (py::isinstance( + obj)) { // last one to avoid intercepting numpy arrays, etc + js = JSON::iterable_to_json_list(obj); } else { - auto type_str = std::string(py::str(obj.get_type())); - if ( type_str == "" - || type_str == "" - || type_str == "" - || type_str == "" ) { - auto tmp = obj.cast>(); - js.push_back(tmp.real()); - js.push_back(tmp.imag()); - } else if ( type_str == "" - || type_str == "" - || type_str == "" - || type_str == "" ) { - js = obj.cast(); - } else if ( type_str == "" - || type_str == "" ) { - js = obj.cast(); - } else if ( py::isinstance(obj) ){ // last one to avoid intercepting numpy arrays, etc - js = JSON::iterable_to_json_list(obj); - } else { - throw std::runtime_error("to_json not implemented for this type of object: " + std::string(py::str(obj.get_type()))); - } + throw std::runtime_error( + "to_json not implemented for this type of object: " + + std::string(py::str(obj.get_type()))); } + } } void std::from_json(const json_t &js, py::object &o) { - if (js.is_boolean()) { - o = py::bool_(js.get()); - } else if (js.is_number()) { - if (js.is_number_float()) { - o = py::float_(js.get()); - } else if (js.is_number_unsigned()) { - o = py::int_(js.get()); - } else { - o = py::int_(js.get()); - } - } else if (js.is_string()) { - o = py::str(js.get()); - } else if (js.is_array()) { - std::vector obj(js.size()); - for (auto i = 0; i < js.size(); i++) - { - py::object tmp; - from_json(js[i], tmp); - obj[i] = tmp; - } - o = py::cast(obj); - } else if (js.is_object()) { - py::dict obj; - for (json_t::const_iterator it = js.cbegin(); it != js.cend(); ++it) - { - py::object tmp; - from_json(it.value(), tmp); - obj[py::str(it.key())] = tmp; - } - o = std::move(obj); - } else if (js.is_null()) { - o = py::none(); + if (js.is_boolean()) { + o = py::bool_(js.get()); + } else if (js.is_number()) { + if (js.is_number_float()) { + o = py::float_(js.get()); + } else if (js.is_number_unsigned()) { + o = py::int_(js.get()); } else { - throw std::runtime_error("from_json not implemented for this json::type: " + js.dump()); + o = py::int_(js.get()); + } + } else if (js.is_string()) { + o = py::str(js.get()); + } else if (js.is_array()) { + std::vector obj(js.size()); + for (auto i = 0; i < js.size(); i++) { + py::object tmp; + from_json(js[i], tmp); + obj[i] = tmp; + } + o = py::cast(obj); + } else if (js.is_object()) { + py::dict obj; + for (json_t::const_iterator it = js.cbegin(); it != js.cend(); ++it) { + py::object tmp; + from_json(it.value(), tmp); + obj[py::str(it.key())] = tmp; } + o = std::move(obj); + } else if (js.is_null()) { + o = py::none(); + } else { + throw std::runtime_error("from_json not implemented for this json::type: " + + js.dump()); + } } //------------------------------------------------------------------------------ diff --git a/src/framework/python_parser.hpp b/src/framework/python_parser.hpp index e2867a8a0c..1e089cd349 100644 --- a/src/framework/python_parser.hpp +++ b/src/framework/python_parser.hpp @@ -19,136 +19,139 @@ #include "json_parser.hpp" #include "pybind_json.hpp" -namespace AER{ +namespace AER { template <> struct Parser { - Parser() = delete; - - static bool check_key(const std::string& key, const py::handle& po){ - if(py::isinstance(po)){ - return !py::cast(po)[key.c_str()].is_none(); - } - return py::hasattr(po, key.c_str()); - } - - static bool check_keys(const std::vector& keys, const py::handle& po) { - bool pass = true; - for (const auto &s : keys){ - pass &= check_key(s, po); - } - return pass; - } - - static py::object get_py_value(const std::string& key, const py::handle& po){ - if(py::isinstance(po)){ - return py::cast(po)[key.c_str()]; - } - return po.attr(key.c_str()); - } - - static bool get_value(py::object& var, const std::string& key, const py::handle& po) { - if(check_key(key, po)) { - var = get_py_value(key, po); - return true; - } else { - return false; - } - } - - template - static bool get_value(T &var, const std::string& key, const py::handle& po){ - if(check_key(key, po)) { - var = get_py_value(key, po).cast(); - return true; - } else { - return false; - } - } - - static void convert_to_json(json_t &var, const py::handle& po){ - if(py::hasattr(po, "to_dict")){ - std::to_json(var, po.attr("to_dict")()); - }else if(py::isinstance(po)){ - var = nl::json::array(); - for(auto item: po){ - json_t item_js; - convert_to_json(item_js, item); - var.push_back(item_js); - } - }else{ - std::to_json(var, po); - } - } - - static py::object get_value(const std::string& key, const py::handle& po){ - return get_py_value(key, po); - } - - static bool is_array(const py::handle& po){ - return py::isinstance(po) || py::isinstance(po); - } - - static bool is_array(const std::string& key, const py::handle& po) { - py::object the_list = get_py_value(key, po); - return is_array(the_list); - } - - static bool is_list_like(const py::handle& po){ - return is_array(po) || py::isinstance(po); - } - - static py::list get_as_list(const py::handle& po){ - if(!is_list_like(po)){ - throw std::runtime_error("Object is not list like!"); - } - return py::cast(po); - } - - static py::list get_list(const std::string& key, const py::handle& po){ - py::object the_list = get_py_value(key, po); - if(!is_array(the_list)){ - throw std::runtime_error("Object " + key + "is not a list!"); - } - return py::cast(the_list); - } - - static bool is_number(const py::handle& po){ - return py::isinstance(po) || py::isinstance(po); - } - - static bool is_number(const std::string& key, const py::handle& po) { - py::object key_po = get_py_value(key, po); - return is_number(key_po); - } - - template - static T get_list_elem(const py::list& po, unsigned int i){ - return py::cast(po[i]).cast(); - } - - template - static T get_list_elem(const py::handle& po, unsigned int i){ - auto py_list = get_as_list(po); - return get_list_elem(py_list, i); - } - - static std::string dump(const py::handle& po){ - json_t js; - convert_to_json(js, po); - return js.dump(); - } + Parser() = delete; + + static bool check_key(const std::string &key, const py::handle &po) { + if (py::isinstance(po)) { + return !py::cast(po)[key.c_str()].is_none(); + } + return py::hasattr(po, key.c_str()); + } + + static bool check_keys(const std::vector &keys, + const py::handle &po) { + bool pass = true; + for (const auto &s : keys) { + pass &= check_key(s, po); + } + return pass; + } + + static py::object get_py_value(const std::string &key, const py::handle &po) { + if (py::isinstance(po)) { + return py::cast(po)[key.c_str()]; + } + return po.attr(key.c_str()); + } + + static bool get_value(py::object &var, const std::string &key, + const py::handle &po) { + if (check_key(key, po)) { + var = get_py_value(key, po); + return true; + } else { + return false; + } + } + + template + static bool get_value(T &var, const std::string &key, const py::handle &po) { + if (check_key(key, po)) { + var = get_py_value(key, po).cast(); + return true; + } else { + return false; + } + } + + static void convert_to_json(json_t &var, const py::handle &po) { + if (py::hasattr(po, "to_dict")) { + std::to_json(var, po.attr("to_dict")()); + } else if (py::isinstance(po)) { + var = nl::json::array(); + for (auto item : po) { + json_t item_js; + convert_to_json(item_js, item); + var.push_back(item_js); + } + } else { + std::to_json(var, po); + } + } + + static py::object get_value(const std::string &key, const py::handle &po) { + return get_py_value(key, po); + } + + static bool is_array(const py::handle &po) { + return py::isinstance(po) || py::isinstance(po); + } + + static bool is_array(const std::string &key, const py::handle &po) { + py::object the_list = get_py_value(key, po); + return is_array(the_list); + } + + static bool is_list_like(const py::handle &po) { + return is_array(po) || py::isinstance(po); + } + + static py::list get_as_list(const py::handle &po) { + if (!is_list_like(po)) { + throw std::runtime_error("Object is not list like!"); + } + return py::cast(po); + } + + static py::list get_list(const std::string &key, const py::handle &po) { + py::object the_list = get_py_value(key, po); + if (!is_array(the_list)) { + throw std::runtime_error("Object " + key + "is not a list!"); + } + return py::cast(the_list); + } + + static bool is_number(const py::handle &po) { + return py::isinstance(po) || py::isinstance(po); + } + + static bool is_number(const std::string &key, const py::handle &po) { + py::object key_po = get_py_value(key, po); + return is_number(key_po); + } + + template + static T get_list_elem(const py::list &po, unsigned int i) { + return py::cast(po[i]).cast(); + } + + template + static T get_list_elem(const py::handle &po, unsigned int i) { + auto py_list = get_as_list(po); + return get_list_elem(py_list, i); + } + + static std::string dump(const py::handle &po) { + json_t js; + convert_to_json(js, po); + return js.dump(); + } }; template <> -bool Parser::get_value(json_t &var, const std::string& key, const py::handle& po){ - py::object ret_po; - auto success = get_value(ret_po, key, po); - if(success){ - convert_to_json(var, ret_po); - } - return success; -} +bool Parser::get_value(json_t &var, const std::string &key, + const py::handle &po) { + py::object ret_po; + auto success = get_value(ret_po, key, po); + if (success) { + convert_to_json(var, ret_po); + } + return success; } +} // namespace AER #endif // _aer_framework_python_parser_hpp_ diff --git a/src/framework/qobj.hpp b/src/framework/qobj.hpp old mode 100755 new mode 100644 index 7eb7313a1b..d6cf1e32da --- a/src/framework/qobj.hpp +++ b/src/framework/qobj.hpp @@ -30,7 +30,7 @@ namespace AER { //============================================================================ class Qobj { - public: +public: //---------------------------------------------------------------- // Constructors //---------------------------------------------------------------- @@ -46,12 +46,12 @@ class Qobj { //---------------------------------------------------------------- // Data //---------------------------------------------------------------- - std::string id; // qobj identifier passed to result - std::string type = "QASM"; // currently we only support QASM - std::vector circuits; // List of circuits - json_t header; // (optional) passed through to result - json_t config; // (optional) qobj level config data - Noise::NoiseModel noise_model; // (optional) noise model + std::string id; // qobj identifier passed to result + std::string type = "QASM"; // currently we only support QASM + std::vector> circuits; // List of circuits + json_t header; // (optional) passed through to result + json_t config; // (optional) qobj level config data + Noise::NoiseModel noise_model; // (optional) noise model }; //============================================================================ @@ -67,7 +67,7 @@ Qobj::Qobj(const inputdata_t &input) { if (Parser::get_value(id, "qobj_id", input) == false) { throw std::invalid_argument(R"(Invalid qobj: no "qobj_id" field)"); }; - Parser::get_value(type, "type", input); + Parser::get_value(type, "type", input); if (type != "QASM") { throw std::invalid_argument(R"(Invalid qobj: "type" != "QASM".)"); }; @@ -100,12 +100,13 @@ Qobj::Qobj(const inputdata_t &input) { } // Check for fixed simulator seed - // If simulator seed is set, each experiment will be set to a fixed (but different) seed - // Otherwise a random seed will be chosen for each experiment + // If simulator seed is set, each experiment will be set to a fixed (but + // different) seed Otherwise a random seed will be chosen for each experiment int_t seed = -1; uint_t seed_shift = 0; - bool has_simulator_seed = Parser::get_value(seed, "seed_simulator", config); // config always json - const auto& circs = Parser::get_list("experiments", input); + bool has_simulator_seed = Parser::get_value( + seed, "seed_simulator", config); // config always json + const auto &circs = Parser::get_list("experiments", input); const size_t num_circs = circs.size(); // Check if parameterized qobj @@ -116,7 +117,7 @@ Qobj::Qobj(const inputdata_t &input) { // i is the instruction index in the experiment // j is the param index in the instruction // pars = [par0, par1, ...] is a list of different parameterizations - using pos_t = std::pair; + using pos_t = std::pair; using exp_params_t = std::vector>>; std::vector param_table; Parser::get_value(param_table, "parameterizations", config); @@ -128,46 +129,57 @@ Qobj::Qobj(const inputdata_t &input) { } // Load circuits - for (size_t i=0; i(circs[i]), config, truncation); + auto circuit = std::make_shared( + static_cast(circs[i]), config, truncation); // Non parameterized circuit - circuits.push_back(std::move(circuit)); + circuits.push_back(circuit); } else { // Get base circuit from qobj without truncation - Circuit circuit(static_cast(circs[i]), config, false); + auto circuit = std::make_shared( + static_cast(circs[i]), config, false); // Load different parameterizations of the initial circuit const auto circ_params = param_table[i]; const size_t num_params = circ_params[0].second.size(); - const size_t num_instr = circuit.ops.size(); - for (size_t j=0; jops.size(); + for (size_t j = 0; j < num_params; j++) { // Make a copy of the initial circuit - Circuit param_circuit = circuit; + auto param_circuit = std::make_shared(*circuit); for (const auto ¶ms : circ_params) { const auto instr_pos = params.first.first; const auto param_pos = params.first.second; // Validation - if (instr_pos >= num_instr) { - throw std::invalid_argument(R"(Invalid parameterized qobj: instruction position out of range)"); + if (instr_pos == AER::Config::GLOBAL_PHASE_POS) { + // negative position is for global phase + circuit->global_phase_angle = params.second[j]; + } else { + if (instr_pos >= num_instr) { + throw std::invalid_argument( + R"(Invalid parameterized qobj: instruction position out of range)"); + } + auto &op = param_circuit->ops[instr_pos]; + if (param_pos >= op.params.size()) { + throw std::invalid_argument( + R"(Invalid parameterized qobj: instruction param position out of range)"); + } + if (j >= params.second.size()) { + throw std::invalid_argument( + R"(Invalid parameterized qobj: parameterization value out of range)"); + } + // Update the param + op.params[param_pos] = params.second[j]; } - auto &op = param_circuit.ops[instr_pos]; - if (param_pos >= op.params.size()) { - throw std::invalid_argument(R"(Invalid parameterized qobj: instruction param position out of range)"); - } - if (j >= params.second.size()) { - throw std::invalid_argument(R"(Invalid parameterized qobj: parameterization value out of range)"); - } - // Update the param - op.params[param_pos] = params.second[j]; } // Run truncation. - // TODO: Truncation should be performed and parameters should be resolved after it. - // However, parameters are associated with indices of instructions, which can be changed in truncation. - // Therefore, current implementation performs truncation for each parameter set. + // TODO: Truncation should be performed and parameters should be + // resolved after it. However, parameters are associated with indices of + // instructions, which can be changed in truncation. Therefore, current + // implementation performs truncation for each parameter set. if (truncation) - param_circuit.set_params(true); - circuits.push_back(std::move(param_circuit)); + param_circuit->set_params(true); + circuits.push_back(param_circuit); } } } @@ -175,15 +187,15 @@ Qobj::Qobj(const inputdata_t &input) { // We shift the seed for each successive experiment // So that results aren't correlated between experiments if (!has_simulator_seed) { - seed = circuits[0].seed; + seed = circuits[0]->seed; } - for (auto& circuit : circuits) { - circuit.seed = seed + seed_shift; - seed_shift += 2113; // Shift the seed + for (auto &circuit : circuits) { + circuit->seed = seed + seed_shift; + seed_shift += 2113; // Shift the seed } } //------------------------------------------------------------------------------ -} // namespace AER +} // namespace AER //------------------------------------------------------------------------------ #endif diff --git a/src/framework/results/data/data.hpp b/src/framework/results/data/data.hpp index 878c117af0..82ccdd8650 100644 --- a/src/framework/results/data/data.hpp +++ b/src/framework/results/data/data.hpp @@ -16,22 +16,22 @@ #define _aer_framework_results_data_hpp_ // Data primatives -#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/accum_data.hpp" #include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/list_data.hpp" #include "framework/results/data/subtypes/single_data.hpp" // Data Containers -#include "framework/results/data/mixins/data_creg.hpp" -#include "framework/results/data/mixins/data_rvalue.hpp" -#include "framework/results/data/mixins/data_rvector.hpp" -#include "framework/results/data/mixins/data_rdict.hpp" +#include "framework/results/data/mixins/data_cdict.hpp" #include "framework/results/data/mixins/data_cmatrix.hpp" +#include "framework/results/data/mixins/data_creg.hpp" #include "framework/results/data/mixins/data_cvector.hpp" -#include "framework/results/data/mixins/data_cdict.hpp" #include "framework/results/data/mixins/data_json.hpp" #include "framework/results/data/mixins/data_mps.hpp" +#include "framework/results/data/mixins/data_rdict.hpp" +#include "framework/results/data/mixins/data_rvalue.hpp" +#include "framework/results/data/mixins/data_rvector.hpp" namespace AER { @@ -65,60 +65,60 @@ struct Data : public DataCreg, //---------------------------------------------------------------- template void add_single(const T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_single(T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_single(T &&data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); //---------------------------------------------------------------- // Add list data //---------------------------------------------------------------- template void add_list(const T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_list(T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_list(T &&data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); //---------------------------------------------------------------- // Add accum data //---------------------------------------------------------------- template void add_accum(const T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_accum(T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_accum(T &&data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); //---------------------------------------------------------------- // Add average data //---------------------------------------------------------------- template void add_average(const T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_average(T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add_average(T &&data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); //---------------------------------------------------------------- // Utility and config @@ -162,87 +162,86 @@ json_t Data::to_json() { return result; } - template void Data::add_single(const T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_single(T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_single(T &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(std::move(data), outer_key, inner_keys...); } template void Data::add_list(const T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_list(T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_list(T &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(std::move(data), outer_key, inner_keys...); } template void Data::add_accum(const T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_accum(T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_accum(T &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(std::move(data), outer_key, inner_keys...); } template void Data::add_average(const T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_average(T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Data::add_average(T &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(std::move(data), outer_key, inner_keys...); } diff --git a/src/framework/results/data/metadata.hpp b/src/framework/results/data/metadata.hpp index 969179ca3a..cf7cb39bb1 100644 --- a/src/framework/results/data/metadata.hpp +++ b/src/framework/results/data/metadata.hpp @@ -33,15 +33,15 @@ struct Metadata : public DataMap, //---------------------------------------------------------------- template void add(const json_t &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add(json_t &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template void add(json_t &&data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); //---------------------------------------------------------------- // Add general metadata @@ -51,13 +51,13 @@ struct Metadata : public DataMap, //---------------------------------------------------------------- template void add(const T &data, const std::string &outer_key, - const Args &... inner_keys); + const Args &...inner_keys); template - void add(T &data, const std::string &outer_key, const Args &... inner_keys); + void add(T &data, const std::string &outer_key, const Args &...inner_keys); template - void add(T &&data, const std::string &outer_key, const Args &... inner_keys); + void add(T &&data, const std::string &outer_key, const Args &...inner_keys); // Serialize engine data to JSON json_t to_json(); @@ -87,7 +87,7 @@ json_t Metadata::to_json() { template void Metadata::add(const T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { json_t tmp = data; DataMap::add( std::move(tmp), outer_key, inner_keys...); @@ -95,7 +95,7 @@ void Metadata::add(const T &data, const std::string &outer_key, template void Metadata::add(T &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { json_t tmp = data; DataMap::add( std::move(tmp), outer_key, inner_keys...); @@ -103,7 +103,7 @@ void Metadata::add(T &data, const std::string &outer_key, template void Metadata::add(T &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { json_t tmp = data; DataMap::add( std::move(tmp), outer_key, inner_keys...); @@ -111,21 +111,21 @@ void Metadata::add(T &&data, const std::string &outer_key, template void Metadata::add(const json_t &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Metadata::add(json_t &data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add(data, outer_key, inner_keys...); } template void Metadata::add(json_t &&data, const std::string &outer_key, - const Args &... inner_keys) { + const Args &...inner_keys) { DataMap::add( std::move(data), outer_key, inner_keys...); } diff --git a/src/framework/results/data/mixins/data_cdict.hpp b/src/framework/results/data/mixins/data_cdict.hpp index 16d1e3b8d7..69b4670c3b 100644 --- a/src/framework/results/data/mixins/data_cdict.hpp +++ b/src/framework/results/data/mixins/data_cdict.hpp @@ -29,10 +29,11 @@ namespace AER { // Result container for Qiskit-Aer //============================================================================ -struct DataCDict : public DataMap, 1>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 2> { +struct DataCDict + : public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2> { // Serialize engine data to JSON void add_to_json(json_t &result); @@ -46,10 +47,14 @@ struct DataCDict : public DataMap, //------------------------------------------------------------------------------ DataCDict &DataCDict::combine(DataCDict &&other) { - DataMap, 1>::combine(std::move(other)); - DataMap, 2>::combine(std::move(other)); - DataMap, 1>::combine(std::move(other)); - DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine( + std::move(other)); + DataMap, 2>::combine( + std::move(other)); + DataMap, 1>::combine( + std::move(other)); + DataMap, 2>::combine( + std::move(other)); return *this; } diff --git a/src/framework/results/data/mixins/data_cmatrix.hpp b/src/framework/results/data/mixins/data_cmatrix.hpp index 0aa1920603..f1e23b6707 100644 --- a/src/framework/results/data/mixins/data_cmatrix.hpp +++ b/src/framework/results/data/mixins/data_cmatrix.hpp @@ -15,9 +15,9 @@ #ifndef _aer_framework_results_data_cmatrix_hpp_ #define _aer_framework_results_data_cmatrix_hpp_ -#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/accum_data.hpp" #include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/list_data.hpp" #include "framework/results/data/subtypes/single_data.hpp" #include "framework/types.hpp" @@ -28,23 +28,22 @@ namespace AER { // Result container for Qiskit-Aer //============================================================================ -struct DataCMatrix : - public DataMap, 1>, - public DataMap, 1>, - public DataMap, 2>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 1>, - public DataMap, 2>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 1>, - public DataMap, 2>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 1>, - public DataMap, 2>, - public DataMap, 2> { +struct DataCMatrix : public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2> { // Serialize engine data to JSON void add_to_json(json_t &result); diff --git a/src/framework/results/data/mixins/data_creg.hpp b/src/framework/results/data/mixins/data_creg.hpp index 73a68acb11..f97b769ce6 100644 --- a/src/framework/results/data/mixins/data_creg.hpp +++ b/src/framework/results/data/mixins/data_creg.hpp @@ -26,8 +26,8 @@ namespace AER { // Result container for Qiskit-Aer //============================================================================ -struct DataCreg : public DataMap, // Counts - public DataMap // Memory +struct DataCreg : public DataMap, // Counts + public DataMap // Memory { // Serialize engine data to JSON void add_to_json(json_t &result); diff --git a/src/framework/results/data/mixins/data_mps.hpp b/src/framework/results/data/mixins/data_mps.hpp index 21e20619c6..281a91bf70 100644 --- a/src/framework/results/data/mixins/data_mps.hpp +++ b/src/framework/results/data/mixins/data_mps.hpp @@ -25,7 +25,7 @@ namespace AER { //============================================================================ // Result container for Qiskit-Aer //============================================================================ -//using cmat = std::vector>; +// using cmat = std::vector>; struct DataMPS : public DataMap, public DataMap, public DataMap, diff --git a/src/framework/results/data/mixins/data_rdict.hpp b/src/framework/results/data/mixins/data_rdict.hpp index 8f9e0f55a2..9e470267b4 100644 --- a/src/framework/results/data/mixins/data_rdict.hpp +++ b/src/framework/results/data/mixins/data_rdict.hpp @@ -15,12 +15,12 @@ #ifndef _aer_framework_results_data_rdict_hpp_ #define _aer_framework_results_data_rdict_hpp_ -#include -#include "framework/results/data/subtypes/data_map.hpp" -#include "framework/results/data/subtypes/list_data.hpp" #include "framework/results/data/subtypes/accum_data.hpp" #include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" #include "framework/types.hpp" +#include namespace AER { @@ -28,12 +28,13 @@ namespace AER { // Result container for Qiskit-Aer //============================================================================ -struct DataRDict : public DataMap, 1>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 2>, - public DataMap, 1>, - public DataMap, 2> { +struct DataRDict + : public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2> { // Serialize engine data to JSON void add_to_json(json_t &result); @@ -47,12 +48,18 @@ struct DataRDict : public DataMap, 1>, //------------------------------------------------------------------------------ DataRDict &DataRDict::combine(DataRDict &&other) { - DataMap, 1>::combine(std::move(other)); - DataMap, 2>::combine(std::move(other)); - DataMap, 1>::combine(std::move(other)); - DataMap, 2>::combine(std::move(other)); - DataMap, 1>::combine(std::move(other)); - DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine( + std::move(other)); + DataMap, 2>::combine( + std::move(other)); + DataMap, 1>::combine( + std::move(other)); + DataMap, 2>::combine( + std::move(other)); + DataMap, 1>::combine( + std::move(other)); + DataMap, 2>::combine( + std::move(other)); return *this; } diff --git a/src/framework/results/data/mixins/data_rvalue.hpp b/src/framework/results/data/mixins/data_rvalue.hpp index 901c84eb30..8f89412ea4 100644 --- a/src/framework/results/data/mixins/data_rvalue.hpp +++ b/src/framework/results/data/mixins/data_rvalue.hpp @@ -15,9 +15,9 @@ #ifndef _aer_framework_results_data_rvalue_hpp_ #define _aer_framework_results_data_rvalue_hpp_ -#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/accum_data.hpp" #include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/list_data.hpp" #include "framework/results/data/subtypes/single_data.hpp" #include "framework/types.hpp" @@ -28,13 +28,12 @@ namespace AER { // Result container for Qiskit-Aer //============================================================================ -struct DataRValue : - public DataMap, - public DataMap, - public DataMap, - public DataMap, - public DataMap, - public DataMap { +struct DataRValue : public DataMap, + public DataMap, + public DataMap, + public DataMap, + public DataMap, + public DataMap { // Serialize engine data to JSON void add_to_json(json_t &result); diff --git a/src/framework/results/data/mixins/data_rvector.hpp b/src/framework/results/data/mixins/data_rvector.hpp index ab283f7ac1..6688b80db1 100644 --- a/src/framework/results/data/mixins/data_rvector.hpp +++ b/src/framework/results/data/mixins/data_rvector.hpp @@ -15,11 +15,11 @@ #ifndef _aer_framework_results_data_rvector_hpp_ #define _aer_framework_results_data_rvector_hpp_ +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" #include "framework/results/data/subtypes/data_map.hpp" #include "framework/results/data/subtypes/list_data.hpp" #include "framework/results/data/subtypes/single_data.hpp" -#include "framework/results/data/subtypes/accum_data.hpp" -#include "framework/results/data/subtypes/average_data.hpp" #include "framework/types.hpp" namespace AER { diff --git a/src/framework/results/data/mixins/pybind_data_cdict.hpp b/src/framework/results/data/mixins/pybind_data_cdict.hpp old mode 100755 new mode 100644 index 7bcddf3cdc..9f025e39b2 --- a/src/framework/results/data/mixins/pybind_data_cdict.hpp +++ b/src/framework/results/data/mixins/pybind_data_cdict.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataCDict &&data); // Move an DataCDict container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataCDict &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,10 +43,26 @@ py::object AerToPy::to_python(AER::DataCDict &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataCDict &&data) { - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_cmatrix.hpp b/src/framework/results/data/mixins/pybind_data_cmatrix.hpp old mode 100755 new mode 100644 index 4b53de3c8a..994adc2e8b --- a/src/framework/results/data/mixins/pybind_data_cmatrix.hpp +++ b/src/framework/results/data/mixins/pybind_data_cmatrix.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataCMatrix &&data); // Move an DataCMatrix container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataCMatrix &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,22 +43,70 @@ py::object AerToPy::to_python(AER::DataCMatrix &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataCMatrix &&data) { - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast< + AER::DataMap, 1> &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast< + AER::DataMap, 2> &&>(data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_creg.hpp b/src/framework/results/data/mixins/pybind_data_creg.hpp old mode 100755 new mode 100644 index 9585154c42..2156dd1d9b --- a/src/framework/results/data/mixins/pybind_data_creg.hpp +++ b/src/framework/results/data/mixins/pybind_data_creg.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataCreg &&data); // Move an DataCreg container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataCreg &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,8 +43,12 @@ py::object AerToPy::to_python(AER::DataCreg &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataCreg &&data) { - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast &&>(data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_cvector.hpp b/src/framework/results/data/mixins/pybind_data_cvector.hpp old mode 100755 new mode 100644 index 3115cc8b09..15c7991780 --- a/src/framework/results/data/mixins/pybind_data_cvector.hpp +++ b/src/framework/results/data/mixins/pybind_data_cvector.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataCVector &&data); // Move an DataCVector container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataCVector &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,14 +43,38 @@ py::object AerToPy::to_python(AER::DataCVector &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataCVector &&data) { - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_json.hpp b/src/framework/results/data/mixins/pybind_data_json.hpp old mode 100755 new mode 100644 index 89759ed3ce..51bebb049b --- a/src/framework/results/data/mixins/pybind_data_json.hpp +++ b/src/framework/results/data/mixins/pybind_data_json.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataJSON &&data); // Move an DataJSON container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataJSON &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,10 +43,14 @@ py::object AerToPy::to_python(AER::DataJSON &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataJSON &&data) { - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_mps.hpp b/src/framework/results/data/mixins/pybind_data_mps.hpp old mode 100755 new mode 100644 index 7196d884a4..b7be3fcc00 --- a/src/framework/results/data/mixins/pybind_data_mps.hpp +++ b/src/framework/results/data/mixins/pybind_data_mps.hpp @@ -25,7 +25,8 @@ namespace AerToPy { // Move mps_container_t to python object -template <> py::object to_python(AER::mps_container_t &&mps); +template <> +py::object to_python(AER::mps_container_t &&mps); // Move an DataMPS container object to a new Python dict py::object to_python(AER::DataMPS &&data); @@ -33,21 +34,21 @@ py::object to_python(AER::DataMPS &&data); // Move an DataMPS container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataMPS &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations //============================================================================ -template <> py::object AerToPy::to_python(AER::mps_container_t &&data) { +template <> +py::object AerToPy::to_python(AER::mps_container_t &&data) { py::list mats; - for (auto& pair: data.first) { + for (auto &pair : data.first) { mats.append(py::make_tuple(AerToPy::to_python(std::move(pair.first)), AerToPy::to_python(std::move(pair.second)))); } py::list vecs; - for (auto&& vec: data.second) { + for (auto &&vec : data.second) { vecs.append(AerToPy::to_python(std::move(vec))); } return py::make_tuple(std::move(mats), std::move(vecs)); @@ -60,10 +61,22 @@ py::object AerToPy::to_python(AER::DataMPS &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataMPS &&data) { - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast &&>( + data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_rdict.hpp b/src/framework/results/data/mixins/pybind_data_rdict.hpp old mode 100755 new mode 100644 index 03e2a0a30c..1a92571c63 --- a/src/framework/results/data/mixins/pybind_data_rdict.hpp +++ b/src/framework/results/data/mixins/pybind_data_rdict.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataRDict &&data); // Move an DataRDict container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataRDict &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,12 +43,32 @@ py::object AerToPy::to_python(AER::DataRDict &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataRDict &&data) { - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> + &&>(data)); + AerToPy::add_to_python( + pydata, + static_cast< + AER::DataMap, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast< + AER::DataMap, 2> &&>( + data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_rvalue.hpp b/src/framework/results/data/mixins/pybind_data_rvalue.hpp old mode 100755 new mode 100644 index 15d9f18469..dfed06ea4c --- a/src/framework/results/data/mixins/pybind_data_rvalue.hpp +++ b/src/framework/results/data/mixins/pybind_data_rvalue.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataRValue &&data); // Move an DataRValue container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataRValue &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,12 +43,18 @@ py::object AerToPy::to_python(AER::DataRValue &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataRValue &&data) { - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); - AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); + AerToPy::add_to_python( + pydata, static_cast &&>(data)); } #endif diff --git a/src/framework/results/data/mixins/pybind_data_rvector.hpp b/src/framework/results/data/mixins/pybind_data_rvector.hpp old mode 100755 new mode 100644 index 58ca61ec9d..212bef99bb --- a/src/framework/results/data/mixins/pybind_data_rvector.hpp +++ b/src/framework/results/data/mixins/pybind_data_rvector.hpp @@ -30,8 +30,7 @@ py::object to_python(AER::DataRVector &&data); // Move an DataRVector container object to an existing new Python dict void add_to_python(py::dict &pydata, AER::DataRVector &&data); -} //end namespace AerToPy - +} // end namespace AerToPy //============================================================================ // Implementations @@ -44,12 +43,30 @@ py::object AerToPy::to_python(AER::DataRVector &&data) { } void AerToPy::add_to_python(py::dict &pydata, AER::DataRVector &&data) { - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); - AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 1> &&>( + data)); + AerToPy::add_to_python( + pydata, + static_cast, 2> &&>( + data)); } #endif diff --git a/src/framework/results/data/pybind_data.hpp b/src/framework/results/data/pybind_data.hpp old mode 100755 new mode 100644 index dcd25fb17f..53b338c372 --- a/src/framework/results/data/pybind_data.hpp +++ b/src/framework/results/data/pybind_data.hpp @@ -16,23 +16,23 @@ #define _aer_framework_result_data_pybind_data_hpp_ #include "framework/results/data/data.hpp" -#include "framework/results/data/mixins/pybind_data_creg.hpp" -#include "framework/results/data/mixins/pybind_data_rdict.hpp" -#include "framework/results/data/mixins/pybind_data_rvalue.hpp" -#include "framework/results/data/mixins/pybind_data_rvector.hpp" +#include "framework/results/data/mixins/pybind_data_cdict.hpp" #include "framework/results/data/mixins/pybind_data_cmatrix.hpp" +#include "framework/results/data/mixins/pybind_data_creg.hpp" #include "framework/results/data/mixins/pybind_data_cvector.hpp" -#include "framework/results/data/mixins/pybind_data_cdict.hpp" #include "framework/results/data/mixins/pybind_data_json.hpp" #include "framework/results/data/mixins/pybind_data_mps.hpp" +#include "framework/results/data/mixins/pybind_data_rdict.hpp" +#include "framework/results/data/mixins/pybind_data_rvalue.hpp" +#include "framework/results/data/mixins/pybind_data_rvector.hpp" namespace AerToPy { // Move an ExperimentResult data object to a Python dict -template <> py::object to_python(AER::Data &&data); - -} //end namespace AerToPy +template <> +py::object to_python(AER::Data &&data); +} // end namespace AerToPy //============================================================================ // Implementations @@ -41,15 +41,15 @@ template <> py::object to_python(AER::Data &&data); template <> py::object AerToPy::to_python(AER::Data &&data) { py::dict pydata; - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); - AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); return std::move(pydata); } diff --git a/src/framework/results/data/pybind_metadata.hpp b/src/framework/results/data/pybind_metadata.hpp old mode 100755 new mode 100644 index 244083d931..ca2f8a58bb --- a/src/framework/results/data/pybind_metadata.hpp +++ b/src/framework/results/data/pybind_metadata.hpp @@ -21,10 +21,10 @@ namespace AerToPy { // Move an ExperimentResult metdata object to a Python dict -template <> py::object to_python(AER::Metadata &&metadata); - -} //end namespace AerToPy +template <> +py::object to_python(AER::Metadata &&metadata); +} // end namespace AerToPy //============================================================================ // Implementations @@ -33,9 +33,15 @@ template <> py::object to_python(AER::Metadata &&metadata); template <> py::object AerToPy::to_python(AER::Metadata &&metadata) { py::dict pydata; - add_to_python(pydata, static_cast&&>(metadata)); - add_to_python(pydata, static_cast&&>(metadata)); - add_to_python(pydata, static_cast&&>(metadata)); + add_to_python( + pydata, + static_cast &&>(metadata)); + add_to_python( + pydata, + static_cast &&>(metadata)); + add_to_python( + pydata, + static_cast &&>(metadata)); return std::move(pydata); } diff --git a/src/framework/results/data/subtypes/accum_data.hpp b/src/framework/results/data/subtypes/accum_data.hpp old mode 100755 new mode 100644 index a424f8a107..3e34f24caf --- a/src/framework/results/data/subtypes/accum_data.hpp +++ b/src/framework/results/data/subtypes/accum_data.hpp @@ -22,16 +22,17 @@ namespace AER { template class AccumData : public SingleData { -using Base = SingleData; + using Base = SingleData; + public: // Add data (copy) - void add(const T& data); + void add(const T &data); // Add data (move) - void add(T&& data); + void add(T &&data); // Combine data (move) - void combine(AccumData&& other); + void combine(AccumData &&other); // Clear all stored data void clear(); @@ -45,7 +46,7 @@ using Base = SingleData; //------------------------------------------------------------------------------ template -void AccumData::add(const T& data) { +void AccumData::add(const T &data) { if (empty_) { Base::data_ = data; empty_ = false; @@ -55,7 +56,7 @@ void AccumData::add(const T& data) { } template -void AccumData::add(T&& data) { +void AccumData::add(T &&data) { if (empty_) { Base::data_ = std::move(data); empty_ = false; @@ -65,7 +66,7 @@ void AccumData::add(T&& data) { } template -void AccumData::combine(AccumData&& other) { +void AccumData::combine(AccumData &&other) { add(std::move(other.data_)); } @@ -76,6 +77,6 @@ void AccumData::clear() { } //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif diff --git a/src/framework/results/data/subtypes/average_data.hpp b/src/framework/results/data/subtypes/average_data.hpp old mode 100755 new mode 100644 index fea9a7c858..25db5a8cd3 --- a/src/framework/results/data/subtypes/average_data.hpp +++ b/src/framework/results/data/subtypes/average_data.hpp @@ -21,19 +21,20 @@ namespace AER { template class AverageData : public AccumData { -using Base = AccumData; + using Base = AccumData; + public: // Access data - T& value(); + T &value(); // Add data (copy) - void add(const T& data); + void add(const T &data); // Add data (move) - void add(T&& data); + void add(T &&data); // Add data - void combine(AverageData&& other); + void combine(AverageData &&other); // Clear all stored data void clear(); @@ -58,21 +59,21 @@ using Base = AccumData; //------------------------------------------------------------------------------ template -void AverageData::add(const T& data) { +void AverageData::add(const T &data) { denormalize(); Base::add(data); count_ += 1; } template -void AverageData::add(T&& data) { +void AverageData::add(T &&data) { denormalize(); Base::add(std::move(data)); count_ += 1; } template -void AverageData::combine(AverageData&& other) { +void AverageData::combine(AverageData &&other) { denormalize(); other.denormalize(); Base::combine(std::move(other)); @@ -103,12 +104,12 @@ void AverageData::denormalize() { } template -T& AverageData::value() { +T &AverageData::value() { normalize(); return Base::data_; } //------------------------------------------------------------------------------ -} // end namespace AER +} // end namespace AER //------------------------------------------------------------------------------ #endif diff --git a/src/framework/results/data/subtypes/data_map.hpp b/src/framework/results/data/subtypes/data_map.hpp old mode 100755 new mode 100644 index 7f0d4fb0a6..8c942ae0ac --- a/src/framework/results/data/subtypes/data_map.hpp +++ b/src/framework/results/data/subtypes/data_map.hpp @@ -25,23 +25,23 @@ template