* Added scikit-build support for building Cython extensions

This commit is contained in:
Juan Gomez 2018-08-31 17:13:51 +02:00
parent bc0028b046
commit a76297c654
11 changed files with 1328 additions and 17 deletions

View File

@ -12,8 +12,8 @@
cmake_minimum_required(VERSION 3.6)
project(aer_simulator_cpp LANGUAGES CXX)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
set(CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/cmake)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_SOURCE_DIR}/cmake)
option(STATIC_LINKING "Specify if we want statically link the executable (for
redistribution mainly)" FALSE)
@ -161,3 +161,6 @@ target_link_libraries(aer_simulator_cpp PRIVATE ${AER_LIBRARIES})
if(BUILD_TESTS)
add_subdirectory(test)
endif()
# Cyhton build
add_subdirectory(src/bindings/python)

78
cmake/FindCython.cmake Normal file
View File

@ -0,0 +1,78 @@
#.rst:
#
# Find ``cython`` executable.
#
# This module will set the following variables in your project:
#
# ``CYTHON_EXECUTABLE``
# path to the ``cython`` program
#
# ``CYTHON_VERSION``
# version of ``cython``
#
# ``CYTHON_FOUND``
# true if the program was found
#
# For more information on the Cython project, see http://cython.org/.
#
# *Cython is a language that makes writing C extensions for the Python language
# as easy as Python itself.*
#
#=============================================================================
# Copyright 2011 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
# Use the Cython executable that lives next to the Python executable
# if it is a local installation.
find_package(PythonInterp)
if(PYTHONINTERP_FOUND)
get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH)
find_program(CYTHON_EXECUTABLE
NAMES cython cython.bat cython3
HINTS ${_python_path}
DOC "path to the cython executable")
else()
find_program(CYTHON_EXECUTABLE
NAMES cython cython.bat cython3
DOC "path to the cython executable")
endif()
if(CYTHON_EXECUTABLE)
set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version)
execute_process(COMMAND ${CYTHON_version_command}
OUTPUT_VARIABLE CYTHON_version_output
ERROR_VARIABLE CYTHON_version_error
RESULT_VARIABLE CYTHON_version_result
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT ${CYTHON_version_result} EQUAL 0)
set(_error_msg "Command \"${CYTHON_version_command}\" failed with")
set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}")
message(SEND_ERROR "${_error_msg}")
else()
if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)")
set(CYTHON_VERSION "${CMAKE_MATCH_1}")
endif()
endif()
endif()
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE)
mark_as_advanced(CYTHON_EXECUTABLE)
include(UseCython)

396
cmake/UseCython.cmake Normal file
View File

