Remove standalone and qobj (#2187)

* Remove standalone and qobj

* format

* remove unused imports

* remove unused imports

* restore neccesary files

* remove AerJobSet

* remove assemble

* format

* resolve conflict

* lint

* lint

* remove dask ref in release note

* remove dask ref in release note
This commit is contained in:
Jun Doi 2024-08-20 23:31:34 +09:00 committed by GitHub
parent 0bcc6e5ee7
commit eaba16ca42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
44 changed files with 36 additions and 2939 deletions

View File

@ -8,117 +8,8 @@ concurrency:
group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
standalone:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["macos-13", "ubuntu-latest", "windows-2019"]
steps:
- uses: actions/checkout@v4
- name: Set up Python '3.10'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install deps
run: pip install "conan<2.0.0"
- name: Install openblas
run: |
set -e
sudo apt-get update
sudo apt-get install -y libopenblas-dev
shell: bash
if: runner.os == 'Linux'
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
if: runner.os == 'Windows'
- name: Compile Standalone Windows
run: |
set -e
mkdir out; cd out; cmake .. -DBUILD_TESTS=1
cmake --build . --config Release
shell: bash
if: runner.os == 'Windows'
- name: Compile Standalone
run: |
set -e
mkdir out; cd out; cmake .. -DBUILD_TESTS=1
make
shell: bash
if: runner.os != 'Windows'
- name: Run Unit Tests
run: |
cd out/bin
for test in test*
do echo $test
if ! ./$test
then
ERR=1
fi
done
if [ ! -z "$ERR" ]
then
exit 1
fi
shell: bash
- name: Run qobj
run: |
pip install -U qiskit
python tools/generate_qobj.py
cd out
Release/qasm_simulator ../qobj.json | python ../tools/verify_standalone_results.py
shell: bash
mpi_standalone:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: ["ubuntu-latest"]
steps:
- uses: actions/checkout@v4
- name: Set up Python '3.10'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install deps
run: pip install "conan<2.0.0"
- name: Install openblas and mpi
run: |
set -e
sudo apt-get update
sudo apt-get install -y libopenblas-dev openmpi-bin libopenmpi-dev
shell: bash
- name: Compile Standalone
run: |
set -e
mkdir out; cd out; cmake .. -DBUILD_TESTS=1 -DAER_MPI=True
make
shell: bash
- name: Run Unit Tests with mpi
run: |
cd out/bin
for test in test*
do echo $test
if ! /usr/bin/mpirun.openmpi -host localhost:2 -np 2 ./$test
then
ERR=1
fi
done
if [ ! -z "$ERR" ]
then
exit 1
fi
shell: bash
- name: Run qobj
run: |
pip install -U qiskit
python tools/generate_qobj.py
cd out
/usr/bin/mpirun.openmpi -host localhost:2 -np 2 Release/qasm_simulator ../qobj.json | python ../tools/verify_standalone_results.py
env:
USE_MPI: 1
shell: bash
wheel:
runs-on: ${{ matrix.os }}
needs: ["standalone"]
strategy:
matrix:
os: ["macos-13", "ubuntu-latest", "windows-2019"]

2
.gitignore vendored
View File

@ -36,5 +36,3 @@ docs/_build/
docs/stubs/
docs/api/
# Ignore DASK temporary files
dask-worker-space/*

View File

@ -95,8 +95,6 @@ include(dependency_utils)
# Get version information
get_version(${VERSION_NUM})
configure_file("${PROJECT_SOURCE_DIR}/contrib/standalone/version.hpp.in"
"${PROJECT_SOURCE_DIR}/contrib/standalone/version.hpp")
set(AER_SIMULATOR_CPP_SRC_DIR "${PROJECT_SOURCE_DIR}/src")
set(AER_SIMULATOR_CPP_EXTERNAL_LIBS
@ -543,153 +541,6 @@ set(AER_LIBRARIES
${CMAKE_DL_LIBS})
set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} ${CONAN_DEFINES})
if(SKBUILD) # Terra Addon build
add_subdirectory(qiskit_aer/backends/wrappers)
else() # Standalone build
set(AER_LIBRARIES
${AER_LIBRARIES}
${THRUST_DEPENDANT_LIBS}
${MPI_DEPENDANT_LIBS})
function(build_cuda target src_file is_exec)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "amd64")
if (NOT CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
# We build SIMD filed separately, because they will be reached only if the
# machine running the code has SIMD support
set(SIMD_SOURCE_FILE "${PROJECT_SOURCE_DIR}/src/simulators/statevector/qv_avx2.cpp")
endif()
endif()
set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES LANGUAGE CUDA)
set_source_files_properties(${src_file} PROPERTIES LANGUAGE CUDA)
set_source_files_properties(${src_file} PROPERTIES COMPILE_FLAGS "${CUDA_NVCC_FLAGS}")
if(DEFINED SIMD_FLAGS_LIST)
nvcc_add_compiler_options_list("${SIMD_FLAGS_LIST}" SIMD_FLAGS)
set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "${CUDA_NVCC_FLAGS} ${SIMD_FLAGS}")
endif()
if(${is_exec})
add_executable(${target} ${src_file} ${SIMD_SOURCE_FILE})
else()
add_library(${target} ${src_file} ${SIMD_SOURCE_FILE})
endif()
target_link_libraries(${target} ${AER_LIBRARIES})
string(STRIP ${AER_COMPILER_FLAGS} AER_COMPILER_FLAGS_STRIPPED)
nvcc_add_compiler_options(${AER_COMPILER_FLAGS_STRIPPED} AER_COMPILER_FLAGS_OUT)
add_subdirectory(qiskit_aer/backends/wrappers)
set_target_properties(${target} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 14
COMPILE_FLAGS ${AER_COMPILER_FLAGS_OUT}
LINK_FLAGS ${AER_LINKER_FLAGS}
RUNTIME_OUTPUT_DIRECTORY_DEBUG Debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE Release)
endfunction()
function(build_rocm target src_file is_exec)
# ROCm is only supported in x86_64 devices so it should be safe to leverage AVX2.
set(SIMD_SOURCE_FILE "${PROJECT_SOURCE_DIR}/src/simulators/statevector/qv_avx2.cpp")
set_source_files_properties(
${SIMD_SOURCE_FILE}
${src_file}
PROPERTIES LANGUAGE CXX)
if(${is_exec})
add_executable(${target} ${src_file} ${SIMD_SOURCE_FILE})
else()
add_library(${target} ${src_file} ${SIMD_SOURCE_FILE})
endif()
target_compile_options(${target} PRIVATE ${ROCM_EXTRA_FLAGS} ${SIMD_FLAGS_LIST})
target_compile_definitions(${target} PRIVATE ${ROCM_EXTRA_DEFS} ${AER_COMPILER_DEFINITIONS})
target_link_libraries(${target} PRIVATE ${AER_LIBRARIES})
set_target_properties(${target} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 14
COMPILE_FLAGS ${AER_COMPILER_FLAGS}
LINK_FLAGS ${AER_LINKER_FLAGS}
RUNTIME_OUTPUT_DIRECTORY_DEBUG Debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE Release)
endfunction()
function(build_cpu target src_file is_exec)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "amd64")
if (NOT CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
# We build SIMD filed separately, because they will be reached only if the
# machine running the code has SIMD support
set(SIMD_SOURCE_FILE "${PROJECT_SOURCE_DIR}/src/simulators/statevector/qv_avx2.cpp")
endif()
endif()
string(REPLACE ";" " " SIMD_FLAGS "${SIMD_FLAGS_LIST}")
set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "${SIMD_FLAGS}")
if(${is_exec})
add_executable(${target} ${src_file} ${SIMD_SOURCE_FILE})
else()
add_library(${target} SHARED ${src_file} ${SIMD_SOURCE_FILE})
endif()
target_link_libraries(${target} PRIVATE ${AER_LIBRARIES})
set_target_properties(${target} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 14
COMPILE_FLAGS ${AER_COMPILER_FLAGS}
LINK_FLAGS ${AER_LINKER_FLAGS}
RUNTIME_OUTPUT_DIRECTORY_DEBUG Debug
RUNTIME_OUTPUT_DIRECTORY_RELEASE Release)
target_include_directories(${target}
PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR}
PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS})
target_compile_definitions(${target}
PRIVATE ${AER_COMPILER_DEFINITIONS})
if(WIN32 AND NOT AER_BLAS_LIB_PATH)
add_custom_command(TARGET ${target} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different
${BACKEND_REDIST_DEPS}
$<TARGET_FILE_DIR:${target}>)
install(FILES ${BACKEND_REDIST_DEPS} DESTINATION bin)
endif()
endfunction()
# build qasm_simulator
set(AER_SIMULATOR_SOURCE "${PROJECT_SOURCE_DIR}/contrib/standalone/qasm_simulator.cpp")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
if(CUDA_FOUND AND AER_THRUST_BACKEND STREQUAL "CUDA")
build_cuda(qasm_simulator ${AER_SIMULATOR_SOURCE} TRUE)
elseif(HIP_FOUND AND AER_THRUST_BACKEND STREQUAL "ROCM")
build_rocm(qasm_simulator ${AER_SIMULATOR_SOURCE} TRUE)
else()
build_cpu(qasm_simulator ${AER_SIMULATOR_SOURCE} TRUE)
endif()
install(TARGETS qasm_simulator DESTINATION bin)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(AER_RUNTIME_SOURCE "${PROJECT_SOURCE_DIR}/contrib/runtime/aer_runtime.cpp")
if(CUDA_FOUND AND AER_THRUST_BACKEND STREQUAL "CUDA")
build_cuda(aer ${AER_RUNTIME_SOURCE} FALSE)
elseif(HIP_FOUND AND AER_THRUST_BACKEND STREQUAL "ROCM")
build_rocm(aer ${AER_RUNTIME_SOURCE} FALSE)
else()
build_cpu(aer ${AER_RUNTIME_SOURCE} FALSE)
endif()
install(TARGETS aer)
# Tests
if(BUILD_TESTS AND NOT AER_MPI)
add_executable(test_libaer "${PROJECT_SOURCE_DIR}/test/runtime/runtime_sample.c")
target_include_directories(test_libaer PUBLIC "${PROJECT_SOURCE_DIR}/contrib/runtime/")
# AER_LINKER_FLAGS carry eventual OpenMP linking flags.
set_target_properties(test_libaer PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE bin
LINK_FLAGS ${AER_LINKER_FLAGS})
target_link_libraries(test_libaer PRIVATE ${AER_LIBRARIES})
target_link_libraries(test_libaer PRIVATE aer)
add_test(NAME aer_runtime_test COMMAND bin/test_libaer)
endif()
endif()
endif()
# Tests
if(BUILD_TESTS)
add_subdirectory(test)
endif()

View File

@ -292,7 +292,6 @@ repository.
This is useful for building from source offline, or to reuse the installed package dependencies.
If we are only building the standalone version and do not want to install all Python requirements you can just install
**Conan**:
$ pip install conan
@ -352,11 +351,6 @@ Ubuntu
#### <a name="linux-build"> Build </a>
There are two ways of building `Aer` simulators, depending on your goal:
1. Build a Python extension that works with Terra.
2. Build a standalone executable.
**Python extension**
As any other Python package, we can install from source code by just running:
@ -404,36 +398,6 @@ we install those dependencies outside the regular setuptools *mechanism*. If you
of these packages set the environment variable DISABLE_DEPENDENCY_INSTALL (ON or 1).
**Standalone Executable**
If you want to build a standalone executable, you have to use *CMake* directly.
The preferred way *CMake* is meant to be used, is by setting up an "out of
source" build. So in order to build your standalone executable, you have to follow
these steps:
qiskit-aer$ mkdir out
qiskit-aer$ cd out
qiskit-aer/out$ cmake ..
qiskit-aer/out$ cmake --build . --config Release -- -j4
Once built, you will have your standalone executable into the `Release/` or
`Debug/` directory (depending on the type of building chosen with the `--config`
option):
qiskit-aer/out$ cd Release
qiskit-aer/out/Release/$ ls
qasm_simulator
**Advanced options**
Because the standalone version of `Aer` doesn't need Python at all, the build system is
based on CMake, just like most of other C++ projects. So to pass all the different
options we have on `Aer` to CMake, we use its native mechanism:
qiskit-aer/out$ cmake -DCMAKE_CXX_COMPILER=g++-9 -DAER_BLAS_LIB_PATH=/path/to/my/blas ..
### macOS
#### <a name="mac-dependencies"> Dependencies </a>
@ -451,11 +415,6 @@ You further need to have *Xcode Command Line Tools* installed on macOS:
#### <a name="mac-build"> Build </a>
There are two ways of building `Aer` simulators, depending on your goal:
1. Build a Python extension that works with Terra;
2. Build a standalone executable.
**Python extension**
As any other Python package, we can install from source code by just running:
@ -501,35 +460,6 @@ As we are using *scikit-build* and we need some *Python* dependencies to be pres
we install those dependencies outside the regular setuptools *mechanism*. If you want to avoid automatic installation
of these packages set the environment variable DISABLE_DEPENDENCY_INSTALL (ON or 1).
**Standalone Executable**
If you want to build a standalone executable, you have to use **CMake** directly.
The preferred way **CMake** is meant to be used, is by setting up an "out of
source" build. So in order to build your standalone executable, you have to follow
these steps:
qiskit-aer$ mkdir out
qiskit-aer$ cd out
qiskit-aer/out$ cmake ..
qiskit-aer/out$ cmake --build . --config Release -- -j4
Once built, you will have your standalone executable into the `Release/` or
`Debug/` directory (depending on the type of building chosen with the `--config`
option):
qiskit-aer/out$ cd Release
qiskit-aer/out/Release/$ ls
qasm_simulator
***Advanced options***
Because the standalone version of `Aer` doesn't need Python at all, the build system is
based on CMake, just like most of other C++ projects. So to pass all the different
options we have on `Aer` to CMake, we use its native mechanism:
qiskit-aer/out$ cmake -DCMAKE_CXX_COMPILER=g++-9 -DAER_BLAS_LIB_PATH=/path/to/my/blas ..
### Windows
@ -604,34 +534,6 @@ As we are using *scikit-build* and we need some *Python* dependencies to be pres
we install those dependencies outside the regular setuptools *mechanism*. If you want to avoid automatic installation
of these packages set the environment variable DISABLE_DEPENDENCY_INSTALL (ON or 1).
**Standalone Executable**
If you want to build a standalone executable, you have to use **CMake** directly.
The preferred way **CMake** is meant to be used, is by setting up an "out of
source" build. So in order to build our standalone executable, you have to follow
these steps:
(QiskitDevEnv) qiskit-aer> mkdir out
(QiskitDevEnv) qiskit-aer> cd out
(QiskitDevEnv) qiskit-aer\out> cmake ..
(QiskitDevEnv) qiskit-aer\out> cmake --build . --config Release -- -j4
Once built, you will have your standalone executable into the `Release/` or
`Debug/` directory (depending on the type of building chosen with the `--config`
option):
(QiskitDevEnv) qiskit-aer\out> cd Release
(QiskitDevEnv) qiskit-aer\out\Release> dir
qasm_simulator
***Advanced options***
Because the standalone version of `Aer` doesn't need Python at all, the build system is
based on CMake, just like most of other C++ projects. So to pass all the different
options we have on `Aer` to CMake, we use its native mechanism:
(QiskitDevEnv) qiskit-aer\out> cmake -G "Visual Studio 15 2017" -DAER_BLAS_LIB_PATH=c:\path\to\my\blas ..
### Building with GPU support
@ -714,21 +616,7 @@ on the `thrust` library. This enables Aer to run on AMD® GPUs,
including the AMD® Instinct GPU line based on the CDNA architecture.
ROCm® only support linux platforms.
To build the standalone version, the following should be sufficient:
```
cmake <qiskit-aer source folder> -G Ninja \
-DCMAKE_INSTALL_PREFIX=<qiskit-aer target instalation folder> \
-DSKBUILD=FALSE \
-DAER_THRUST_BACKEND=ROCM \
-DAER_MPI=<set to ON or OFF depending on whether to activate MPI support> \
-DAER_ROCM_ARCH=<target AMD GPU list, white-space separated, e.g. 'gfx90a gfx908'> \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTS=True
ninja install
```
Alternatively, and possibly preferred for most use cases, you can create a Python
wheel file that you can install as part of your Python environemnt:
You can create a Python wheel file that you can install as part of your Python environemnt:
```
cd <qiskit-aer source folder>
@ -1106,10 +994,6 @@ create the wheel file:
qiskit-aer$> python ./setup.py bdist_wheel --build-type=Debug
If you want to debug the standalone executable, the parameter changes to:
qiskit-aer/out$> cmake -DCMAKE_BUILD_TYPE=Debug
There are three different build configurations: `Release`, `Debug`, and `Release with Debug Symbols`, whose parameters are:
`Release`, `Debug`, `RelWithDebInfo` respectively.

View File

@ -1,203 +0,0 @@
/**
* This code is part of Qiskit.
*
* (C) Copyright IBM 2018, 2019.
*
* This code is licensed under the Apache License, Version 2.0. You may
* obtain a copy of this license in the LICENSE.txt file in the root directory
* of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
*
* Any modifications or derivative works of this code must retain this
* copyright notice, and modified files need to carry a notice indicating
* that they have been altered from the originals.
*/
// #define DEBUG // Uncomment for verbose debugging output
#include <cstdio>
#include <iostream>
#include <string>
#ifdef AER_MPI
#include <mpi.h>
#endif
#include "version.hpp"
// Simulator
#include "controllers/aer_controller.hpp"
/*******************************************************************************
*
* 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 };
inline CmdArguments parse_cmd_options(const std::string &argv) {
if (argv == "-v" || argv == "--version")
return CmdArguments::SHOW_VERSION;
if (argv == "-c" || argv == "--config")
return CmdArguments::INPUT_CONFIG;
return CmdArguments::INPUT_DATA;
}
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) {
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) {
failed("Invalid command line", out);
// Print usage message
std::cerr << "\n\n";
show_version();
std::cerr << "\n";
std::cerr << "Usage: \n";
std::cerr << command << " [-v] [-c <config>] <file>\n";
std::cerr << " -v : Show version\n";
std::cerr << " -c <config> : Configuration file\n";
;
std::cerr << " file : qobj file\n";
}
int main(int argc, char **argv) {
std::ostream &out = std::cout; // output stream
int indent = 4;
json_t qobj;
json_t config;
int myrank = 0;
std::cerr << "The standalone simulator is deprecated as of Qiskit 0.14"
<< std::endl;
std::cerr << "and will be removed no sooner than 3 months from that release."
<< std::endl;
std::cerr << "Please run the simulator from Python using AerSimulator.run()"
<< std::endl;
std::cerr << "with qiskit.circuit" << std::endl;
#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);
#endif
if (argc == 1) { // NOLINT
usage(std::string(argv[0]), out); // NOLINT
return 1;
}
// Parse command line options
for (auto pos = 1UL; pos < static_cast<unsigned int>(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<unsigned int>(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;
}
}
// Execute simulation
try {
// Check for command line config
// and if present add to qobj config
json_t &config_all = qobj["config"];
if (!config.empty()) // NOLINT
config_all.update(config.begin(), config.end());
// Remap legacy method names
std::string method;
JSON::get_value(method, "method", config_all);
if (method == "statevector_gpu") {
config_all["method"] = "statevector";
config_all["device"] = "GPU";
} else if (method == "density_matrix_gpu") {
config_all["method"] = "density_matrix";
config_all["device"] = "GPU";
}
// Initialize simulator
AER::Controller sim;
auto result = sim.execute(qobj).to_json();
if (myrank == 0) {
out << result.dump(4) << std::endl;
}
// Check if execution was successful.
bool success = false;
std::string status;
JSON::get_value(success, "success", result);
JSON::get_value(status, "status", result);
if (!success) {
#ifdef AER_MPI
MPI_Finalize();
#endif
if (status == "COMPLETED")
return 3; // The simulation was was completed unsuccesfully.
return 2; // Failed to execute the Qobj
}
} catch (std::exception &e) {
std::stringstream msg;
msg << "Failed to execute qobj (" << e.what() << ")";
failed(msg.str(), out, indent);
#ifdef AER_MPI
MPI_Finalize();
#endif
return 2;
}
#ifdef AER_MPI
MPI_Finalize();
#endif
return 0;
} // end main

View File

@ -1,17 +0,0 @@
/**
* This code is part of Qiskit.
*
* (C) Copyright IBM 2018, 2019.
*
* This code is licensed under the Apache License, Version 2.0. You may
* obtain a copy of this license in the LICENSE.txt file in the root directory
* of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
*
* Any modifications or derivative works of this code must retain this
* copyright notice, and modified files need to carry a notice indicating
* that they have been altered from the originals.
*/
#define AER_MAJOR_VERSION ${MAJOR_VERSION}
#define AER_MINOR_VERSION ${MINOR_VERSION}
#define AER_PATCH_VERSION ${PATCH_VERSION}

View File

@ -1,6 +1,6 @@
.. _dask:
.. _threadpool:
Running with Threadpool and DASK
Running with Threadpool
================================
Qiskit Aer runs simulation jobs on a single-worker Python multiprocessing ThreadPool executor
@ -8,24 +8,11 @@ so that all parallelization is handled by low-level OpenMP and CUDA code.
However to customize job-level parallel execution of multiple circuits a user can specify
a custom multiprocessing executor and control the splitting of circuits using
the ``executor`` and ``max_job_size`` backend options.
For large scale job parallelization on HPC clusters Qiskit Aer executors support
the distributed Clients from the `DASK <http://dask.org>`__.
Installation of DASK packages with Aer
---------------------------------------
If you want to install dask client at the same time as Qiskit Aer,
please add the ``dask`` extra as follows.
This option installs Aer, dask, and distributed packages.
.. code-block:: sh
pip install .[dask]
Usage of executor
-----------------
To use Threadpool or DASK as an executor, you need to set
To use Threadpool as an executor, you need to set
``executor`` and ``max_job_size`` by ``set_options`` function.
If both ``executor`` (default None) and ``max_job_size`` (default None) are set,
Aer splits the multiple circuits to some chunk of circuits and submits them to the executor.
@ -69,44 +56,3 @@ Example: Threadpool execution
qbackend.set_options(max_job_size=1)
result = qbackend.run(circ_list).result()
Example: Dask execution
'''''''''''''''''''''''
The Dask client uses ``multiprocessing`` so you need to
guard it by an ``if __name__ == "__main__":`` block.
.. code-block:: python
import qiskit
from qiskit_aer import AerSimulator
from dask.distributed import LocalCluster, Client
from math import pi
def q_exec():
# Generate circuits
circ = qiskit.QuantumCircuit(15, 15)
circ.h(0)
circ.cx(0, 1)
circ.cx(1, 2)
circ.p(pi/2, 2)
circ.measure([0, 1, 2], [0, 1 ,2])
circ2 = qiskit.QuantumCircuit(15, 15)
circ2.h(0)
circ2.cx(0, 1)
circ2.cx(1, 2)
circ2.p(pi/2, 2)
circ2.measure([0, 1, 2], [0, 1 ,2])
circ_list = [circ, circ2]
exc = Client(address=LocalCluster(n_workers=2, processes=True))
# Set executor and max_job_size
qbackend = AerSimulator()
qbackend.set_options(executor=exc)
qbackend.set_options(max_job_size=1)
result = qbackend.run(circ_list).result()
if __name__ == '__main__':
q_exec()

View File

@ -67,7 +67,7 @@ if platform.system() == "Darwin":
# pylint: disable=wrong-import-position
from qiskit_aer.aerprovider import AerProvider
from qiskit_aer.jobs import AerJob, AerJobSet
from qiskit_aer.jobs import AerJob
from qiskit_aer.aererror import AerError
from qiskit_aer.backends import *
from qiskit_aer import library

View File

@ -41,7 +41,6 @@ from qiskit.circuit.controlflow import (
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Decompose
from qiskit_aer.aererror import AerError
from qiskit_aer.noise import NoiseModel

View File

@ -26,7 +26,6 @@ from .backendconfiguration import AerBackendConfiguration
from .backendproperties import target_to_backend_properties
from .backend_utils import (
cpp_execute_circuits,
cpp_execute_qobj,
available_methods,
available_devices,
MAX_QUBITS_STATEVECTOR,
@ -226,7 +225,7 @@ class AerSimulator(AerBackend):
maximum will be set to the number of CPU cores (Default: 0).
* ``max_parallel_experiments`` (int): Sets the maximum number of
qobj experiments that may be executed in parallel up to the
experiments that may be executed in parallel up to the
max_parallel_threads value. If set to 1 parallel circuit
execution will be disabled. If set to 0 the maximum will be
automatically set to max_parallel_threads (Default: 1).
@ -688,7 +687,7 @@ class AerSimulator(AerBackend):
"conditional": True,
"memory": True,
"max_shots": int(1e6),
"description": "A C++ QasmQobj simulator with noise",
"description": "A C++ Qasm simulator with noise",
"coupling_map": None,
"basis_gates": BASIS_GATES["automatic"],
"custom_instructions": _CUSTOM_INSTR["automatic"],
@ -929,17 +928,6 @@ class AerSimulator(AerBackend):
ret = cpp_execute_circuits(self._controller, aer_circuits, noise_model, config)
return ret
def _execute_qobj(self, qobj):
"""Execute a qobj on the backend.
Args:
qobj (QasmQobj): simulator input.
Returns:
dict: return a dictionary of results.
"""
return cpp_execute_qobj(self._controller, qobj)
def set_option(self, key, value):
if key == "custom_instructions":
self._set_configuration_option(key, value)
@ -976,25 +964,6 @@ class AerSimulator(AerBackend):
if value != "CPU":
self.name += f"_{value}".lower()
def _validate(self, qobj):
"""Semantic validations of the qobj which cannot be done via schemas.
Warn if no measure or save instructions in run circuits.
"""
for experiment in qobj.experiments:
# If circuit does not contain measurement or save
# instructions raise a warning
no_data = True
for op in experiment.instructions:
if op.name == "measure" or op.name[:5] == "save_":
no_data = False
break
if no_data:
logger.warning(
'No measure or save instruction in circuit "%s": results will be empty.',
experiment.header.name,
)
def _basis_gates(self):
"""Return simualtor basis gates.

View File

@ -22,16 +22,14 @@ import warnings
from abc import ABC, abstractmethod
from qiskit.circuit import QuantumCircuit, ParameterExpression, Delay
from qiskit.compiler import assemble
from qiskit.providers import BackendV2 as Backend
from qiskit.providers.models.backendstatus import BackendStatus
from qiskit.pulse import Schedule, ScheduleBlock
from qiskit.qobj import QasmQobj, PulseQobj
from qiskit.result import Result
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.target import Target
from ..aererror import AerError
from ..jobs import AerJob, AerJobSet, split_qobj
from ..jobs import AerJob
from ..noise.noise_model import NoiseModel, QuantumErrorLocation
from ..noise.errors.base_quantum_error import QuantumChannelInstruction
from .aer_compiler import compile_circuit, assemble_circuits, generate_aer_config
@ -204,49 +202,6 @@ class AerBackend(Backend, ABC):
return aer_job
# pylint: disable=arguments-differ
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
if validate:
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")
# 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),
)
# 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"):
self._options.executor = None
# Submit job
job_id = str(uuid.uuid4())
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.submit()
# Restore removed executor after submission
if hasattr(self._options, "executor"):
self._options.executor = opts_executor
return aer_job
def configuration(self):
"""Return the simulator backend configuration.
@ -501,71 +456,6 @@ class AerBackend(Backend, ABC):
status_msg="",
)
def _execute_qobj_job(self, qobj, job_id="", format_result=True):
"""Run a qobj job"""
# Start timer
start = time.time()
# Take metadata from headers of experiments to work around JSON serialization error
metadata_list = []
metadata_index = 0
for expr in qobj.experiments:
if hasattr(expr.header, "metadata"):
metadata_copy = expr.header.metadata.copy()
metadata_list.append(metadata_copy)
expr.header.metadata.clear()
if "id" in metadata_copy:
expr.header.metadata["id"] = metadata_copy["id"]
expr.header.metadata["metadata_index"] = metadata_index
metadata_index += 1
# Run simulation
output = self._execute_qobj(qobj)
# Recover metadata
metadata_index = 0
for expr in qobj.experiments:
if hasattr(expr.header, "metadata"):
expr.header.metadata.clear()
expr.header.metadata.update(metadata_list[metadata_index])
metadata_index += 1
# 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.")
# Format results
output["job_id"] = job_id
output["date"] = datetime.datetime.now().isoformat()
output["backend_name"] = self.name
output["backend_version"] = self.configuration().backend_version
# 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"]
):
metadata_index = result["header"]["metadata"]["metadata_index"]
result["header"]["metadata"] = metadata_list[metadata_index]
# Add execution time
output["time_taken"] = time.time() - start
# Display warning if simulation failed
if not output.get("success", False):
msg = "Simulation failed"
if "status" in output:
msg += f" and returned the following error message:\n{output['status']}"
logger.warning(msg)
if format_result:
return self._format_results(output)
return output
def _execute_circuits_job(
self, circuits, parameter_binds, run_options, job_id="", format_result=True
):
@ -660,49 +550,6 @@ class AerBackend(Backend, ABC):
return circuits, noise_model
def _assemble(self, circuits, parameter_binds=None, **run_options):
"""Assemble one or more Qobj for running on the simulator"""
if isinstance(circuits, (QasmQobj, PulseQobj)):
qobj = circuits
else:
# compile and insert noise injection points
circuits, noise_model = self._compile(circuits, **run_options)
# If noise model exists, add it to the run options
if noise_model:
run_options["noise_model"] = noise_model
if parameter_binds:
# Handle parameter binding
parameterizations = self._convert_binds(circuits, parameter_binds)
qobj = None
for circuit in circuits:
assemble_bind = {param: 1 for param in circuit.parameters}
qobj_tmp = assemble(
[circuit],
backend=self,
parameter_binds=[assemble_bind],
parameterizations=parameterizations,
)
if qobj:
qobj.experiments.append(qobj_tmp.experiments[0])
else:
qobj = qobj_tmp
else:
qobj = assemble(circuits, backend=self)
# Add options
for key, val in self.options.__dict__.items():
if val is not None:
setattr(qobj.config, key, val)
# Override with run-time options
for key, val in run_options.items():
setattr(qobj.config, key, val)
return qobj
def _assemble_noise_model(self, circuits, optypes, **run_options):
"""Move quantum error instructions from circuits to noise model"""
# Make a shallow copy so we can modify list elements if required
@ -777,18 +624,6 @@ class AerBackend(Backend, ABC):
else:
return getattr(self._options, "executor", None)
@abstractmethod
def _execute_qobj(self, qobj):
"""Execute a qobj on the backend.
Args:
qobj (QasmQobj or PulseQobj): simulator input.
Returns:
dict: return a dictionary of results.
"""
pass
@abstractmethod
def _execute_circuits(self, aer_circuits, noise_model, config):
"""Execute aer circuits on the backend.
@ -803,10 +638,6 @@ class AerBackend(Backend, ABC):
"""
pass
def _validate(self, qobj):
"""Validate the qobj for the backend"""
pass
def set_option(self, key, value):
"""Special handling for setting backend options.

View File

@ -21,7 +21,6 @@ from types import SimpleNamespace
import psutil
from qiskit.circuit import QuantumCircuit
from qiskit.qobj import QasmQobjInstruction
from qiskit.result import ProbDistribution
from qiskit.quantum_info import Clifford
@ -435,15 +434,6 @@ BASIS_GATES[None] = BASIS_GATES["automatic"] = sorted(
)
def cpp_execute_qobj(controller, qobj):
"""Execute qobj on C++ controller wrapper"""
# Location where we put external libraries that will be
# loaded at runtime by the simulator extension
qobj.config.library_dir = LIBRARY_DIR
return controller(qobj)
def cpp_execute_circuits(controller, aer_circuits, noise_model, config):
"""Execute aer circuits on C++ controller wrapper"""
@ -475,25 +465,6 @@ def available_devices(controller):
return tuple(dev)
def add_final_save_instruction(qobj, state):
"""Add final save state instruction to all experiments in a qobj."""
def save_inst(num_qubits):
"""Return n-qubit save statevector inst"""
return QasmQobjInstruction(
name=f"save_{state}",
qubits=list(range(num_qubits)),
label=f"{state}",
snapshot_type="single",
)
for exp in qobj.experiments:
num_qubits = exp.config.n_qubits
exp.instructions.append(save_inst(num_qubits))
return qobj
def add_final_save_op(aer_circuits, state):
"""Add final save state op to all experiments in a qobj."""
@ -504,14 +475,6 @@ def add_final_save_op(aer_circuits, state):
return aer_circuits
def map_legacy_method_options(qobj):
"""Map legacy method names of qasm simulator to aer simulator options"""
method = getattr(qobj.config, "method", None)
if method in LEGACY_METHOD_MAP:
qobj.config.method, qobj.config.device = LEGACY_METHOD_MAP[method]
return qobj
def map_legacy_method_config(config):
"""Map legacy method names of qasm simulator to aer simulator options"""
method = config.method

View File

@ -26,12 +26,10 @@ from .aerbackend import AerBackend
from .backendconfiguration import AerBackendConfiguration
from .backendproperties import target_to_backend_properties
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,
)
@ -164,7 +162,7 @@ class QasmSimulator(AerBackend):
maximum will be set to the number of CPU cores (Default: 0).
* ``max_parallel_experiments`` (int): Sets the maximum number of
qobj experiments that may be executed in parallel up to the
experiments that may be executed in parallel up to the
max_parallel_threads value. If set to 1 parallel circuit
execution will be disabled. If set to 0 the maximum will be
automatically set to max_parallel_threads (Default: 1).
@ -405,7 +403,7 @@ class QasmSimulator(AerBackend):
"conditional": True,
"memory": True,
"max_shots": int(1e6),
"description": "A C++ QasmQobj simulator with noise",
"description": "A C++ Qasm simulator with noise",
"coupling_map": None,
"basis_gates": _DEFAULT_BASIS_GATES,
"custom_instructions": _DEFAULT_CUSTOM_INSTR,
@ -614,18 +612,6 @@ class QasmSimulator(AerBackend):
"""Return the available simulation methods."""
return copy.copy(self._AVAILABLE_DEVICES)
def _execute_qobj(self, qobj):
"""Execute a qobj on the backend.
Args:
qobj (QasmQobj): simulator input.
Returns:
dict: return a dictionary of results.
"""
qobj = map_legacy_method_options(qobj)
return cpp_execute_qobj(self._controller, qobj)
def _execute_circuits(self, aer_circuits, noise_model, config):
"""Execute circuits on the backend."""
config = map_legacy_method_config(config)
@ -649,29 +635,6 @@ class QasmSimulator(AerBackend):
if key in ["method", "noise_model", "basis_gates"]:
self._cached_basis_gates = self._basis_gates()
def _validate(self, qobj):
"""Semantic validations of the qobj which cannot be done via schemas.
Warn if no measurements in circuit with classical registers.
"""
for experiment in qobj.experiments:
# If circuit contains classical registers but not
# measurements raise a warning
if experiment.config.memory_slots > 0:
# Check if measure opts missing
no_measure = True
for op in experiment.instructions:
if not no_measure:
break # we don't need to check any more ops
if no_measure and op.name == "measure":
no_measure = False
# 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,
)
def _basis_gates(self):
"""Return simualtor basis gates.

View File

@ -17,7 +17,6 @@ import copy
import logging
from warnings import warn
import psutil
from qiskit.providers.options import Options
from ..aererror import AerError
@ -25,13 +24,10 @@ from ..version import __version__
from .aerbackend import AerBackend
from .backendconfiguration import AerBackendConfiguration
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,
)
@ -107,7 +103,7 @@ class StatevectorSimulator(AerBackend):
maximum will be set to the number of CPU cores (Default: 0).
* ``max_parallel_experiments`` (int): Sets the maximum number of
qobj experiments that may be executed in parallel up to the
experiments that may be executed in parallel up to the
max_parallel_threads value. If set to 1 parallel circuit
execution will be disabled. If set to 0 the maximum will be
automatically set to max_parallel_threads (Default: 1).
@ -327,56 +323,8 @@ class StatevectorSimulator(AerBackend):
"""Return the available simulation methods."""
return copy.copy(self._AVAILABLE_DEVICES)
def _execute_qobj(self, qobj):
"""Execute a qobj on the backend.
Args:
qobj (QasmQobj): simulator input.
Returns:
dict: return a dictionary of results.
"""
# Make deepcopy so we don't modify the original qobj
qobj = copy.deepcopy(qobj)
qobj = add_final_save_instruction(qobj, "statevector")
qobj = map_legacy_method_options(qobj)
return cpp_execute_qobj(self._controller, qobj)
def _execute_circuits(self, aer_circuits, noise_model, config):
"""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)
def _validate(self, qobj):
"""Semantic validations of the qobj which cannot be done via schemas.
Some of these may later move to backend schemas.
1. Set shots=1.
2. Check number of qubits will fit in local memory.
"""
name = self.name
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(psutil.virtual_memory().total / (1024**3))} 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:
logger.info(
'"%s" only supports 1 shot. Setting shots=1 for circuit "%s".',
name,
exp_name,
)
experiment.config.shots = 1

View File

@ -18,20 +18,16 @@ import copy
import logging
from warnings import warn
import psutil
from qiskit.providers.options import Options
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,
)
@ -94,7 +90,7 @@ class UnitarySimulator(AerBackend):
* ``max_shot_size`` (int or None): If the number of shots with
a noise model exceeds this value, simulation will split the experiments into
sub experiments in the qobj. If ``None`` simulator does nothing (Default: None).
sub experiments. If ``None`` simulator does nothing (Default: None).
* ``"initial_unitary"`` (matrix_like): Sets a custom initial unitary
matrix for the simulation instead of identity (Default: None).
@ -111,7 +107,7 @@ class UnitarySimulator(AerBackend):
maximum will be set to the number of CPU cores (Default: 0).
* ``"max_parallel_experiments"`` (int): Sets the maximum number of
qobj experiments that may be executed in parallel up to the
experiments that may be executed in parallel up to the
max_parallel_threads value. If set to 1 parallel circuit
execution will be disabled. If set to 0 the maximum will be
automatically set to max_parallel_threads (Default: 1).
@ -313,60 +309,8 @@ class UnitarySimulator(AerBackend):
"""Return the available simulation methods."""
return copy.copy(self._AVAILABLE_DEVICES)
def _execute_qobj(self, qobj):
"""Execute a qobj on the backend.
Args:
qobj (QasmQobj): simulator input.
Returns:
dict: return a dictionary of results.
"""
# Make deepcopy so we don't modify the original qobj
qobj = copy.deepcopy(qobj)
qobj = add_final_save_instruction(qobj, "unitary")
qobj = map_legacy_method_options(qobj)
return cpp_execute_qobj(self._controller, qobj)
def _execute_circuits(self, aer_circuits, noise_model, config):
"""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)
def _validate(self, qobj):
"""Semantic validations of the qobj which cannot be done via schemas.
Some of these may later move to backend schemas.
1. Set shots=1
2. No measurements or reset
3. Check number of qubits will fit in local memory.
"""
name = self.name
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'max ({max_qubits}) for "{name}" with '
f"{int(psutil.virtual_memory().total / (1024**3))} 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:
logger.info(
'"%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"]:
raise AerError(
f"Unsupported {name} instruction {operation.name} in circuit {exp_name}"
)

View File

@ -42,13 +42,6 @@ template <typename T>
class ControllerExecutor {
public:
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<T>(json_t(qobj)));
#else
return AerToPy::to_python(controller_execute<T>(qobj));
#endif
}
py::object execute(std::vector<std::shared_ptr<Circuit>> &circuits,
Noise::NoiseModel &noise_model,
@ -91,7 +84,6 @@ void bind_aer_controller(MODULE m) {
py::class_<ControllerExecutor<Controller>> aer_ctrl(m,
"aer_controller_execute");
aer_ctrl.def(py::init<>());
aer_ctrl.def("__call__", &ControllerExecutor<Controller>::operator());
aer_ctrl.def("__reduce__",
[aer_ctrl](const ControllerExecutor<Controller> &self) {
return py::make_tuple(aer_ctrl, py::tuple());

View File

@ -29,10 +29,7 @@ The following are the classes used to manage job submissions.
:toctree: ../stubs/
AerJob
AerJobSet
"""
from .aerjob import AerJob
from .aerjobset import AerJobSet
from .utils import split_qobj

View File

@ -15,7 +15,6 @@
"""This module implements the job class used for AerBackend objects."""
import logging
import warnings
from qiskit.providers import JobV1 as Job
from qiskit.providers import JobStatus, JobError
@ -32,7 +31,6 @@ class AerJob(Job):
backend,
job_id,
fn,
qobj=None,
circuits=None,
parameter_binds=None,
run_options=None,
@ -46,14 +44,10 @@ class AerJob(Job):
fn(function): a callable function to execute qobj on backend.
This should usually be a bound :meth:`AerBackend._run()` method,
with the signature `(qobj: QasmQobj, job_id: str) -> Result`.
qobj(QasmQobj): qobj to execute
circuits(list of QuantumCircuit): circuits to execute.
If `qobj` is set, this argument is ignored.
parameter_binds(list): parameters for circuits.
If `qobj` is set, this argument is ignored.
run_options(dict): run_options to execute.
If `qobj` is set, this argument is ignored.
executor(ThreadPoolExecutor or dask.distributed.client):
executor(ThreadPoolExecutor):
The executor to be used to submit the job.
Raises:
@ -61,18 +55,9 @@ class AerJob(Job):
"""
super().__init__(backend, job_id)
self._fn = fn
if qobj:
self._qobj = qobj
self._circuits = None
self._parameter_binds = None
self._run_options = None
elif circuits:
self._qobj = None
self._circuits = circuits
self._parameter_binds = parameter_binds
self._run_options = run_options
else:
raise JobError("AerJob needs a qobj or circuits")
self._circuits = circuits
self._parameter_binds = parameter_binds
self._run_options = run_options
self._executor = executor or DEFAULT_EXECUTOR
self._future = None
@ -86,12 +71,9 @@ class AerJob(Job):
"""
if self._future is not None:
raise JobError("Aer job has already been submitted.")
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._parameter_binds, self._run_options, self._job_id
)
self._future = self._executor.submit(
self._fn, self._circuits, self._parameter_binds, self._run_options, self._job_id
)
@requires_submit
def result(self, timeout=None):
@ -148,22 +130,6 @@ class AerJob(Job):
"""Return the instance of the backend used for this job."""
return self._backend
def qobj(self):
"""Return the Qobj submitted for this job.
Returns:
Qobj: the Qobj submitted for this job.
"""
warnings.warn(
"`AerJob.qobj() is deprecated as of qiskit-aer 0.14`. "
"Using a qobj for `backend.run()` is deprecated as of qiskit-aer 0.14"
" 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):
"""Return the list of QuantumCircuit submitted for this job.

View File

@ -1,446 +0,0 @@
# -*- coding: utf-8 -*-
# This code is part of Qiskit.
#
# (C) Copyright IBM 2019, 2020.
#
# 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.
# pylint: disable=arguments-differ
"""A set of cluster jobs for Aer."""
from typing import List, Optional, Union, Tuple, Iterable
import time
import logging
import datetime
import uuid
from collections import Counter
from qiskit.circuit import QuantumCircuit
from qiskit.pulse import Schedule
from qiskit.qobj import QasmQobj
from qiskit.providers import JobV1 as Job
from qiskit.providers import JobStatus, JobError
from qiskit.result import Result
from .utils import DEFAULT_EXECUTOR, requires_submit
from .aerjob import AerJob
logger = logging.getLogger(__name__)
class AerJobSet(Job):
"""A set of :class:`~AerJob` classes for Aer simulators.
An instance of this class is returned when you submit experiments with
executor option. It provides methods that allow you to interact
with the jobs as a single entity. For example, you can retrieve the results
for all of the jobs using :meth:`result()` and cancel all jobs using
:meth:`cancel()`.
"""
def __init__(self, backend, job_id, fn, experiments: List[QasmQobj], executor=None):
"""AerJobSet constructor.
Args:
backend(Aerbackend): Aerbackend.
job_id(int): Job Id.
fn(function): a callable function to execute qobj on backend.
This should usually be a bound :meth:`AerBackend._run()` method,
with the signature `(qobj: QasmQobj, job_id: str) -> Result`.
experiments(List[QasmQobj]): List[QasmQobjs] to execute.
executor(ThreadPoolExecutor or dask.distributed.client):
The executor to be used to submit the job.
"""
super().__init__(backend, job_id)
self._experiments = experiments
# Used for caching
self._future = None
self._futures = []
self._results = None
self._fn = fn
self._executor = executor or DEFAULT_EXECUTOR
self._start_time = None
self._end_time = None
self._combined_result = []
def submit(self):
"""Execute this set of jobs on an executor.
Raises:
RuntimeError: If the jobs were already submitted.
"""
if self._futures:
raise RuntimeError("The jobs for this managed job set have already been submitted.")
self._future = True
worker_id = 0
self._start_time = datetime.datetime.now()
for experiments in self._experiments:
_worker_id_list = []
for exp in experiments:
job_id = str(uuid.uuid4())
logger.debug("Job %s submitted", worker_id)
aer_job = AerJob(self._backend, job_id, self._fn, exp, self._executor)
aer_job.submit()
aer_job._future.add_done_callback(self._set_end_time)
self._futures.append(aer_job)
_worker_id_list.append(worker_id)
worker_id = worker_id + 1
self._combined_result.append(_worker_id_list)
@requires_submit
def status(self, worker: Union[None, int, Iterable[int]]) -> Union[JobStatus, List[JobStatus]]:
"""Return the status of each job in this set.
Args
worker: Worker id. When None, all workers' statuses are returned.
Returns:
A list of job statuses.
"""
if isinstance(worker, int):
aer_job = self._futures[worker]
return aer_job.status()
elif isinstance(worker, Iterable):
job_list = []
for worker_id in worker:
aer_job = self._futures[worker_id]
job_list.append(aer_job.status())
return job_list
else:
return [aer.status() for aer in self._futures]
@requires_submit
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
the timeout is reached.
Args:
timeout: Number of seconds to wait for job results.
Returns:
qiskit.Result: Result object
Raises:
JobError: if unable to retrieve all job results before the
specified timeout.
"""
res = self.worker_results(worker=None, timeout=timeout)
return res
@requires_submit
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.
Args:
worker: Worker id to wait for job result.
timeout: Number of seconds to wait for job results.
Returns:
qiskit.Result: Result object
instance that can be used to retrieve results
for individual experiments.
Raises:
JobError: if unable to retrieve all job results before the
specified timeout.
"""
# We'd like to use futures.as_completed or futures.wait
# however this excludes the use of dask as executor
# because dask's futures are not ~exactly~ the same.
res = []
if isinstance(worker, int):
res = self._get_worker_result(worker, timeout)
elif isinstance(worker, Iterable):
_res = []
for worker_id in worker:
_res.append(self._get_worker_result(worker_id, timeout))
res = self._combine_results(_res)
else:
for _worker_id_list in self._combined_result:
_res = []
for worker_id in _worker_id_list:
_res.append(self._get_worker_result(worker_id, timeout))
res.append(self._combine_results(_res))
res = self._accumulate_experiment_results(res)
return self._combine_job_results(res)
def _get_worker_result(self, worker: int, timeout: Optional[float] = None):
"""Return the result of the jobs specified with worker_id.
this call return all worker's result specified worker and
block until job result become available or the timeout is reached.
Analogous to dask.client.gather()
Args:
worker: Worker id to wait for job result.
timeout: Number of seconds to wait for job results.
Returns:
qiskit.Result: Result object
instance that can be used to retrieve a result.
Raises:
JobError: if unable to retrieve all job results before the
specified timeout.
"""
start_time = time.time()
original_timeout = timeout
aer_job = self._futures[worker]
try:
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)
else:
logger.warning("AerJobSet %s did not return a result", aer_job.name())
except JobError as ex:
raise JobError(
"Timeout while waiting for the results of experiment {}".format(aer_job.name())
) from ex
if timeout:
timeout = original_timeout - (time.time() - start_time)
if timeout <= 0:
raise JobError("Timeout while waiting for JobSet results")
return result
def _combine_job_results(self, result_list: List[Result]):
if len(result_list) == 1:
return result_list[0]
master_result = result_list[0]
_merge_result_list = []
for _result in result_list[1:]:
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
def _accumulate_experiment_results(self, results: List[Result]):
"""Merge all experiments into a single in a`Result`
this function merges the counts and the number of shots
from each experiment in a `Result` for a noise simulation
if `id` in metadata field is the same.
Args:
results: Result list whose experiments will be combined.
Returns:
list: Result list
Raises:
JobError: If results do not have count or memory data
"""
results_list = []
for each_result in results:
_merge_results = []
master_id = None
master_result = None
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")
meta_data = getattr(_result.header, "metadata", None)
if meta_data and "id" in meta_data:
_id = meta_data["id"]
if master_id == _id:
master_result = self._merge_exp(master_result, _result)
else:
master_id = _id
master_result = _result
_merge_results.append(master_result)
else:
_merge_results.append(_result)
each_result.results = _merge_results
results_list.append(each_result)
return results_list
def _merge_exp(self, master: Result, sub: Result):
master.shots = master.shots + sub.shots
if hasattr(master.data, "counts"):
master.data.counts = Counter(master.data.counts) + Counter(sub.data.counts)
if hasattr(master.data, "memory"):
master.data.memory = master.data.memory + sub.data.memory
return master
def _combine_results(self, results: List[Union[Result, None]] = None) -> Result:
"""Combine results from all jobs into a single `Result`.
Note:
Since the order of the results must match the order of the initial
experiments, job results can only be combined if all jobs succeeded.
Args:
results: Result will be combined.
Returns:
A :class:`~qiskit.result.Result` object that contains results from
all jobs.
Raises:
JobError: If results cannot be combined because some jobs failed.
"""
if not 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)
if _result:
combined_result = {
"backend_name": _result.backend_name,
"backend_version": _result.backend_version,
"qobj_id": _result.qobj_id,
"job_id": _result.job_id,
"success": _result.success,
}
combined_result["results"] = []
if hasattr(_result, "status"):
combined_result["status"] = _result.status
if hasattr(_result, "header"):
combined_result["header"] = _result.header.to_dict()
combined_result.update(_result._metadata)
else:
raise JobError("Results cannot be combined - no results.")
for each_result in results:
if each_result is not None:
combined_result["results"].extend(x.to_dict() for x in each_result.results)
if self._end_time is None:
self._end_time = datetime.datetime.now()
if self._start_time:
_time_taken = self._end_time - self._start_time
combined_result["time_taken"] = _time_taken.total_seconds()
else:
combined_result["time_taken"] = 0
combined_result["date"] = datetime.datetime.isoformat(self._end_time)
return Result.from_dict(combined_result)
@requires_submit
def cancel(self) -> None:
"""Cancel all jobs in this job set."""
for aer_job in self._futures:
aer_job.cancel()
@requires_submit
def job(self, experiment: Union[str, QuantumCircuit, Schedule]) -> Tuple[AerJob, int]:
"""Retrieve the job used to submit the specified experiment and its index.
Args:
experiment: Retrieve the job used to submit this experiment. Several
types are accepted for convenience:
* str: The name of the experiment.
* QuantumCircuit: The name of the circuit instance will be used.
* Schedule: The name of the schedule instance will be used.
Returns:
A tuple of the job used to submit the experiment and the experiment index.
Raises:
JobError: If the job for the experiment could not be found.
"""
worker_index = self.worker(experiment)
return self.worker_job(worker_index)
@requires_submit
def worker(self, experiment: Union[str, QuantumCircuit, Schedule]) -> Union[int, List[int]]:
"""Retrieve the index of job.
Args:
experiment: Retrieve the job used to submit this experiment. Several
types are accepted for convenience:
* str: The name of the experiment.
* QuantumCircuit: The name of the circuit instance will be used.
* Schedule: The name of the schedule instance will be used.
Returns:
list or integer value of the job id
Raises:
JobError: If the job for the experiment could not be found.
"""
if isinstance(experiment, (QuantumCircuit, Schedule)):
experiment = experiment.name
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:
job_list.append(i)
if len(job_list) == 1:
return job_list[0]
elif len(job_list) > 1:
return job_list
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]]:
"""Retrieve the job specified with job's id
Args:
worker: retrive job used to submit with this job id.
Returns:
A list of :class:`~qiskit_aer.AerJob`
instances that represents the submitted jobs.
Raises:
JobError: If the job for the experiment could not be found.
"""
aer_jobs = []
if isinstance(worker, int):
return self._futures[worker]
elif isinstance(worker, Iterable):
for worker_id in worker:
aer_jobs.append(self._futures[worker_id])
return aer_jobs
else:
return self._futures
def _set_end_time(self, future):
"""Set job's end time to calculate "time_taken" value
Args:
future(concurrent.futures or dask.distributed.futures): callback future object
"""
# pylint: disable=unused-argument
self._end_time = datetime.datetime.now()
def executor(self):
"""Return the executor for this job"""
return self._executor

View File

@ -11,12 +11,8 @@
# that they have been altered from the originals.
"""Utility functions for Aer job management."""
import uuid
import copy
from math import ceil
from functools import singledispatch, update_wrapper, wraps
from concurrent.futures import ThreadPoolExecutor
from qiskit.qobj import QasmQobj, PulseQobj
from qiskit.providers import JobError
@ -59,69 +55,6 @@ def methdispatch(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 = []
if shot_mod == 0 and num_shot_jobs == 1:
return qobj
if shot_mod > 0:
qobj.config.shots = shot_mod
for experiment in qobj.experiments:
_id = str(uuid.uuid4())
experiment.header.metadata["id"] = _id
qobj_list.append(qobj)
if num_shot_jobs > 1:
_qid = qobj_id or str(uuid.uuid4())
_config = copy.copy(qobj.config)
setattr(_config, "shots", max_shot_size)
experiment_list = []
for experiment in qobj.experiments:
_id = str(uuid.uuid4())
for _ in range(num_shot_jobs):
cpy_exp = copy.copy(experiment)
cpy_exp.header = copy.copy(experiment.header)
cpy_exp.header.metadata["id"] = _id
experiment_list.append(cpy_exp)
qobj_list.append(QasmQobj(_qid, _config, experiment_list, qobj.header))
return qobj_list
def _split_qobj(qobj, max_size, qobj_id, seed):
# Check if we don't need to split
if max_size is None or not max_size > 0:
return qobj
num_jobs = ceil(len(qobj.experiments) / max_size)
if num_jobs == 1:
return qobj
qobjs = []
# Check for parameterizations
params = getattr(qobj.config, "parameterizations", None)
for i in range(num_jobs):
sub_id = qobj_id or str(uuid.uuid4())
indices = slice(i * max_size, (i + 1) * max_size)
sub_exp = qobj.experiments[indices]
sub_config = qobj.config
if params is not None:
sub_config.parameterizations = params[indices]
sub_config = copy.copy(qobj.config)
if seed > 0:
if sub_config is qobj.config:
sub_config = copy.copy(qobj.config)
qobjs.append(type(qobj)(sub_id, sub_config, sub_exp, qobj.header))
return qobjs
def _check_custom_instruction(experiments, optypes=None):
"""Return True if circuits contain instructions that cant be split"""
# Check via optype list if available
@ -131,73 +64,3 @@ def _check_custom_instruction(experiments, optypes=None):
# Otherwise iterate over instruction names
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
if seed == 0:
return
for _each_qobj_list in qobj_list:
for _each_qobj in _each_qobj_list:
_each_qobj.config.seed_simulator = seed
seed = seed + seed_shift
def split_qobj(qobj, max_size=None, max_shot_size=None, qobj_id=None):
"""Split a qobj and return a list of qobjs each with a single experiment.
Args:
qobj (Qobj): The input qobj object to split
max_size (int or None): the maximum number of circuits per job. If
None don't split (Default: None).
max_shot_size (int or None): the maximum number of shots per job. If
None don't split (Default: None).
qobj_id (str): Optional, set a fixed qobj ID for all subjob qobjs.
Raises:
JobError : If max_job_size > 1 and seed is set.
JobError : If custom instructions exist.
Returns:
List: A list of qobjs.
"""
optypes = getattr(qobj.config, "optypes", None)
split_qobj_list = []
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."
)
_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."
)
if max_shot_size is not None and max_shot_size > 0:
_qobj = _copy_qobj_for_noise(qobj, max_shot_size, qobj_id)
if isinstance(_qobj, list):
for each_qobj in _qobj:
_split = _split_qobj(each_qobj, max_size, qobj_id, _seed)
if isinstance(_split, QasmQobj):
split_qobj_list.append([_split])
else:
split_qobj_list.append(_split)
_set_seed(split_qobj_list, _seed)
return split_qobj_list
_qobj = _split_qobj(qobj, max_size, qobj_id, _seed)
if isinstance(_qobj, (PulseQobj, QasmQobj)):
return _qobj
else:
split_qobj_list.append(_qobj)
_set_seed(split_qobj_list, _seed)
return split_qobj_list

View File

@ -58,15 +58,6 @@ class SaveData(Instruction):
self._label = label
self._subtype = subtype
def assemble(self):
"""Return the QasmQobjInstruction for the intructions."""
instr = super().assemble()
# Use same fields as Snapshot instruction
# so we dont need to modify QasmQobjInstruction
instr.snapshot_type = self._subtype
instr.label = self._label
return instr
def inverse(self, annotated=False):
"""Special case. Return self."""
return copy.copy(self)

View File

@ -759,7 +759,7 @@ class NoiseModel:
for name, label in self._instruction_names_labels(instructions):
self._check_number_of_qubits(error, name)
if not isinstance(label, str):
raise NoiseError("Qobj invalid instructions.")
raise NoiseError("QuantumCircuit invalid instructions.")
# Check number of qubits is correct for standard instructions
self._check_number_of_qubits(error, name)
if label in self._local_quantum_errors:

View File

@ -23,8 +23,8 @@ features:
Supported executors include those in the Python ``concurrent.futures``
`module <https://docs.python.org/3/library/concurrent.futures.html>`__
(eg. ``ThreadPoolExecutor``, ``ProcessPoolExecutor``), and
`Dask <http://dask.org>`__ distributed Client executors if the optional
Dask <http://dask.org>__ distributed Client executors if the optional
dask library is installed. Using a Dask executor allows configuring parallel
execution of multiple circuits on HPC clusters. See the
Dask executor :ref:`API Documentation <dask>` for additional details
Dask executor API Documentation dask for additional details
on using Dask executors for HPC simulation.

View File

@ -0,0 +1,5 @@
---
deprecations:
- |
Removed standalone simulators and remove using qobj as input circuits

View File

@ -19,8 +19,6 @@ ADD_CUDA_REQUIREMENTS = (
else True
)
extras_requirements = {"dask": ["dask", "distributed"]}
requirements = [
"qiskit>=1.1.0",
"numpy>=1.16.3",
@ -110,7 +108,6 @@ setup(
install_requires=requirements,
include_package_data=False,
package_data={"qiskit_aer": ["VERSION.txt"], "qiskit_aer.library": ["*.csv"]},
extras_require=extras_requirements,
cmake_args=cmake_args,
keywords="qiskit, simulator, quantum computing, backend",
zip_safe=False,

View File

@ -43,7 +43,6 @@
#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"
@ -77,14 +76,9 @@ public:
Controller() {}
//-----------------------------------------------------------------------
// Execute qobj
// Execute circuits
//-----------------------------------------------------------------------
// Load a QOBJ from a JSON file and execute on the State type
// class.
template <typename inputdata_t>
Result execute(const inputdata_t &qobj);
Result execute(std::vector<std::shared_ptr<Circuit>> &circuits,
Noise::NoiseModel &noise_model, const Config &config);
@ -457,52 +451,6 @@ std::vector<std::string> Controller::available_devices() {
return ret;
}
//-------------------------------------------------------------------------
// Qobj execution
//-------------------------------------------------------------------------
template <typename inputdata_t>
Result Controller::execute(const inputdata_t &input_qobj) {
// Load QOBJ in a try block so we can catch parsing errors and still return
// a valid JSON output containing the error message.
try {
// Start QOBJ timer
auto timer_start = myclock_t::now();
// Initialize QOBJ
Qobj qobj(input_qobj);
auto qobj_time_taken =
std::chrono::duration<double>(myclock_t::now() - timer_start).count();
// Set config
set_config(qobj.config);
// Run qobj circuits
auto result = execute(qobj.circuits, qobj.noise_model, qobj.config);
// Add QOBJ loading time
result.metadata.add(qobj_time_taken, "time_taken_load_qobj");
// Get QOBJ id and pass through header to result
result.qobj_id = qobj.id;
if (!qobj.header.empty()) {
result.header = qobj.header;
}
// Stop the timer and add total timing data including qobj parsing
auto time_taken =
std::chrono::duration<double>(myclock_t::now() - timer_start).count();
result.metadata.add(time_taken, "time_taken");
return result;
} catch (std::exception &e) {
// qobj was invalid, return valid output containing error message
Result result;
result.status = Result::Status::error;
result.message = std::string("Failed to load qobj: ") + e.what();
return result;
}
}
//-------------------------------------------------------------------------
// Experiment execution
//-------------------------------------------------------------------------

View File

@ -35,12 +35,6 @@ void initialize_libraries(const std::string &lib_dir) {
Hacks::maybe_load_openmp(lib_dir);
}
template <class controller_t, typename inputdata_t>
Result controller_execute(const inputdata_t &qobj) {
controller_t controller;
return controller.execute(qobj);
}
template <class controller_t>
Result controller_execute(std::vector<std::shared_ptr<Circuit>> &input_circs,
AER::Noise::NoiseModel &noise_model,

View File

@ -38,7 +38,6 @@ 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"

View File

@ -699,8 +699,7 @@ 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 instruction ("name" is empty).)");
}
// Raise an exception if qubits list is empty

View File

@ -1,201 +0,0 @@
/**
* This code is part of Qiskit.
*
* (C) Copyright IBM 2018, 2019.
*
* This code is licensed under the Apache License, Version 2.0. You may
* obtain a copy of this license in the LICENSE.txt file in the root directory
* of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
*
* Any modifications or derivative works of this code must retain this
* copyright notice, and modified files need to carry a notice indicating
* that they have been altered from the originals.
*/
#ifndef _aer_framework_qobj_hpp_
#define _aer_framework_qobj_hpp_
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
#include "framework/circuit.hpp"
#include "noise/noise_model.hpp"
namespace AER {
//============================================================================
// Qobj data structure
//============================================================================
class Qobj {
public:
//----------------------------------------------------------------
// Constructors
//----------------------------------------------------------------
// Default constructor and destructors
Qobj() = default;
virtual ~Qobj() = default;
// Deserialization constructor
template <typename inputdata_t>
Qobj(const inputdata_t &input);
//----------------------------------------------------------------
// Data
//----------------------------------------------------------------
std::string id; // qobj identifier passed to result
std::string type = "QASM"; // currently we only support QASM
std::vector<std::shared_ptr<Circuit>> 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
};
//============================================================================
// JSON initialization and deserialization
//============================================================================
// JSON deserialization
inline void from_json(const json_t &js, Qobj &qobj) { qobj = Qobj(js); }
template <typename inputdata_t>
Qobj::Qobj(const inputdata_t &input) {
// Check required fields
if (Parser<inputdata_t>::get_value(id, "qobj_id", input) == false) {
throw std::invalid_argument(R"(Invalid qobj: no "qobj_id" field)");
};
Parser<inputdata_t>::get_value(type, "type", input);
if (type != "QASM") {
throw std::invalid_argument(R"(Invalid qobj: "type" != "QASM".)");
};
if (Parser<inputdata_t>::check_key("experiments", input) == false) {
throw std::invalid_argument(R"(Invalid qobj: no "experiments" field.)");
}
// Apply qubit truncation
bool truncation = true;
// Parse config
if (Parser<inputdata_t>::get_value(config, "config", input)) {
// Check for truncation option
Parser<json_t>::get_value(truncation, "enable_truncation", config);
// Load noise model
if (Parser<json_t>::get_value(noise_model, "noise_model", config)) {
// If noise model has non-local errors disable trunction
if (noise_model.has_nonlocal_quantum_errors()) {
truncation = false;
}
}
} else {
config = json_t::object();
}
// Parse header
if (!Parser<inputdata_t>::get_value(header, "header", input)) {
header = json_t::object();
}
// 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
int_t seed = -1;
uint_t seed_shift = 0;
bool has_simulator_seed = Parser<json_t>::get_value(
seed, "seed_simulator", config); // config always json
const auto &circs = Parser<inputdata_t>::get_list("experiments", input);
const size_t num_circs = circs.size();
// Check if parameterized qobj
// It should be of the form
// [exp0_params, exp1_params, ...]
// where:
// expk_params = [((i, j), pars), ....]
// 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<int_t, int_t>;
using exp_params_t = std::vector<std::pair<pos_t, std::vector<double>>>;
std::vector<exp_params_t> param_table;
Parser<json_t>::get_value(param_table, "parameterizations", config);
// Validate parameterizations for number of circuis
if (!param_table.empty() && param_table.size() != num_circs) {
throw std::invalid_argument(
R"(Invalid parameterized qobj: "parameterizations" length does not match number of circuits.)");
}
// Load circuits
for (size_t i = 0; i < num_circs; i++) {
if (param_table.empty() || param_table[i].empty()) {
// Get base circuit from qobj
auto circuit = std::make_shared<Circuit>(
static_cast<inputdata_t>(circs[i]), config, truncation);
// Non parameterized circuit
circuits.push_back(circuit);
} else {
// Get base circuit from qobj without truncation
auto circuit = std::make_shared<Circuit>(
static_cast<inputdata_t>(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; j < num_params; j++) {
// Make a copy of the initial circuit
auto param_circuit = std::make_shared<Circuit>(*circuit);
for (const auto &params : circ_params) {
const auto instr_pos = params.first.first;
const auto param_pos = params.first.second;
// Validation
if (instr_pos == AER::Config::GLOBAL_PHASE_POS) {
// negative position is for global phase
param_circuit->global_phase_angle = params.second[j];
} else {
if ((uint_t)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 ((uint_t)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.
if (truncation)
param_circuit->set_params(true);
circuits.push_back(param_circuit);
}
}
}
// Override random seed with fixed seed if set
// We shift the seed for each successive experiment
// So that results aren't correlated between experiments
if (!has_simulator_seed) {
seed = circuits[0]->seed;
}
for (auto &circuit : circuits) {
circuit->seed = seed + seed_shift;
seed_shift += 2113; // Shift the seed
}
}
//------------------------------------------------------------------------------
} // namespace AER
//------------------------------------------------------------------------------
#endif

View File

@ -1,25 +0,0 @@
macro(add_test_executable target_name)
add_executable(${target_name} ${ARGN})
set_target_properties(${target_name} PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 14)
target_include_directories(${target_name}
PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR}
PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS})
target_link_libraries(${target_name}
PRIVATE AER_DEPENDENCY_PKG::catch2
PRIVATE ${AER_LIBRARIES})
add_test(${target_name} ${target_name})
if(WIN32 AND NOT BLAS_LIB_PATH)
add_custom_command(TARGET test_linalg POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different
${BACKEND_REDIST_DEPS}
$<TARGET_FILE_DIR:${target_name}>)
endif()
endmacro()
add_test_executable(test_linalg "src/test_linalg.cpp")
# Don't forget to add your test target here
add_custom_target(build_tests
test_linalg)

View File

@ -1,57 +0,0 @@
{
"qobj_id": "matrix_observable_snapshot_example",
"schema_version": "1.0.0",
"type": "QASM",
"experiments": [
{
"config": {
"shots": 1000,
"memory_slots": 2,
"n_qubits": 2
},
"instructions": [
{"name": "h", "qubits": [0]},
{"name": "cx", "qubits": [0, 1]},
{
"name": "snapshot",
"type": "expectation_value_matrix",
"label": "<ZZ>pre_measure",
"params": [
[1, [[[0], [[1, 0], [0, -1]]],
[[1], [[1, 0], [0, -1]]]]
]
]
},
{
"name": "snapshot",
"type": "expectation_value_matrix",
"label": "<ZI+IZ>pre_measure",
"params": [
[1, [[[0], [[1, 0], [0, -1]]]]],
[1, [[[1], [[1, 0], [0, -1]]]]]
]
},
{"name": "measure", "qubits": [0, 1], "memory": [0, 1]},
{
"name": "snapshot",
"type": "expectation_value_matrix",
"label": "<ZZ>post_measure",
"params": [
[1, [[[0], [[1, 0], [0, -1]]],
[[1], [[1, 0], [0, -1]]]]
]
]
},
{
"name": "snapshot",
"type": "expectation_value_matrix",
"label": "<ZI+IZ>post_measure",
"params": [
[1, [[[0], [[1, 0], [0, -1]]]]],
[1, [[[1], [[1, 0], [0, -1]]]]]
]
}
]
}
]
}

View File

@ -1,48 +0,0 @@
{
"qobj_id": "pauli_observable_snapshot_example",
"schema_version": "1.0.0",
"type": "QASM",
"experiments": [
{
"config": {
"shots": 1000,
"memory_slots": 2,
"n_qubits": 2
},
"instructions": [
{"name": "h", "qubits": [0]},
{"name": "cx", "qubits": [0, 1]},
{
"name": "snapshot",
"type": "expectation_value_pauli",
"label": "<ZZ>pre_measure",
"qubits": [0, 1],
"params": [[[1, 0] , "ZZ"]]
},
{
"name": "snapshot",
"type": "expectation_value_pauli",
"label": "<ZI+IZ>pre_measure",
"qubits": [0, 1],
"params": [[[1, 0] , "ZI"], [[1, 0], "IZ"]]
},
{"name": "measure", "qubits": [0], "memory": [0]},
{"name": "measure", "qubits": [0], "memory": [1]},
{
"name": "snapshot",
"type": "expectation_value_pauli",
"label": "<ZZ>post_measure",
"qubits": [0, 1],
"params": [[[1, 0] , "ZI"], [[1, 0], "ZZ"]]
},
{
"name": "snapshot",
"type": "expectation_value_pauli",
"label": "<ZI+IZ>post_measure",
"qubits": [0, 1],
"params": [[[1, 0] , "ZI"], [[1, 0], "IZ"]]
}
]
}
]
}

View File

@ -1,24 +0,0 @@
{
"qobj_id": "probabilities_snapshot_example",
"schema_version": "1.0.0",
"type": "QASM",
"experiments": [
{
"config": {
"shots": 1000,
"memory_slots": 2,
"n_qubits": 2
},
"instructions": [
{"name": "h", "qubits": [0]},
{"name": "cx", "qubits": [0, 1]},
{"name": "snapshot", "type": "probabilities",
"label": "pre_measure", "qubits": [0, 1]},
{"name": "measure", "qubits": [0], "memory": [0]},
{"name": "measure", "qubits": [1], "memory": [1]},
{"name": "snapshot", "type": "probabilities",
"label": "post_measure", "qubits": [0, 1]}
]
}
]
}

View File

@ -1,21 +0,0 @@
{
"qobj_id": "state_snapshot_example",
"schema_version": "1.0.0",
"type": "QASM",
"experiments": [
{
"config": {
"shots": 1,
"memory_slots": 0,
"n_qubits": 2
},
"instructions": [
{"name": "snapshot", "type": "statevector", "label": "initial"},
{"name": "h", "qubits": [0]},
{"name": "snapshot", "type": "statevector", "label": "middle"},
{"name": "cx", "qubits": [0, 1]},
{"name": "snapshot", "type": "statevector", "label": "final"}
]
}
]
}

View File

@ -1,401 +0,0 @@
/**
* This code is part of Qiskit.
*
* (C) Copyright IBM 2018, 2019, 2020.
*
* 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.
*/
#define _USE_MATH_DEFINES
#include <framework/stl_ostream.hpp>
#include <map>
#include <math.h>
#include <type_traits>
#include <framework/linalg/linalg.hpp>
#include <framework/types.hpp>
#include <framework/utils.hpp>
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include "utils.hpp"
using namespace AER::Test::Utilities;
namespace {
// check if polar coordinates are almost equal
// r -> (0,inf)
// angle -> (-PI, PI)
template <typename T>
T eps() {
return std::numeric_limits<T>::epsilon();
}
template <typename T>
bool check_polar_coords(T r, T angle, T r_2, T angle_2, T max_diff = eps<T>(),
T max_relative_diff = eps<T>());
template <typename T>
bool check_eigenvector(const std::vector<std::complex<T>> &expected_eigen,
const std::vector<std::complex<T>> &actual_eigen,
T max_diff = eps<T>(), T max_relative_diff = eps<T>());
// Sometimes eigenvectors differ by a factor and/or phase
// This function compares them taking this into account
template <typename T>
bool check_all_eigenvectors(const matrix<std::complex<T>> &expected,
const matrix<std::complex<T>> &actual,
T max_diff = eps<T>(),
T max_relative_diff = eps<T>());
template <typename T>
using scenarioData = std::tuple<std::string, matrix<std::complex<T>>,
matrix<std::complex<T>>, std::vector<T>>;
template <typename T>
matrix<std::complex<T>> herm_mat_2d();
template <typename T>
matrix<std::complex<T>> herm_mat_2d_eigenvectors();
template <typename T>
std::vector<T> herm_mat_2d_eigenvalues();
template <typename T>
scenarioData<T> get_herm_2d_scen();
template <typename T>
matrix<std::complex<T>> psd_mat_2d();
template <typename T>
matrix<std::complex<T>> psd_mat_2d_eigenvectors();
template <typename T>
std::vector<T> psd_mat_2d_eigenvalues();
template <typename T>
scenarioData<T> get_psd_2d_scen();
template <typename T>
matrix<std::complex<T>> psd_mat_2d_with_zero();
template <typename T>
matrix<std::complex<T>> psd_mat_2d_wiht_zero_eigenvectors();
template <typename T>
std::vector<T> psd_mat_2d_wiht_zero_eigenvalues();
template <typename T>
scenarioData<T> get_psd_mat_2d_wiht_zero_scen();
} // namespace
TEST_CASE("Basic Matrix Ops", "[matrix]") {
auto mat = matrix<std::complex<double>>(2, 2);
mat(0, 0) = std::complex<double>(1.0, 0.0);
mat(0, 1) = std::complex<double>(1.0, -2.0);
mat(1, 0) = std::complex<double>(1.0, 2.0);
mat(1, 1) = std::complex<double>(0.5, 0.0);
SECTION("matrix - col_index") {
std::vector<std::complex<double>> col0{{1.0, 0.0}, {1.0, 2.0}};
std::vector<std::complex<double>> col1{{1.0, -2.0}, {0.5, 0.0}};
REQUIRE(compare(col0, mat.col_index(0)));
REQUIRE(compare(col1, mat.col_index(1)));
}
SECTION("matrix - col_index") {
std::vector<std::complex<double>> row0{{1.0, 0.0}, {1.0, -2.0}};
std::vector<std::complex<double>> row1{{1.0, 2.0}, {0.5, 0.0}};
REQUIRE(compare(row0, mat.row_index(0)));
REQUIRE(compare(row1, mat.row_index(1)));
}
}
TEMPLATE_TEST_CASE("Linear Algebra utilities", "[eigen_hermitian]", float,
double) {
std::string scenario_name;
matrix<std::complex<TestType>> herm_mat;
matrix<std::complex<TestType>> expected_eigenvectors;
std::vector<TestType> expected_eigenvalues;
std::tie(scenario_name, herm_mat, expected_eigenvectors,
expected_eigenvalues) =
GENERATE(get_herm_2d_scen<TestType>(), get_psd_2d_scen<TestType>(),
get_psd_mat_2d_wiht_zero_scen<TestType>());
// We are checking results from a numerical method so we allow for some room
// in comparisons
TestType eps_threshold = 5 * eps<TestType>();
SECTION(scenario_name + ": zheevx") {
SECTION("sanity check - eigenvals/vecs should recreate original") {
// sanity check
matrix<std::complex<TestType>> sanity_value(herm_mat.GetRows(),
herm_mat.GetColumns());
for (size_t j = 0; j < expected_eigenvalues.size(); j++) {
sanity_value +=
expected_eigenvalues[j] *
AER::Utils::projector(expected_eigenvectors.col_index(j));
}
REQUIRE(compare(herm_mat, sanity_value, eps_threshold, eps_threshold));
}
SECTION("actual check - heevx returns correctly") {
std::vector<TestType> eigenvalues;
matrix<std::complex<TestType>> eigenvectors;
eigensystem_hermitian(herm_mat, eigenvalues, eigenvectors);
// test equality
REQUIRE(check_all_eigenvectors(expected_eigenvectors, eigenvectors,
eps_threshold, eps_threshold));
REQUIRE(compare(expected_eigenvalues, eigenvalues, eps_threshold,
eps_threshold));
// test reconstruction
matrix<std::complex<TestType>> value(herm_mat.GetRows(),
herm_mat.GetColumns());
for (size_t j = 0; j < eigenvalues.size(); j++) {
value +=
AER::Utils::projector(eigenvectors.col_index(j)) * eigenvalues[j];
}
REQUIRE(compare(herm_mat, value, eps_threshold, eps_threshold));
}
}
}
TEST_CASE("Framework Utilities", "[almost_equal]") {
SECTION("The maximum difference between two scalars over 1.0 is greater than "
"epsilon, so they are amlmost equal") {
double first = 1.0 + eps<double>();
double actual = 1.0;
// Because the max_diff param is bigger than epsilon, this should be almost
// equal
REQUIRE(AER::Linalg::almost_equal(first, actual)); //, 1e-15, 1e-15));
}
SECTION("The difference between two scalars really close to 0 should say are "
"almost equal") {
double first = 5e-323; // Really close to the min magnitude of double
double actual = 6e-323;
REQUIRE(AER::Linalg::almost_equal(first, actual)); //, 1e-323, 1e-323));
}
SECTION("The maximum difference between two complex of doubles over 1.0 is "
"greater than epsilon, so they are almost equal") {
std::complex<double> first = {eps<double>() + double(1.0),
eps<double>() + double(1.0)};
std::complex<double> actual{1.0, 1.0};
// Because the max_diff param is bigger than epsilon, this should be almost
// equal
REQUIRE(AER::Linalg::almost_equal(first, actual)); //, 1e-15, 1e-15));
}
SECTION("The difference between two complex of doubles really close to 0 "
"should say are almost equal") {
std::complex<double> first = {
5e-323, 5e-323}; // Really close to the min magnitude of double
std::complex<double> actual = {6e-323, 6e-323};
REQUIRE(AER::Linalg::almost_equal(first, actual)); // 1e-323, 1e-323));
}
}
TEST_CASE("Test_utils", "[check_polar_coords]") {
auto r = 1.0;
auto angle = M_PI_2;
auto r_2 = 1.0 + eps<double>();
auto angle_2 = M_PI_2 + eps<double>();
SECTION("Check 2 numbers that are equal") {
REQUIRE(check_polar_coords(r, angle, r_2, angle_2));
}
SECTION("Check 2 numbers that differ in absolute value") {
r_2 = r_2 + 1e3 * eps<double>();
REQUIRE(!check_polar_coords(r, angle, r_2, angle_2));
}
SECTION("Check 2 numbers that differ in absolute value") {
angle_2 = angle_2 + 1e3 * eps<double>();
REQUIRE(!check_polar_coords(r, angle, r_2, angle_2));
}
SECTION("Check corner case: close to +/-0 angles") {
angle = 0.0 - eps<double>() / 2.;
angle_2 = -angle;
REQUIRE(check_polar_coords(r, angle, r_2, angle_2));
}
SECTION("Check corner case: angle PI and angle -PI") {
angle = M_PI - eps<double>();
angle_2 = -angle;
REQUIRE(check_polar_coords(r, angle, r_2, angle_2));
}
}
namespace {
template <typename T>
matrix<std::complex<T>> herm_mat_2d() {
auto mat = matrix<std::complex<T>>(2, 2);
mat(0, 0) = std::complex<T>(1.0, 0.0);
mat(0, 1) = std::complex<T>(1.0, 2.0);
mat(1, 0) = std::complex<T>(1.0, -2.0);
mat(1, 1) = std::complex<T>(0.5, 0.0);
return mat;
}
template <typename T>
matrix<std::complex<T>> herm_mat_2d_eigenvectors() {
auto mat = matrix<std::complex<T>>(2, 2);
auto den = 3. * std::sqrt(5.);
mat(0, 0) = std::complex<T>(-2 / den, -4 / den);
mat(1, 0) = std::complex<T>(5 / den, 0.0);
mat(0, 1) = std::complex<T>(-1. / 3., -2. / 3.);
mat(1, 1) = std::complex<T>(-2. / 3., 0);
return mat;
}
template <typename T>
std::vector<T> herm_mat_2d_eigenvalues() {
return {-1.5, 3.0};
}
template <typename T>
scenarioData<T> get_herm_2d_scen() {
return {"Hermitian matrix 2x2", herm_mat_2d<T>(),
herm_mat_2d_eigenvectors<T>(), herm_mat_2d_eigenvalues<T>()};
}
template <typename T>
matrix<std::complex<T>> psd_mat_2d() {
auto psd_matrix = matrix<std::complex<T>>(2, 2);
psd_matrix(0, 0) = std::complex<T>(13., 0.);
psd_matrix(0, 1) = std::complex<T>(0., 5.);
psd_matrix(1, 0) = std::complex<T>(0., -5.);
psd_matrix(1, 1) = std::complex<T>(2., 0.);
return psd_matrix;
}
template <typename T>
matrix<std::complex<T>> psd_mat_2d_eigenvectors() {
matrix<std::complex<T>> expected_eigenvectors(2, 2);
expected_eigenvectors(0, 0) = std::complex<T>(0, -0.3605966767761846214491);
expected_eigenvectors(0, 1) = std::complex<T>(0, -0.9327218431547380506075);
expected_eigenvectors(1, 0) = std::complex<T>(0.9327218431547380506075, 0);
expected_eigenvectors(1, 1) = std::complex<T>(-0.3605966767761846214491, 0);
return expected_eigenvectors;
}
template <typename T>
std::vector<T> psd_mat_2d_eigenvalues() {
return {0.06696562634074720854471252, 14.93303437365925212532147};
}
template <typename T>
scenarioData<T> get_psd_2d_scen() {
return {"PSD matrix 2x2", psd_mat_2d<T>(), psd_mat_2d_eigenvectors<T>(),
psd_mat_2d_eigenvalues<T>()};
}
template <typename T>
matrix<std::complex<T>> psd_mat_2d_with_zero() {
auto psd_matrix = matrix<std::complex<T>>(2, 2);
psd_matrix(0, 0) = std::complex<T>(1., 0.);
psd_matrix(0, 1) = std::complex<T>(2., 0.);
psd_matrix(1, 0) = std::complex<T>(2., 0.);
psd_matrix(1, 1) = std::complex<T>(4., 0.);
return psd_matrix;
}
template <typename T>
matrix<std::complex<T>> psd_mat_2d_wiht_zero_eigenvectors() {
matrix<std::complex<T>> expected_eigenvectors(2, 2);
expected_eigenvectors(0, 0) = std::complex<T>(-2. / std::sqrt(5.), 0);
expected_eigenvectors(0, 1) = std::complex<T>(1. / std::sqrt(5.), 0);
expected_eigenvectors(1, 0) = std::complex<T>(1. / std::sqrt(5.), 0);
expected_eigenvectors(1, 1) = std::complex<T>(2. / std::sqrt(5.), 0);
return expected_eigenvectors;
}
template <typename T>
std::vector<T> psd_mat_2d_wiht_zero_eigenvalues() {
return {0.0, 5.0};
}
template <typename T>
scenarioData<T> get_psd_mat_2d_wiht_zero_scen() {
return {"PSD matrix 2x2 with a zero eigen value", psd_mat_2d_with_zero<T>(),
psd_mat_2d_wiht_zero_eigenvectors<T>(),
psd_mat_2d_wiht_zero_eigenvalues<T>()};
}
template <typename T>
bool check_polar_coords(T r, T angle, T r_2, T angle_2, T max_diff,
T max_relative_diff) {
if (!AER::Linalg::almost_equal(r, r_2, max_diff, max_relative_diff))
return false;
if (!AER::Linalg::almost_equal(angle, angle_2, max_diff, max_relative_diff)) {
// May be corner case with PI and -PI
T angle_plus = angle > 0. ? angle : angle + 2 * M_PI;
T angle_2_plus = angle_2 > 0. ? angle_2 : angle_2 + 2 * M_PI;
if (!AER::Linalg::almost_equal(angle_plus, angle_2_plus, max_diff,
max_relative_diff))
return false;
}
return true;
}
template <typename T>
bool check_eigenvector(const std::vector<std::complex<T>> &expected_eigen,
const std::vector<std::complex<T>> &actual_eigen,
T max_diff, T max_relative_diff) {
auto div = expected_eigen[0] / actual_eigen[0];
T r = std::abs(div);
T angle = std::arg(div);
for (size_t j = 1; j < expected_eigen.size(); j++) {
auto div_2 = expected_eigen[j] / actual_eigen[j];
T r_2 = std::abs(div_2);
T angle_2 = std::arg(div_2);
// Check that factor is consistent across components
if (!check_polar_coords(r, angle, r_2, angle_2, max_diff,
max_relative_diff)) {
return false;
}
}
return true;
}
template <typename T>
bool check_all_eigenvectors(const matrix<std::complex<T>> &expected,
const matrix<std::complex<T>> &actual, T max_diff,
T max_relative_diff) {
auto col_num = expected.GetColumns();
if (expected.size() != actual.size() ||
expected.GetColumns() != expected.GetColumns()) {
return false;
}
for (size_t i = 0; i < col_num; i++) {
auto expected_eigen = expected.col_index(i);
auto actual_eigen = actual.col_index(i);
if (!check_eigenvector(expected_eigen, actual_eigen, max_diff,
max_relative_diff)) {
std::cout << "Expected: " << std::setprecision(16) << expected
<< std::endl;
std::cout << "Actual: " << actual << std::endl;
return false;
}
}
return true;
}
} // namespace

View File

@ -1,44 +0,0 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <map>
#include <controllers/qasm_controller.hpp>
#include "utils.hpp"
namespace AER {
namespace Test {
TEST_CASE("Simulators Snapshot", "[snaphot]") {
std::map<std::string, json_t> qobj_snapshots;
qobj_snapshots["state"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_statevector.json");
qobj_snapshots["probs"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_probs.json");
qobj_snapshots["pauli"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_expval_pauli.json");
qobj_snapshots["matrix"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_expval_matrix.json");
AER::Simulator::QasmController sim{};
SECTION("State simulator snapshot") {
auto expected_result = R"({
"final":[[[0.7071067811865476,0.0],[0.0,0.0],[0.0,0.0],[0.7071067811865475,0.0]]],
"initial":[[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]],
"middle":[[[0.7071067811865476,0.0],[0.7071067811865475,0.0],[0.0,0.0],[0.0,0.0]]]
})"_json;
auto result = sim.execute(qobj_snapshots["state"]);
result = result["results"][0]["data"]["snapshots"]["state"];
REQUIRE(result == expected_result);
}
SECTION("Probs simulator snapshot") { REQUIRE(false); }
SECTION("Pauli simulator snaphsot") { REQUIRE(false); }
SECTION("Unitary simulator snapshot") { REQUIRE(false); }
}
//------------------------------------------------------------------------------
} // end namespace Test
//------------------------------------------------------------------------------
} // end namespace AER
//------------------------------------------------------------------------------

View File

@ -1,49 +0,0 @@
#define CATCH_CONFIG_MAIN
#include <catch2/catch.hpp>
#include <map>
#include <controllers/qasm_controller.hpp>
#include "utils.hpp"
namespace AER {
namespace Test {
SCENARIO("We can get snapshots from different simulator types") {
GIVEN("A Qobj with snapshot information for every simulator type") {
std::map<std::string, json_t> qobj_snapshots;
qobj_snapshots["state"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_statevector.json");
qobj_snapshots["probs"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_probs.json");
qobj_snapshots["pauli"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_expval_pauli.json");
qobj_snapshots["matrix"] = AER::Test::Utilities::load_qobj(
"../../test/data/qobj_snapshot_expval_matrix.json");
AER::Simulator::QasmController sim{};
WHEN("we get the expected results") {
auto expected_result = R"({
"final":[[[0.7071067811865476,0.0],[0.0,0.0],[0.0,0.0],[0.7071067811865475,0.0]]],
"initial":[[[1.0,0.0],[0.0,0.0],[0.0,0.0],[0.0,0.0]]],
"middle":[[[0.7071067811865476,0.0],[0.7071067811865475,0.0],[0.0,0.0],[0.0,0.0]]]
})"_json;
THEN("the state simulator should pass") {
auto result = sim.execute(qobj_snapshots["state"]);
result = result["results"][0]["data"]["snapshots"]["state"];
REQUIRE(result == expected_result);
}
THEN("the probs simulator should pass") { REQUIRE(false); }
THEN("the pauli simulator should pass") { REQUIRE(false); }
THEN("the unitary matrix simulator should pass") { REQUIRE(false); }
}
}
}
//------------------------------------------------------------------------------
} // end namespace Test
//------------------------------------------------------------------------------
} // end namespace AER
//------------------------------------------------------------------------------

View File

@ -1,128 +0,0 @@
/**
* This code is part of Qiskit.
*
* (C) Copyright IBM 2018, 2019, 2020.
*
* 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.
*/
#include "framework/json.hpp"
#include <string>
#include <catch2/catch.hpp>
namespace Catch {
template <typename T>
std::string convertMyTypeToString(const std::vector<T> &value) {
std::stringstream oss;
oss << value;
return oss.str();
}
template <typename T>
struct StringMaker<std::vector<T>> {
static std::string convert(const std::vector<AER::complex_t> &value) {
return convertMyTypeToString(value);
}
};
} // namespace Catch
namespace AER {
namespace Test {
namespace Utilities {
inline json_t load_qobj(const std::string &filename) {
return JSON::load(filename);
}
template <typename T>
T calculate_floats(T start, T decrement, int count) {
for (int i = 0; i < count; ++i)
start -= decrement;
return start;
}
template <typename T, typename U>
bool _compare(const matrix<T> &lhs, const matrix<T> &rhs,
U max_diff = std::numeric_limits<U>::epsilon(),
U max_relative_diff = std::numeric_limits<U>::epsilon()) {
bool res = true;
std::ostringstream message;
if (lhs.size() != rhs.size()) {
res = false;
}
for (size_t i = 0; i < lhs.GetRows(); ++i) {
for (size_t j = 0; j < lhs.GetColumns(); ++j) {
if (!(AER::Linalg::almost_equal(lhs(i, j), rhs(i, j), max_diff,
max_relative_diff))) {
message << "Matrices differ at element: (" << i << ", " << j << ")"
<< std::setprecision(22) << ". [" << lhs(i, j) << "] != ["
<< rhs(i, j) << "]\n";
res = false;
}
}
}
if (!res) {
message << "Matrices differ: " << lhs << " != " << rhs << std::endl;
std::cout << message.str();
}
return res;
}
template <typename T>
bool compare(const matrix<T> &lhs, const matrix<T> &rhs,
T max_diff = std::numeric_limits<T>::epsilon(),
T max_relative_diff = std::numeric_limits<T>::epsilon()) {
return _compare(lhs, rhs, max_diff, max_relative_diff);
}
template <typename T>
bool compare(const matrix<std::complex<T>> &lhs,
const matrix<std::complex<T>> &rhs,
T max_diff = std::numeric_limits<T>::epsilon(),
T max_relative_diff = std::numeric_limits<T>::epsilon()) {
return _compare(lhs, rhs, max_diff, max_relative_diff);
}
template <typename T, typename U>
bool _compare(const std::vector<T> &lhs, const std::vector<T> &rhs,
U max_diff = std::numeric_limits<U>::epsilon(),
U max_relative_diff = std::numeric_limits<U>::epsilon()) {
if (lhs.size() != rhs.size())
return false;
for (size_t i = 0; i < lhs.size(); ++i) {
if (!(AER::Linalg::almost_equal(lhs[i], rhs[i], max_diff,
max_relative_diff))) {
std::cout << "Vectors differ at element: " << i << std::setprecision(22)
<< ". [" << lhs[i] << "] != [" << rhs[i] << "]\n";
std::cout << "Vectors differ: " << Catch::convertMyTypeToString(lhs)
<< " != " << Catch::convertMyTypeToString(rhs) << std::endl;
return false;
}
}
return true;
}
template <typename T>
bool compare(const std::vector<T> &lhs, const std::vector<T> &rhs,
T max_diff = std::numeric_limits<T>::epsilon(),
T max_relative_diff = std::numeric_limits<T>::epsilon()) {
return _compare(lhs, rhs, max_diff, max_relative_diff);
}
template <typename T>
bool compare(const std::vector<std::complex<T>> &lhs,
const std::vector<std::complex<T>> &rhs,
T max_diff = std::numeric_limits<T>::epsilon(),
T max_relative_diff = std::numeric_limits<T>::epsilon()) {
return _compare(lhs, rhs, max_diff, max_relative_diff);
}
} // namespace Utilities
} // namespace Test
} // namespace AER

View File

@ -1,99 +0,0 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2018, 2019.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=arguments-differ
import unittest
import logging
from ddt import ddt, data
from qiskit import transpile, assemble
from qiskit.providers import JobError
from qiskit.circuit.random import random_circuit
from qiskit_aer.jobs import split_qobj
from test.terra.reference.ref_save_expval import (
save_expval_circuits,
save_expval_circuit_parameterized,
)
from test.terra.backends.simulator_test_case import SimulatorTestCase
@ddt
class TestJobSplitting(SimulatorTestCase):
"""Test job splitting option"""
@staticmethod
def parameterized_circuits():
"""Return ParameterizedQobj for settings."""
pcirc1, param1 = save_expval_circuit_parameterized(
pershot=False,
measure=True,
snapshot=False,
)
circuits2to4 = save_expval_circuits(
pauli=True,
skip_measure=False,
pershot=False,
)
pcirc2, param2 = save_expval_circuit_parameterized(
pershot=False,
measure=True,
snapshot=False,
)
circuits = [pcirc1] + circuits2to4 + [pcirc2]
params = [param1, [], [], [], param2]
return circuits, params
def split_compare(self, circs, backend, parameterizations=None):
"""Qobj split test"""
qobj = assemble(circs, parameterizations=parameterizations, qobj_id="testing")
if parameterizations:
qobjs = [
assemble(c, parameterizations=[p], qobj_id="testing")
for (c, p) in zip(circs, parameterizations)
]
else:
qobjs = [assemble(c, qobj_id="testing") for c in circs]
test_qobjs = split_qobj(qobj, max_size=1, qobj_id="testing")
self.assertEqual(len(test_qobjs[0]), len(qobjs))
for ref, test in zip(qobjs, test_qobjs[0]):
self.assertEqual(ref, test)
def add_custom_instruction(self):
backend = self.backend(max_job_size=1, max_shot_size=1)
circ = random_circuit(num_qubits=2, depth=4)
circ.save_statevector()
circ = transpile(circ, backend)
qobj = assemble(circ)
split_qobj(qobj, max_size=1, max_shot_size=1, qobj_id="testing")
def test_split(self):
"""Circuits split test"""
backend = self.backend(max_job_size=1)
circs = [random_circuit(num_qubits=2, depth=4, measure=True, seed=i) for i in range(2)]
circs = transpile(circs, backend)
self.split_compare(circs, backend)
def test_parameterized_split(self):
"""Parameterized circuits split test"""
backend = self.backend(max_job_size=1)
circs, params = self.parameterized_circuits()
self.split_compare(circs, backend, parameterizations=params)
def test_custom_instruction_error(self):
with self.assertRaises(JobError):
self.add_custom_instruction()
if __name__ == "__main__":
unittest.main()

View File

@ -20,8 +20,7 @@ import numpy as np
from test.terra import common
import qiskit
from qiskit.compiler import assemble, transpile
from qiskit.compiler import transpile
from qiskit.circuit import QuantumCircuit, Parameter
from test.terra.reference.ref_save_expval import (
save_expval_circuits,
@ -35,8 +34,8 @@ from qiskit_aer.library import SaveStatevector
from qiskit_aer import AerSimulator, AerError
class TestParameterizedQobj(common.QiskitAerTestCase):
"""Parameterized Qobj extension tests"""
class TestParameterizedCircuit(common.QiskitAerTestCase):
"""Parameterized circuit extension tests"""
BACKEND_OPTS = {"seed_simulator": 2113}

View File

@ -22,8 +22,7 @@ import numpy as np
from test.terra import common
import qiskit
from qiskit.compiler import assemble, transpile
from qiskit.compiler import transpile
from qiskit.circuit import QuantumCircuit, Parameter
from test.terra.reference.ref_save_expval import (
save_expval_circuits,

View File

@ -1,76 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright 2021, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.
import json
import os
from qiskit import ClassicalRegister
from qiskit.compiler import assemble, transpile
from qiskit import QuantumCircuit
from qiskit import QuantumRegister
def grovers_circuit(final_measure=True, allow_sampling=True):
"""Testing a circuit originated in the Grover algorithm"""
circuits = []
# 6-qubit grovers
qr = QuantumRegister(6)
if final_measure:
cr = ClassicalRegister(2)
regs = (qr, cr)
else:
regs = (qr,)
circuit = QuantumCircuit(*regs)
circuit.h(qr[0])
circuit.h(qr[1])
circuit.x(qr[2])
circuit.x(qr[3])
circuit.x(qr[0])
circuit.cx(qr[0], qr[2])
circuit.x(qr[0])
circuit.cx(qr[1], qr[3])
circuit.ccx(qr[2], qr[3], qr[4])
circuit.cx(qr[1], qr[3])
circuit.x(qr[0])
circuit.cx(qr[0], qr[2])
circuit.x(qr[0])
circuit.x(qr[1])
circuit.x(qr[4])
circuit.h(qr[4])
circuit.ccx(qr[0], qr[1], qr[4])
circuit.h(qr[4])
circuit.x(qr[0])
circuit.x(qr[1])
circuit.x(qr[4])
circuit.h(qr[0])
circuit.h(qr[1])
circuit.h(qr[4])
if final_measure:
circuit.barrier(qr)
circuit.measure(qr[0], cr[0])
circuit.measure(qr[1], cr[1])
if not allow_sampling:
circuit.barrier(qr)
circuit.id(qr)
circuits.append(circuit)
return circuits
if __name__ == "__main__":
# Run qasm simulator
shots = 4000
circuits = grovers_circuit(final_measure=True, allow_sampling=True)
if os.getenv("USE_MPI", False):
qobj = assemble(transpile(circuits), shots=shots, blocking_enable=True, blocking_qubits=2)
else:
qobj = assemble(transpile(circuits), shots=shots)
with open("qobj.json", "wt") as fp:
json.dump(qobj.to_dict(), fp)

View File

@ -8,7 +8,7 @@
import numpy as np
from qiskit import ClassicalRegister
from qiskit.compiler import assemble, transpile
from qiskit.compiler import transpile
from qiskit import QuantumCircuit
from qiskit import QuantumRegister
from qiskit.quantum_info import Operator, Statevector