@ -0,0 +1,396 @@
#.rst:
#
# The following functions are defined:
#
# .. cmake:command:: add_cython_target
#
# Create a custom rule to generate the source code for a Python extension module
# using cython.
#
# add_cython_target(<Name> [<CythonInput>]
# [EMBED_MAIN]
# [C | CXX]
# [PY2 | PY3]
# [OUTPUT_VAR <OutputVar>])
#
# ``<Name>`` is the name of the new target, and ``<CythonInput>``
# is the path to a cython source file. Note that, despite the name, no new
# targets are created by this function. Instead, see ``OUTPUT_VAR`` for
# retrieving the path to the generated source for subsequent targets.
#
# If only ``<Name>`` is provided, and it ends in the ".pyx" extension, then it
# is assumed to be the ``<CythonInput>``. The name of the input without the
# extension is used as the target name. If only ``<Name>`` is provided, and it
# does not end in the ".pyx" extension, then the ``<CythonInput>`` is assumed to
# be ``<Name>.pyx``.
#
# The Cython include search path is amended with any entries found in the
# ``INCLUDE_DIRECTORIES`` property of the directory containing the
# ``<CythonInput>`` file. Use ``include_directories`` to add to the Cython
# include search path.
#
# Options:
#
# ``EMBED_MAIN``
# Embed a main() function in the generated output (for stand-alone
# applications that initialize their own Python runtime).
#
# ``C | CXX``
# Force the generation of either a C or C++ file. By default, a C file is
# generated, unless the C language is not enabled for the project; in this
# case, a C++ file is generated by default.
#
# ``PY2 | PY3``
# Force compilation using either Python-2 or Python-3 syntax and code
# semantics. By default, Python-2 syntax and semantics are used if the major
# version of Python found is 2. Otherwise, Python-3 syntax and sematics are
# used.
#
# ``OUTPUT_VAR <OutputVar>``
# Set the variable ``<OutputVar>`` in the parent scope to the path to the
# generated source file. By default, ``<Name>`` is used as the output
# variable name.
#
# Defined variables:
#
# ``<OutputVar>``
# The path of the generated source file.
#
# Cache variables that effect the behavior include:
#
# ``CYTHON_ANNOTATE``
# whether to create an annotated .html file when compiling
#
# ``CYTHON_FLAGS``
# additional flags to pass to the Cython compiler
#
# Example usage
# ^^^^^^^^^^^^^
#
# .. code-block:: cmake
#
# find_package(Cython)
#
# # Note: In this case, either one of these arguments may be omitted; their
# # value would have been inferred from that of the other.
# add_cython_target(cy_code cy_code.pyx)
#
# add_library(cy_code MODULE ${cy_code})
# target_link_libraries(cy_code ...)
#
#=============================================================================
# Copyright 2011 Kitware, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#=============================================================================
# Configuration options.
set(CYTHON_ANNOTATE OFF
CACHE BOOL "Create an annotated .html file when compiling *.pyx.")
set(CYTHON_FLAGS "" CACHE STRING
"Extra flags to the cython compiler.")
mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS)
string(REGEX REPLACE " " ";" CYTHON_FLAGS_LIST "${CYTHON_FLAGS}")
find_package(PythonLibs REQUIRED)
set(CYTHON_CXX_EXTENSION "cxx")
set(CYTHON_C_EXTENSION "c")
get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES)
function(add_cython_target _name)
set(options EMBED_MAIN C CXX PY2 PY3)
set(options1 OUTPUT_VAR)
cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN})
list(GET _args_UNPARSED_ARGUMENTS 0 _arg0)
# if provided, use _arg0 as the input file path
if(_arg0)
set(_source_file ${_arg0})
# otherwise, must determine source file from name, or vice versa
else()
get_filename_component(_name_ext "${_name}" EXT)
# if extension provided, _name is the source file
if(_name_ext)
set(_source_file ${_name})
get_filename_component(_name "${_source_file}" NAME_WE)
# otherwise, assume the source file is ${_name}.pyx
else()
set(_source_file ${_name}.pyx)
endif()
endif()
set(_embed_main FALSE)
if("C" IN_LIST languages)
set(_output_syntax "C")
elseif("CXX" IN_LIST languages)
set(_output_syntax "CXX")
else()
message(FATAL_ERROR "Either C or CXX must be enabled to use Cython")
endif()
if("${PYTHONLIBS_VERSION_STRING}" MATCHES "^2.")
set(_input_syntax "PY2")
else()
set(_input_syntax "PY3")
endif()
if(_args_EMBED_MAIN)
set(_embed_main TRUE)
endif()
if(_args_C)
set(_output_syntax "C")
endif()
if(_args_CXX)
set(_output_syntax "CXX")
endif()
if(_args_PY2)
set(_input_syntax "PY2")
endif()
if(_args_PY3)
set(_input_syntax "PY3")
endif()
set(embed_arg "")
if(_embed_main)
set(embed_arg "--embed")
endif()
set(cxx_arg "")
set(extension "c")
if(_output_syntax STREQUAL "CXX")
set(cxx_arg "--cplus")
set(extension "cxx")
endif()
set(py_version_arg "")
if(_input_syntax STREQUAL "PY2")
set(py_version_arg "-2")
elseif(_input_syntax STREQUAL "PY3")
set(py_version_arg "-3")
endif()
set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}")
set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE)
set(_output_var ${_name})
if(_args_OUTPUT_VAR)
set(_output_var ${_args_OUTPUT_VAR})
endif()
set(${_output_var} ${generated_file} PARENT_SCOPE)
file(RELATIVE_PATH generated_file_relative
${CMAKE_BINARY_DIR} ${generated_file})
set(comment "Generating ${_output_syntax} source ${generated_file_relative}")
set(cython_include_directories "")
set(pxd_dependencies "")
set(c_header_dependencies "")
# Get the include directories.
get_source_file_property(pyx_location ${_source_file} LOCATION)
get_filename_component(pyx_path ${pyx_location} PATH)
get_directory_property(cmake_include_directories
DIRECTORY ${pyx_path}
INCLUDE_DIRECTORIES)
list(APPEND cython_include_directories ${cmake_include_directories})
# Determine dependencies.
# Add the pxd file with the same basename as the given pyx file.
get_filename_component(pyx_file_basename ${_source_file} NAME_WE)
unset(corresponding_pxd_file CACHE)
find_file(corresponding_pxd_file ${pyx_file_basename}.pxd
PATHS "${pyx_path}" ${cmake_include_directories}
NO_DEFAULT_PATH)
if(corresponding_pxd_file)
list(APPEND pxd_dependencies "${corresponding_pxd_file}")
endif()
# pxd files to check for additional dependencies
set(pxds_to_check "${_source_file}" "${pxd_dependencies}")
set(pxds_checked "")
set(number_pxds_to_check 1)
while(number_pxds_to_check GREATER 0)
foreach(pxd ${pxds_to_check})
list(APPEND pxds_checked "${pxd}")
list(REMOVE_ITEM pxds_to_check "${pxd}")
# look for C headers
file(STRINGS "${pxd}" extern_from_statements
REGEX "cdef[ ]+extern[ ]+from.*$")
foreach(statement ${extern_from_statements})
# Had trouble getting the quote in the regex
string(REGEX REPLACE
"cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1"
header "${statement}")
unset(header_location CACHE)
find_file(header_location ${header} PATHS ${cmake_include_directories})
if(header_location)
list(FIND c_header_dependencies "${header_location}" header_idx)
if(${header_idx} LESS 0)
list(APPEND c_header_dependencies "${header_location}")
endif()
endif()
endforeach()
# check for pxd dependencies
# Look for cimport statements.
set(module_dependencies "")
file(STRINGS "${pxd}" cimport_statements REGEX cimport)
foreach(statement ${cimport_statements})
if(${statement} MATCHES from)
string(REGEX REPLACE
"from[ ]+([^ ]+).*" "\\1"
module "${statement}")
else()
string(REGEX REPLACE
"cimport[ ]+([^ ]+).*" "\\1"
module "${statement}")
endif()
list(APPEND module_dependencies ${module})
endforeach()
# check for pxi dependencies
# Look for include statements.
set(include_dependencies "")
file(STRINGS "${pxd}" include_statements REGEX include)
foreach(statement ${include_statements})
string(REGEX REPLACE
"include[ ]+[\"]([^\"]+)[\"].*" "\\1"
module "${statement}")
list(APPEND include_dependencies ${module})
endforeach()
list(REMOVE_DUPLICATES module_dependencies)
list(REMOVE_DUPLICATES include_dependencies)
# Add modules to the files to check, if appropriate.
foreach(module ${module_dependencies})
unset(pxd_location CACHE)
find_file(pxd_location ${module}.pxd
PATHS "${pyx_path}" ${cmake_include_directories}
NO_DEFAULT_PATH)
if(pxd_location)
list(FIND pxds_checked ${pxd_location} pxd_idx)
if(${pxd_idx} LESS 0)
list(FIND pxds_to_check ${pxd_location} pxd_idx)
if(${pxd_idx} LESS 0)
list(APPEND pxds_to_check ${pxd_location})
list(APPEND pxd_dependencies ${pxd_location})
endif() # if it is not already going to be checked
endif() # if it has not already been checked
endif() # if pxd file can be found
endforeach() # for each module dependency discovered
# Add includes to the files to check, if appropriate.
foreach(_include ${include_dependencies})
unset(pxi_location CACHE)
find_file(pxi_location ${_include}
PATHS "${pyx_path}" ${cmake_include_directories}
NO_DEFAULT_PATH)
if(pxi_location)
list(FIND pxds_checked ${pxi_location} pxd_idx)
if(${pxd_idx} LESS 0)
list(FIND pxds_to_check ${pxi_location} pxd_idx)
if(${pxd_idx} LESS 0)
list(APPEND pxds_to_check ${pxi_location})
list(APPEND pxd_dependencies ${pxi_location})
endif() # if it is not already going to be checked
endif() # if it has not already been checked
endif() # if include file can be found
endforeach() # for each include dependency discovered
endforeach() # for each include file to check
list(LENGTH pxds_to_check number_pxds_to_check)
endwhile()
# Set additional flags.
set(annotate_arg "")
if(CYTHON_ANNOTATE)
set(annotate_arg "--annotate")
endif()
set(no_docstrings_arg "")
if(CMAKE_BUILD_TYPE STREQUAL "Release" OR
CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
set(no_docstrings_arg "--no-docstrings")
endif()
set(cython_debug_arg "")
set(embed_pos_arg "")
set(line_directives_arg "")
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR
CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
set(cython_debug_arg "--gdb")
set(embed_pos_arg "--embed-positions")
set(line_directives_arg "--line-directives")
endif()
# Include directory arguments.
list(REMOVE_DUPLICATES cython_include_directories)
set(include_directory_arg "")
foreach(_include_dir ${cython_include_directories})
set(include_directory_arg
${include_directory_arg} "--include-dir" "${_include_dir}")
endforeach()
list(REMOVE_DUPLICATES pxd_dependencies)
list(REMOVE_DUPLICATES c_header_dependencies)
# Add the command to run the compiler.
add_custom_command(OUTPUT ${generated_file}
COMMAND ${CYTHON_EXECUTABLE}
ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg}
${embed_arg} ${annotate_arg} ${no_docstrings_arg}
${cython_debug_arg} ${embed_pos_arg}
${line_directives_arg} ${CYTHON_FLAGS_LIST} ${pyx_location}
--output-file ${generated_file}
DEPENDS ${_source_file}
${pxd_dependencies}
IMPLICIT_DEPENDS ${_output_syntax}
${c_header_dependencies}
COMMENT ${comment})
# NOTE(opadron): I thought about making a proper target, but after trying it
# out, I decided that it would be far too convenient to use the same name as
# the target for the extension module (e.g.: for single-file modules):
#
# ...
# add_cython_target(_module.pyx)
# add_library(_module ${_module})
# ...
#
# The above example would not be possible since the "_module" target name
# would already be taken by the cython target. Since I can't think of a
# reason why someone would need the custom target instead of just using the
# generated file directly, I decided to leave this commented out.
#
# add_custom_target(${_name} DEPENDS ${generated_file})
# Remove their visibility to the user.
set(corresponding_pxd_file "" CACHE INTERNAL "")
set(header_location "" CACHE INTERNAL "")
set(pxd_location "" CACHE INTERNAL "")
endfunction()

2
pyproject.toml Normal file
View File

@ -0,0 +1,2 @@
[build-system]
requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja"]

2
requirements-dev.txt Normal file
View File

@ -0,0 +1,2 @@
scikit-build
cython

View File

@ -1,20 +1,27 @@
from distutils.core import setup
from Cython.Build import cythonize
from skbuild import setup
import sys
import os
sys.path.append(os.path.abspath('./src/simulators'))
from simulator_extension import simulator_extension
# Simulator extension
package_name = 'aer.backends.aer_qv_wrapper'
source_files = [os.path.abspath('src/simulators/qubitvector/qv_wrapper.pyx')]
include_dirs = [os.path.abspath('./src')]
simulator = simulator_extension(package_name, source_files, include_dirs=include_dirs)
#sys.path.append(os.path.abspath('./'))
setup(
name=package_name,
packages=[package_name],
ext_modules=cythonize(simulator)
name='aer_qv_wrapper',
packages=['aer_qv_wrapper'],
version="0.0.1",
description="QV Quantum Simulator",
author="AER Development Team",
author_email="qiskit@us.ibm.com",
license="Apache 2.0",
classifiers=[
"Environment :: Console",
"License :: OSI Approved :: Apache Software License",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Scientific/Engineering",
],
keywords="qiskit simulator quantum"
)

View File

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.6.0)
project(aer_qv_wrapper LANGUAGES CXX)
find_package(Cython REQUIRED)
find_package(PythonLibs REQUIRED)
add_cython_target(aer_qv_wrapper qv_wrapper.pyx)
add_library(aer_qv_wrapper MODULE ${aer_qv_wrapper})
set_target_properties(aer_qv_wrapper PROPERTIES
LINKER_LANGUAGE CXX
CXX_STANDARD 14)
target_include_directories(aer_qv_wrapper
PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR}
PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS}
PRIVATE ${PYTHON_INCLUDE_DIRS})
target_link_libraries(aer_qv_wrapper
${PYTHON_LIBRARIES}
${AER_LIBRARIES})
install(TARGETS aer_qv_wrapper LIBRARY DESTINATION aer_qv_wrapper)

View File

@ -0,0 +1,104 @@
"""
Cython quantum circuit simulator.
"""
import sys
import os
import json
import logging
import warnings
import numpy as np
# Import QISKit classes
from qiskit._result import Result
from qiskit.backends import BaseBackend
from qiskit.backends.local.localjob import LocalJob
# Import Simulator tools
from helpers import SimulatorJSONEncoder, qobj2schema
from aer_qv_wrapper import AerSimulatorWrapper
# Logger
logger = logging.getLogger(__name__)
class AerSimulator(BaseBackend):
"""Cython quantum circuit simulator"""
DEFAULT_CONFIGURATION = {
'name': 'local_aer_simulator',
'url': 'NA',
'simulator': True,
'local': True,
'description': 'A C++ statevector simulator for qobj files',
'coupling_map': 'all-to-all',
"basis_gates": 'u0,u1,u2,u3,cx,cz,id,x,y,z,h,s,sdg,t,tdg,rzz'
}
def __init__(self, configuration=None):
super().__init__(configuration or self.DEFAULT_CONFIGURATION.copy())
self.simulator = AerSimulatorWrapper()
def run(self, qobj):
"""Run a QOBJ on the the backend."""
return LocalJob(self._run_job, qobj)
def _run_job(self, qobj):
self._validate(qobj)
qobj_str = json.dumps(qobj2schema(qobj), cls=SimulatorJSONEncoder)
result = json.loads(self.simulator.execute(qobj_str))
# TODO: get schema in line with result object
return result # Result(result)
def load_noise_model(self, noise_model):
self.simulator.load_noise_model(json.dumps(noise_model, cls=SimulatorJSONEncoder))
def clear_noise_model(self):
self.simulator.clear_noise_model()
def load_config(self, engine=None, state=None):
if engine is not None:
self.simulator.load_engine_config(json.dumps(engine, cls=SimulatorJSONEncoder))
if state is not None:
self.simulator.load_state_config(json.dumps(state, cls=SimulatorJSONEncoder))
def set_max_threads_shot(self, threads):
"""
Set the maximum threads used for parallel shot execution.
Args:
threads (int): the thread limit, set to -1 to use maximum available
Note that using parallel shot evaluation disables parallel circuit
evaluation.
"""
self.simulator.set_max_threads_shot(int(threads))
def set_max_threads_circuit(self, threads):
"""
Set the maximum threads used for parallel circuit execution.
Args:
threads (int): the thread limit, set to -1 to use maximum available
Note that using parallel circuit evaluation disables parallel shot
evaluation.
"""
self.simulator.set_max_threads_circuit(int(threads))
def set_max_threads_state(self, threads):
"""
Set the maximum threads used for state update parallel routines.
Args:
threads (int): the thread limit, set to -1 to use maximum available.
Note that using parallel circuit or shot execution takes precidence over
parallel state evaluation.
"""
self.simulator.set_max_threads_state(int(threads))
def _validate(self, qobj):
# TODO
return

544
src/bindings/python/example.ipynb Executable file
View File

@ -0,0 +1,544 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cython C++ QASM Simulator\n",
"\n",
"Author: Christopher J. Wood (cjwood@us.ibm.com)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Building the simulator\n",
"\n",
"The simulator backend can be compiled by running the following code cell"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using the simulator as a QISKit engine\n",
"\n",
"We now import the cython simulator and use it as a qiskit backend"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# Import QISKIT\n",
"import numpy as np\n",
"import qiskit\n",
"from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister\n",
"\n",
"# Import Cython QASM Simulator Backend\n",
"from aer_simulator import AerSimulator\n",
"\n",
"# Backends\n",
"aer = AerSimulator()\n",
"aer.set_max_threads_shot(-1)"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Create a test circuit\n",
"num_qubits = 10\n",
"num_gates = 100\n",
"qr = QuantumRegister(num_qubits)\n",
"cr = ClassicalRegister(num_qubits)\n",
"circ = QuantumCircuit(qr, cr)\n",
"for j in range(num_gates):\n",
" q = np.random.randint(0, num_qubits)\n",
" r = np.random.randint(0, 4)\n",
" if r == 0: \n",
" circ.cx(qr[q], qr[(q + 1) % num_qubits])\n",
" elif r == 1:\n",
" circ.h(qr[q])\n",
" elif r == 2:\n",
" circ.s(qr[q])\n",
" elif r == 3:\n",
" circ.t(qr[q])\n",
"for j in range(num_qubits):\n",
" circ.measure(qr[j], cr[j])"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'backend_name': 'qiskit_aer_simulator',\n",
" 'backend_version': 'alpha 0.1',\n",
" 'date': 'TODO',\n",
" 'id': '284835d8-8617-4ba3-9192-785ad484358a',\n",
" 'metadata': {'omp_available_threads': 4,\n",
" 'omp_circuit_threads': 1,\n",
" 'omp_enabled': True,\n",
" 'time_taken': 0.17163972600000002},\n",
" 'qobj_id': 'TODO',\n",
" 'result': [{'data': {'counts': {'0x100': 2,\n",
" '0x102': 8,\n",
" '0x103': 2,\n",
" '0x10c': 3,\n",
" '0x10d': 3,\n",
" '0x10f': 3,\n",
" '0x114': 7,\n",
" '0x115': 4,\n",
" '0x116': 7,\n",
" '0x117': 5,\n",
" '0x118': 4,\n",
" '0x119': 4,\n",
" '0x11a': 9,\n",
" '0x11b': 4,\n",
" '0x120': 4,\n",
" '0x121': 5,\n",
" '0x122': 3,\n",
" '0x123': 10,\n",
" '0x12c': 4,\n",
" '0x12d': 8,\n",
" '0x12e': 5,\n",
" '0x12f': 8,\n",
" '0x134': 2,\n",
" '0x135': 1,\n",
" '0x136': 3,\n",
" '0x138': 3,\n",
" '0x139': 4,\n",
" '0x13a': 2,\n",
" '0x13b': 2,\n",
" '0x14': 1,\n",
" '0x140': 8,\n",
" '0x141': 6,\n",
" '0x142': 4,\n",
" '0x143': 3,\n",
" '0x14c': 4,\n",
" '0x14d': 5,\n",
" '0x14e': 8,\n",
" '0x14f': 3,\n",
" '0x154': 5,\n",
" '0x155': 2,\n",
" '0x156': 2,\n",
" '0x157': 1,\n",
" '0x15a': 2,\n",
" '0x16': 1,\n",
" '0x160': 3,\n",
" '0x161': 3,\n",
" '0x162': 1,\n",
" '0x163': 1,\n",
" '0x16d': 3,\n",
" '0x16f': 3,\n",
" '0x174': 4,\n",
" '0x175': 4,\n",
" '0x176': 7,\n",
" '0x177': 3,\n",
" '0x178': 8,\n",
" '0x179': 4,\n",
" '0x17a': 4,\n",
" '0x17b': 7,\n",
" '0x180': 3,\n",
" '0x181': 2,\n",
" '0x182': 8,\n",
" '0x183': 7,\n",
" '0x18c': 4,\n",
" '0x18d': 5,\n",
" '0x18e': 4,\n",
" '0x18f': 5,\n",
" '0x19': 2,\n",
" '0x194': 1,\n",
" '0x195': 1,\n",
" '0x196': 2,\n",
" '0x197': 3,\n",
" '0x198': 2,\n",
" '0x199': 2,\n",
" '0x19a': 3,\n",
" '0x19b': 2,\n",
" '0x1a': 1,\n",
" '0x1a0': 2,\n",
" '0x1a1': 1,\n",
" '0x1a2': 1,\n",
" '0x1a3': 3,\n",
" '0x1ac': 3,\n",
" '0x1ad': 2,\n",
" '0x1ae': 1,\n",
" '0x1af': 2,\n",
" '0x1b': 1,\n",
" '0x1b4': 10,\n",
" '0x1b5': 4,\n",
" '0x1b7': 3,\n",
" '0x1b8': 7,\n",
" '0x1b9': 4,\n",
" '0x1ba': 5,\n",
" '0x1bb': 3,\n",
" '0x1c0': 1,\n",
" '0x1c3': 1,\n",
" '0x1cc': 1,\n",
" '0x1cd': 1,\n",
" '0x1d4': 3,\n",
" '0x1d5': 3,\n",
" '0x1d6': 6,\n",
" '0x1d7': 6,\n",
" '0x1d8': 6,\n",
" '0x1d9': 2,\n",
" '0x1da': 2,\n",
" '0x1db': 5,\n",
" '0x1e0': 8,\n",
" '0x1e1': 8,\n",
" '0x1e2': 4,\n",
" '0x1e3': 1,\n",
" '0x1ec': 1,\n",
" '0x1ed': 2,\n",
" '0x1ee': 4,\n",
" '0x1ef': 4,\n",
" '0x1f4': 2,\n",
" '0x1f5': 1,\n",
" '0x1f6': 2,\n",
" '0x1f7': 1,\n",
" '0x1f8': 4,\n",
" '0x1fa': 3,\n",
" '0x1fb': 3,\n",
" '0x20': 1,\n",
" '0x20f': 1,\n",
" '0x21': 2,\n",
" '0x215': 1,\n",
" '0x217': 2,\n",
" '0x218': 1,\n",
" '0x21b': 1,\n",
" '0x22': 1,\n",
" '0x220': 2,\n",
" '0x222': 1,\n",
" '0x22c': 4,\n",
" '0x22d': 3,\n",
" '0x22e': 2,\n",
" '0x236': 1,\n",
" '0x23b': 2,\n",
" '0x240': 2,\n",
" '0x241': 2,\n",
" '0x242': 1,\n",
" '0x243': 1,\n",
" '0x24c': 1,\n",
" '0x24d': 4,\n",
" '0x24e': 1,\n",
" '0x254': 1,\n",
" '0x263': 1,\n",
" '0x26d': 2,\n",
" '0x26e': 1,\n",
" '0x274': 2,\n",
" '0x275': 1,\n",
" '0x277': 1,\n",
" '0x27b': 1,\n",
" '0x280': 1,\n",
" '0x281': 1,\n",
" '0x283': 1,\n",
" '0x28f': 1,\n",
" '0x298': 2,\n",
" '0x299': 1,\n",
" '0x2b4': 1,\n",
" '0x2b6': 1,\n",
" '0x2b7': 1,\n",
" '0x2b8': 1,\n",
" '0x2ba': 1,\n",
" '0x2bb': 1,\n",
" '0x2cc': 1,\n",
" '0x2ce': 1,\n",
" '0x2d': 1,\n",
" '0x2d5': 1,\n",
" '0x2d9': 1,\n",
" '0x2db': 2,\n",
" '0x2e0': 3,\n",
" '0x2e1': 1,\n",
" '0x2e2': 2,\n",
" '0x2ec': 2,\n",
" '0x2ee': 1,\n",
" '0x2ef': 2,\n",
" '0x2f9': 2,\n",
" '0x2fb': 1,\n",
" '0x301': 2,\n",
" '0x302': 3,\n",
" '0x303': 1,\n",
" '0x30c': 2,\n",
" '0x30d': 1,\n",
" '0x30e': 3,\n",
" '0x30f': 4,\n",
" '0x314': 8,\n",
" '0x315': 3,\n",
" '0x316': 2,\n",
" '0x317': 4,\n",
" '0x318': 3,\n",
" '0x319': 7,\n",
" '0x31a': 4,\n",
" '0x31b': 4,\n",
" '0x320': 7,\n",
" '0x321': 5,\n",
" '0x322': 2,\n",
" '0x323': 5,\n",
" '0x32c': 4,\n",
" '0x32d': 4,\n",
" '0x32e': 6,\n",
" '0x32f': 6,\n",
" '0x334': 1,\n",
" '0x335': 2,\n",
" '0x336': 3,\n",
" '0x338': 3,\n",
" '0x339': 1,\n",
" '0x33a': 1,\n",
" '0x33b': 2,\n",
" '0x340': 4,\n",
" '0x341': 7,\n",
" '0x342': 2,\n",
" '0x343': 7,\n",
" '0x34c': 6,\n",
" '0x34d': 2,\n",
" '0x34e': 6,\n",
" '0x34f': 3,\n",
" '0x354': 3,\n",
" '0x356': 1,\n",
" '0x358': 4,\n",
" '0x359': 2,\n",
" '0x35a': 4,\n",
" '0x35b': 1,\n",
" '0x360': 1,\n",
" '0x361': 2,\n",
" '0x362': 1,\n",
" '0x363': 1,\n",
" '0x36c': 3,\n",
" '0x36d': 1,\n",
" '0x36e': 1,\n",
" '0x374': 5,\n",
" '0x375': 10,\n",
" '0x376': 5,\n",
" '0x377': 10,\n",
" '0x378': 4,\n",
" '0x379': 3,\n",
" '0x37a': 3,\n",
" '0x37b': 2,\n",
" '0x380': 5,\n",
" '0x381': 5,\n",
" '0x382': 4,\n",
" '0x383': 4,\n",
" '0x38c': 6,\n",
" '0x38d': 10,\n",
" '0x38e': 7,\n",
" '0x38f': 9,\n",
" '0x394': 1,\n",
" '0x395': 2,\n",
" '0x396': 2,\n",
" '0x397': 1,\n",
" '0x398': 1,\n",
" '0x399': 1,\n",
" '0x39a': 2,\n",
" '0x39b': 1,\n",
" '0x3a0': 4,\n",
" '0x3a1': 2,\n",
" '0x3a2': 1,\n",
" '0x3a3': 2,\n",
" '0x3ac': 2,\n",
" '0x3ad': 2,\n",
" '0x3af': 1,\n",
" '0x3b4': 5,\n",
" '0x3b5': 8,\n",
" '0x3b6': 4,\n",
" '0x3b7': 4,\n",
" '0x3b8': 6,\n",
" '0x3b9': 5,\n",
" '0x3ba': 7,\n",
" '0x3bb': 6,\n",
" '0x3c0': 2,\n",
" '0x3c1': 2,\n",
" '0x3cd': 1,\n",
" '0x3ce': 1,\n",
" '0x3cf': 1,\n",
" '0x3d4': 8,\n",
" '0x3d5': 7,\n",
" '0x3d6': 1,\n",
" '0x3d7': 6,\n",
" '0x3d8': 4,\n",
" '0x3d9': 3,\n",
" '0x3da': 9,\n",
" '0x3db': 8,\n",
" '0x3e1': 6,\n",
" '0x3e2': 5,\n",
" '0x3e3': 9,\n",
" '0x3ec': 6,\n",
" '0x3ed': 4,\n",
" '0x3ee': 5,\n",
" '0x3ef': 6,\n",
" '0x3f5': 1,\n",
" '0x3f6': 1,\n",
" '0x3f7': 3,\n",
" '0x3f8': 2,\n",
" '0x3f9': 4,\n",
" '0x3fa': 2,\n",
" '0x3fb': 3,\n",
" '0x41': 1,\n",
" '0x43': 1,\n",
" '0x4f': 3,\n",
" '0x56': 1,\n",
" '0x59': 1,\n",
" '0x76': 3,\n",
" '0x78': 1,\n",
" '0x80': 1,\n",
" '0x81': 1,\n",
" '0x82': 1,\n",
" '0x83': 1,\n",
" '0x8c': 1,\n",
" '0x8e': 1,\n",
" '0x8f': 1,\n",
" '0x94': 1,\n",
" '0xad': 1,\n",
" '0xb4': 2,\n",
" '0xb6': 1,\n",
" '0xb7': 2,\n",
" '0xb8': 3,\n",
" '0xb9': 1,\n",
" '0xba': 2,\n",
" '0xbb': 1,\n",
" '0xc2': 1,\n",
" '0xc3': 1,\n",
" '0xd5': 1,\n",
" '0xd6': 1,\n",
" '0xd8': 1,\n",
" '0xd9': 2,\n",
" '0xe1': 1,\n",
" '0xe2': 1,\n",
" '0xe3': 2,\n",
" '0xed': 1,\n",
" '0xee': 2,\n",
" '0xef': 1,\n",
" '0xf5': 2}},\n",
" 'header': {'name': 'circuit1'},\n",
" 'metadata': {'omp_shot_threads': 4,\n",
" 'omp_state_threads': 1,\n",
" 'seed': 1925572646,\n",
" 'shots': 1000,\n",
" 'time_taken': 0.17120264200000002},\n",
" 'status': 'DONE',\n",
" 'success': True}],\n",
" 'status': 'COMPLETED',\n",
" 'success': True}"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"result = qiskit.execute(circ, aer, shots=1000).result()\n",
"result"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"noise_model = {\n",
" \"errors\": [\n",
" {\n",
" \"type\": \"kraus\",\n",
" \"operations\": [\"h\"],\n",
" \"probabilities\": [1],\n",
" \"matrices\": [np.array([[1, 0], [0, 0.5]]),\n",
" np.array([[0, 0.86602540378], [0, 0]])]\n",
" }\n",
" ]\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"aer.load_noise_model(noise_model)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'backend_name': 'qiskit_aer_simulator',\n",
" 'backend_version': 'alpha 0.1',\n",
" 'date': 'TODO',\n",
" 'id': '2fad1b6a-7031-49b8-a486-876240caf385',\n",
" 'metadata': {'omp_available_threads': 4,\n",
" 'omp_circuit_threads': 1,\n",
" 'omp_enabled': True,\n",
" 'time_taken': 0.34858704900000004},\n",
" 'qobj_id': 'TODO',\n",
" 'result': [{'data': {'counts': {'0x1ff': 364,\n",
" '0x2ff': 8,\n",
" '0x3ff': 620,\n",
" '0xbf': 1,\n",
" '0xff': 31}},\n",
" 'header': {'name': 'circuit1'},\n",
" 'metadata': {'omp_shot_threads': 4,\n",
" 'omp_state_threads': 1,\n",
" 'seed': 3182171174,\n",
" 'shots': 1024,\n",
" 'time_taken': 0.34713111900000004},\n",
" 'status': 'DONE',\n",
" 'success': True}],\n",
" 'status': 'COMPLETED',\n",
" 'success': True}"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"result = qiskit.execute(circ, aer).result()\n",
"result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python (qiskit)",
"language": "python",
"name": "qiskit"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

50
src/bindings/python/helpers.py Executable file
View File

@ -0,0 +1,50 @@
import json
import numpy as np
class SimulatorJSONEncoder(json.JSONEncoder):
"""
JSON encoder for NumPy arrays and complex numbers.
This functions as the standard JSON Encoder but adds support
for encoding:
complex numbers z as lists [z.real, z.imag]
ndarrays as nested lists.
"""
# pylint: disable=method-hidden,arguments-differ
def default(self, obj):
if isinstance(obj, np.ndarray):
return obj.tolist()
if isinstance(obj, complex):
return [obj.real, obj.imag]
return json.JSONEncoder.default(self, obj)
def qobj2schema(qobj):
"""
Convert current Qiskit qobj in line with schema spec qobj for simulator testing.
"""
shots = qobj.get("config",{}).get("shots", 1)
qobj["type"] = "QASM"
if "circuits" in qobj:
qobj["experiments"] = qobj.pop("circuits")
if "experiments" in qobj:
for i, experiment in enumerate(qobj["experiments"]):
# adjust labels
if "compiled_circuit" in experiment:
experiment["compiled_circuit"].pop("header", None)
experiment["instructions"] = experiment.pop("compiled_circuit", {}).pop("operations", [])
for k, op in enumerate(experiment["instructions"]):
if op.get("name") == "measure":
op["memory"] = op.pop("clbits")
op["register"] = op["memory"]
experiment["instructions"][k] = op
experiment["config"] = {"shots": shots}
# clear compiled qasm
experiment.pop("compiled_circuit_qasm", '')
# clear old header
if "name" in experiment:
experiment["header"] = {"name": experiment.pop("name")}
qobj["experiments"][i] = experiment
return qobj

View File

@ -0,0 +1,106 @@
"""
Cython interface to C++ quantum circuit simulator.
"""
# Import C++ Classes
from libcpp.string cimport string
# Import C++ simulator Interface class
cdef extern from "framework/interface.hpp" namespace "AER":
cdef cppclass Interface:
Interface() except+
string execute[STATE, STATECLASS](string &qobj) except +
void load_noise_model(string &qobj) except +
void load_engine_config(string &qobj) except +
void load_state_config(string &qobj) except +
void clear_noise_model()
void clear_engine_config()
void clear_state_config()
void set_max_threads(int threads)
void set_max_threads_circuit(int threads)
void set_max_threads_shot(int threads)
void set_max_threads_state(int threads)
int get_max_threads()
int get_max_threads_circuit()
int get_max_threads_shot()
int get_max_threads_state()
# QubitVector State class
cdef extern from "simulators/qubitvector/qubitvector.hpp" namespace "QV":
cdef cppclass QubitVector:
State() except +
# QubitVector State class
cdef extern from "simulators/qubitvector/qv_state.hpp" namespace "AER::QubitVector":
cdef cppclass State:
State() except +
cdef class AerSimulatorWrapper:
cdef Interface *thisptr
def __cinit__(self):
self.thisptr = new Interface()
def __dealloc__(self):
del self.thisptr
def execute(self, qobj):
# Convert input to C++ string
cdef string qobj_enc = str(qobj).encode('UTF-8')
# Execute
return self.thisptr.execute[QubitVector, State](qobj_enc)
def load_noise_model(self, config):
# Convert input to C++ string
cdef string config_enc = str(config).encode('UTF-8')
self.thisptr.load_noise_model(config_enc)
def load_state_config(self, config):
# Convert input to C++ string
cdef string config_enc = str(config).encode('UTF-8')
self.thisptr.load_state_config(config_enc)
def load_engine_config(self, config):
# Convert input to C++ string
cdef string config_enc = str(config).encode('UTF-8')
self.thisptr.load_engine_config(config_enc)
def clear_noise_model(self):
self.thisptr.clear_noise_model()
def clear_state_config(self):
self.thisptr.clear_state_config()
def clear_engine_config(self):
self.thisptr.clear_engine_config()
def set_max_threads(self, threads):
self.thisptr.set_max_threads(int(threads))
def set_max_threads_circuit(self, threads):
self.thisptr.set_max_threads_circuit(int(threads))
def set_max_threads_shot(self, threads):
self.thisptr.set_max_threads_shot(int(threads))
def set_max_threads_state(self, threads):
self.thisptr.set_max_threads_state(int(threads))
def get_max_threads(self):
return self.thisptr.get_max_threads()
def get_max_threads_circuit(self):
return self.thisptr.get_max_threads_circuit()
def get_max_threads_shot(self):
return self.thisptr.get_max_threads_shot()
def get_max_threads_state(self):
return self.thisptr.get_max_threads_state()