diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index a8014593c2..8734413ddc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,15 +8,13 @@ # Generic rule for the repository. This pattern is actually the one that will # apply unless specialized by a later rule -* @chriseclectic @atilag +* @chriseclectic @vvilpas # Individual folders on root directory -/qiskit @chriseclectic @atilag @vvilpas -/cmake @atilag @vvilpas -/doc @chriseclectic @atilag @vvilpas -/examples @chriseclectic @atilag @vvilpas -/contrib @chriseclectic @atilag @vvilpas -/test @chriseclectic @atilag @vvilpas -/src @chriseclectic @atilag @vvilpas - -# AER specific folders +/qiskit @chriseclectic @vvilpas @mtreinish +/test @chriseclectic @vvilpas @mtreinish +/doc @chriseclectic @vvilpas @mtreinish +/releasenotes @chriseclectic @vvilpas @mtreinish +/cmake @vvilpas +/contrib @chriseclectic @vvilpas @hhorii +/src @chriseclectic @vvilpas @hhorii diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..f684e925a8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,87 @@ +name: Build +on: + push: + branches: [master, 'stable/*'] + pull_request: + branches: [master, 'stable/*'] +jobs: + standalone: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: ["macOS-latest", "ubuntu-latest", "windows-latest"] + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.7 + uses: actions/setup-python@v2 + with: + python-version: 3.7 + - name: Install deps + run: pip install "conan>=1.31.2" + - 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@v1.0.2 + 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 + wheel: + runs-on: ${{ matrix.os }} + needs: ["standalone"] + strategy: + matrix: + os: ["macOS-latest", "ubuntu-latest", "windows-latest"] + steps: + - uses: actions/checkout@v2 + - name: Set up Python Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + if: runner.os == 'Windows' + - name: Install deps + run: python -m pip install -U cibuildwheel==1.9.0 + - name: Build Wheels + env: + CIBW_BEFORE_ALL_LINUX: "yum install -y openblas-devel" + CIBW_SKIP: "cp27-* cp34-* cp35-* pp*" + CIBW_TEST_COMMAND: "python {project}/tools/verify_wheels.py" + CIBW_TEST_REQUIRES: "git+https://github.com/Qiskit/qiskit-terra.git" + CIBW_ENVIRONMENT_WINDOWS: "CMAKE_GENERATOR='Visual Studio 16 2019'" + run: cibuildwheel --output-dir wheelhouse + - uses: actions/upload-artifact@v2 + with: + path: ./wheelhouse/*.whl diff --git a/.github/workflows/wheels.yml b/.github/workflows/deploy.yml similarity index 63% rename from .github/workflows/wheels.yml rename to .github/workflows/deploy.yml index e08179f5e2..f89063daf1 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/deploy.yml @@ -1,4 +1,4 @@ -name: Build +name: Deploy on: push: tags: @@ -8,7 +8,7 @@ jobs: name: Build qiskit-aer wheels strategy: matrix: - os: ["macOS-latest", "ubuntu-latest"] + os: ["macOS-latest", "ubuntu-latest", "windows-latest"] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -18,16 +18,14 @@ jobs: python-version: '3.7' - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==1.5.5 + python -m pip install cibuildwheel==1.9.0 - name: Build wheels env: CIBW_BEFORE_ALL_LINUX: "yum install -y openblas-devel" - CIBW_BEFORE_BUILD: "pip install -U Cython pip virtualenv pybind11" CIBW_SKIP: "cp27-* cp34-* cp35-* pp*" - CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2010" - CIBW_MANYLINUX_I686_IMAGE: "manylinux2010" - CIBW_TEST_COMMAND: "python3 {project}/tools/verify_wheels.py" + CIBW_TEST_COMMAND: "python {project}/tools/verify_wheels.py" CIBW_TEST_REQUIRES: "git+https://github.com/Qiskit/qiskit-terra.git" + CIBW_ENVIRONMENT_WINDOWS: "CMAKE_GENERATOR='Visual Studio 16 2019'" run: | python -m cibuildwheel --output-dir wheelhouse - uses: actions/upload-artifact@v2 @@ -40,6 +38,29 @@ jobs: run : | pip install -U twine twine upload wheelhouse/* + sdist: + name: Publish qiskit-aer sdist + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + name: Install Python + with: + python-version: '3.8' + - name: Install Deps + run: pip install -U twine wheel + - name: Build Artifacts + run: | + python setup.py sdist + shell: bash + - uses: actions/upload-artifact@v2 + with: + path: ./dist/qiskit* + - name: Publish to PyPi + env: + TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} + TWINE_USERNAME: qiskit + run: twine upload dist/qiskit* gpu-build: name: Build qiskit-aer-gpu wheels runs-on: ubuntu-latest @@ -49,18 +70,18 @@ jobs: name: Install Python with: python-version: '3.7' + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + if: runner.os == 'Windows' - name: Install cibuildwheel run: | - python -m pip install cibuildwheel==1.5.5 + python -m pip install cibuildwheel==1.9.0 - name: Build wheels env: - CIBW_BEFORE_ALL: "yum install -y yum-utils wget && wget https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda-repo-rhel6-10-1-local-10.1.243-418.87.00-1.0-1.x86_64.rpm && rpm -i cuda-repo-rhel6-10-1-local-10.1.243-418.87.00-1.0-1.x86_64.rpm && yum clean all && yum -y install cuda-10-1" - CIBW_BEFORE_BUILD: "pip install -U Cython pip virtualenv pybind11 && yum install -y openblas-devel" + CIBW_BEFORE_ALL: "yum install -y yum-utils wget && wget https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda-repo-rhel6-10-1-local-10.1.243-418.87.00-1.0-1.x86_64.rpm && rpm -i cuda-repo-rhel6-10-1-local-10.1.243-418.87.00-1.0-1.x86_64.rpm && yum clean all && yum -y install cuda-10-1 openblas-devel" CIBW_SKIP: "cp27-* cp34-* cp35-* *-manylinux_i686 pp*" - CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2010" - CIBW_MANYLINUX_I686_IMAGE: "manylinux2010" - CIBW_ENVIRONMENT: QISKIT_AER_PACKAGE_NAME=qiskit-aer-gpu AER_THRUST_BACKEND=CUDA - CIBW_TEST_COMMAND: "python3 {project}/tools/verify_wheels.py" + CIBW_ENVIRONMENT: QISKIT_AER_PACKAGE_NAME=qiskit-aer-gpu AER_THRUST_BACKEND=CUDA CUDACXX=/usr/local/cuda/bin/nvcc + CIBW_TEST_COMMAND: "python {project}/tools/verify_wheels.py" CIBW_TEST_REQUIRES: "git+https://github.com/Qiskit/qiskit-terra.git" run: | python -m cibuildwheel --output-dir wheelhouse diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000000..628ce52135 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,88 @@ +name: Docs and Tutorial +on: + push: + branches: [master, 'stable/*'] + pull_request: + branches: [master, 'stable/*'] +jobs: + docs: + runs-on: ubuntu-latest + timeout-minutes: 60 + strategy: + matrix: + python-version: [3.7] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-docs-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-docs- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install Deps + run: | + set -e + pip install -U pip virtualenv wheel + pip install -U tox + sudo apt-get update + sudo apt-get install -y build-essential libopenblas-dev + shell: bash + - name: Run Docs Build + run: tox -edocs + - uses: actions/upload-artifact@v2 + with: + name: html_docs + path: docs/_build/html + tutorials: + runs-on: ubuntu-latest + needs: [docs] + strategy: + matrix: + python-version: [3.7] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~\AppData\Local\pip\Cache + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-tutorials-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-tutorials- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}}- + - name: Setup tutorials job + run: | + set -e + git clone https://github.com/Qiskit/qiskit-tutorials --depth=1 + python -m pip install --upgrade pip wheel + pip install -U -r requirements-dev.txt -c constraints.txt + pip install -c constraints.txt git+https://github.com/Qiskit/qiskit-terra + pip install -c constraints.txt . + pip install -U "qiskit-ibmq-provider" "z3-solver" "qiskit-ignis" "qiskit-aqua" "pyscf<1.7.4" "matplotlib<3.3.0" jupyter pylatexenc sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt + python setup.py build_ext --inplace + sudo apt install -y graphviz pandoc libopenblas-dev + pip check + shell: bash + - name: Run Tutorials + run: | + set -e + cd qiskit-tutorials + rm -rf tutorials/chemistry tutorials/circuits tutorials/circuits_advanced tutorials/finance tutorials/optimization tutorials/algorithms tutorials/operators tutorials/noise tutorials/machine_learning + sphinx-build -b html . _build/html + - uses: actions/upload-artifact@v2 + with: + name: tutorials_html + path: qiskit-tutorials/_build/html diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 1aba0493d6..0000000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,244 +0,0 @@ -name: Compile tests -on: - push: - branches: [master, 'stable/*'] - pull_request: - branches: [master, 'stable/*'] -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install deps - run: | - set -e - pip install -U pip - pip install -U -c constraints.txt git+https://github.com/Qiskit/qiskit-terra - pip install -U -c constraints.txt -r requirements-dev.txt - shell: bash - - name: Run Lint - run: | - set -e - pycodestyle --ignore=E402,W504 --max-line-length=100 qiskit/providers/aer - pylint -j 2 -rn qiskit/providers/aer - docs: - runs-on: ubuntu-latest - timeout-minutes: 60 - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set up Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - name: Install Deps - run: | - set -e - pip install -U pip virtualenv - pip install -U tox - sudo apt-get update - sudo apt-get install -y build-essential libopenblas-dev - shell: bash - - name: Run Docs Build - run: tox -edocs - - uses: actions/upload-artifact@v2 - with: - name: html_docs - path: docs/_build/html - standalone: - name: compile-standalone-${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: ["macOS-latest", "ubuntu-latest", "windows-latest"] - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - name: Install deps - run: pip install conan - - 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@v1.0.1 - if: runner.os == 'Windows' - - name: Compile Standalone Windows - run: | - set -e - mkdir out; cd out; cmake .. - cmake --build . --config Release - shell: bash - if: runner.os == 'Windows' - - name: Compile Standalone - run: | - set -e - mkdir out; cd out; cmake .. - make - shell: bash - if: runner.os != 'Windows' - sdist: - name: compile-sdist-python${{ matrix.python-version }}-${{ matrix.platform.os }} - runs-on: ${{ matrix.platform.os }} - strategy: - matrix: - python-version: [3.6, 3.7, 3.8] - platform: [ - { os: "macOS-latest", python-architecture: "x64"}, - { os: "ubuntu-latest", python-architecture: "x64" }, - ] - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Install Deps - run: python -m pip install -U setuptools wheel virtualenv - - name: Install openblas - run: | - set -e - sudo apt-get update - sudo apt-get install -y libopenblas-dev - shell: bash - if: runner.os == 'Linux' - - name: Build Sdist - run: python setup.py sdist - - name: Install from sdist and test - run: | - set -e - mkdir out; cd out; virtualenv aer-test - aer-test/bin/pip install ../dist/*tar.gz - aer-test/bin/pip install -c ../constraints.txt git+https://github.com/Qiskit/qiskit-terra - aer-test/bin/python ../tools/verify_wheels.py - aer-test/bin/pip check - shell: bash - wheel: - name: compile-wheel-${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: ["macOS-latest", "ubuntu-latest"] - steps: - - uses: actions/checkout@v2 - - name: Set up Python Python 3.8 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - name: Install deps - run: python -m pip install -U cibuildwheel==1.5.5 - - name: Build Wheels - env: - CIBW_BEFORE_ALL_LINUX: "yum install -y openblas-devel" - CIBW_BEFORE_BUILD: "pip install -U Cython virtualenv pybind11" - CIBW_SKIP: "cp27-* cp34-* cp35-* pp*" - CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2010" - CIBW_MANYLINUX_I686_IMAGE: "manylinux2010" - CIBW_TEST_COMMAND: "python3 {project}/tools/verify_wheels.py" - CIBW_TEST_REQUIRES: "git+https://github.com/Qiskit/qiskit-terra.git" - run: cibuildwheel --output-dir wheelhouse - - uses: actions/upload-artifact@v2 - with: - path: ./wheelhouse/*.whl - tests: - name: tests-python${{ matrix.python-version }}-${{ matrix.os }} - runs-on: ${{ matrix.os }} - needs: [standalone, wheel, sdist, lint, docs] - strategy: - matrix: - python-version: [3.6, 3.7, 3.8] - os: ["macOS-latest", "ubuntu-latest", "windows-latest"] - env: - AER_THRUST_BACKEND: OMP - QISKIT_TEST_CAPTURE_STREAMS: 1 - steps: - - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - name: Add msbuild to PATH - uses: microsoft/setup-msbuild@v1.0.1 - if: runner.os == 'Windows' - - name: Install Deps - run: python -m pip install -U -r requirements-dev.txt git+https://github.com/Qiskit/qiskit-terra - - name: Install openblas - run: | - set -e - sudo apt-get update - sudo apt-get install -y libopenblas-dev - shell: bash - if: runner.os == 'Linux' - - name: Install Aer - run: python -m pip install -U . - if: runner.os != 'Windows' - - name: Install Aer Windows - run: | - set -e - python setup.py bdist_wheel -- -G 'Visual Studio 16 2019' - pip install --find-links=dist qiskit-aer - shell: bash - if: runner.os == 'Windows' - - name: Run Tests - env: - QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y - run: | - set -e - pip check - stestr run --slowest - shell: bash - if: runner.os != 'macOS' || matrix.python-version != '3.8' - - name: Run Tests macOS 3.8 - env: - QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y - QISKIT_IN_PARALLEL: TRUE - run: | - set -e - pip check - stestr run --slowest - shell: bash - if: runner.os == 'macOS' && matrix.python-version == '3.8' - tutorials: - name: Tutorials - runs-on: ubuntu-latest - needs: [standalone, wheel, sdist, lint, docs] - steps: - - uses: actions/checkout@v2 - - name: Set up Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: 3.7 - - name: Setup tutorials job - run: | - set -e - git clone https://github.com/Qiskit/qiskit-tutorials --depth=1 - python -m pip install --upgrade pip - pip install -U -r requirements-dev.txt -c constraints.txt - pip install -c constraints.txt git+https://github.com/Qiskit/qiskit-terra - pip install -c constraints.txt . - pip install -U "qiskit-ibmq-provider" "z3-solver" "qiskit-ignis" "qiskit-aqua" "pyscf<1.7.4" "matplotlib<3.3.0" jupyter pylatexenc sphinx nbsphinx sphinx_rtd_theme cvxpy -c constraints.txt - python setup.py build_ext --inplace - sudo apt install -y graphviz pandoc libopenblas-dev - pip check - shell: bash - - name: Run Tutorials - run: | - set -e - cd qiskit-tutorials - rm -rf tutorials/chemistry tutorials/circuits tutorials/circuits_advanced tutorials/finance tutorials/optimization tutorials/noise - sphinx-build -b html . _build/html - - uses: actions/upload-artifact@v2 - with: - name: tutorials_html - path: qiskit-tutorials/_build/html diff --git a/.github/workflows/tests_linux.yml b/.github/workflows/tests_linux.yml new file mode 100644 index 0000000000..6a67fb9f3f --- /dev/null +++ b/.github/workflows/tests_linux.yml @@ -0,0 +1,126 @@ +name: Tests Linux +on: + push: + branches: [master, 'stable/*'] + pull_request: + branches: [master, 'stable/*'] +jobs: + lint: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-lint-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-lint- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install deps + run: | + set -e + pip install -U pip wheel + pip install -U -c constraints.txt git+https://github.com/Qiskit/qiskit-terra + pip install -U -c constraints.txt -r requirements-dev.txt + shell: bash + - name: Run Lint + run: | + set -e + pycodestyle --ignore=E402,W504 --max-line-length=100 qiskit/providers/aer + pylint -j 2 -rn qiskit/providers/aer + sdist: + runs-on: ${{ matrix.platform.os }} + needs: ["lint"] + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + platform: [ + { os: "ubuntu-latest", python-architecture: "x64" }, + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install Deps + run: python -m pip install -U setuptools wheel virtualenv + - name: Install openblas + run: | + set -e + sudo apt-get update + sudo apt-get install -y libopenblas-dev + shell: bash + - name: Build Sdist + run: python setup.py sdist + - name: Install from sdist and test + run: | + set -e + mkdir out; cd out; virtualenv aer-test + aer-test/bin/pip install ../dist/*tar.gz + aer-test/bin/pip install -c ../constraints.txt git+https://github.com/Qiskit/qiskit-terra + aer-test/bin/python ../tools/verify_wheels.py + aer-test/bin/pip check + shell: bash + tests: + runs-on: ${{ matrix.os }} + needs: [sdist, lint] + timeout-minutes: 30 + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + os: ["ubuntu-latest"] + env: + AER_THRUST_BACKEND: OMP + QISKIT_TEST_CAPTURE_STREAMS: 1 + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-test-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-test- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install Deps + run: python -m pip install -U -c constraints.txt -r requirements-dev.txt wheel git+https://github.com/Qiskit/qiskit-terra + - name: Install openblas + run: | + set -e + sudo apt-get update + sudo apt-get install -y libopenblas-dev + shell: bash + - name: Install Aer + run: python -m pip install -U . + - name: Run Tests + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + run: | + set -e + pip check + stestr run --slowest + shell: bash diff --git a/.github/workflows/tests_mac.yml b/.github/workflows/tests_mac.yml new file mode 100644 index 0000000000..c0fd673bff --- /dev/null +++ b/.github/workflows/tests_mac.yml @@ -0,0 +1,128 @@ +name: Tests MacOS +on: + push: + branches: [master, 'stable/*'] + pull_request: + branches: [master, 'stable/*'] +jobs: + lint: + runs-on: macOS-latest + strategy: + matrix: + python-version: [3.8] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-lint-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-lint- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install deps + run: | + set -e + pip install -U pip wheel + pip install -U -c constraints.txt git+https://github.com/Qiskit/qiskit-terra + pip install -U -c constraints.txt -r requirements-dev.txt + shell: bash + - name: Run Lint + run: | + set -e + pycodestyle --ignore=E402,W504 --max-line-length=100 qiskit/providers/aer + pylint -j 2 -rn qiskit/providers/aer + sdist: + runs-on: ${{ matrix.platform.os }} + needs: ["lint"] + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + platform: [ + { os: "macOS-latest", python-architecture: "x64"}, + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install Deps + run: python -m pip install -U setuptools wheel virtualenv + - name: Build Sdist + run: python setup.py sdist + - name: Install from sdist and test + run: | + set -e + mkdir out; cd out; virtualenv aer-test + aer-test/bin/pip install ../dist/*tar.gz + aer-test/bin/pip install -c ../constraints.txt git+https://github.com/Qiskit/qiskit-terra + aer-test/bin/python ../tools/verify_wheels.py + aer-test/bin/pip check + shell: bash + tests: + runs-on: ${{ matrix.os }} + needs: [sdist, lint] + timeout-minutes: 40 + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + os: ["macOS-latest"] + env: + AER_THRUST_BACKEND: OMP + QISKIT_TEST_CAPTURE_STREAMS: 1 + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-test-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-test- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install Deps + run: | + set -e + pip install -U -c constraints.txt wheel git+https://github.com/Qiskit/qiskit-terra + pip install -U -c constraints.txt -r requirements-dev.txt + - name: Install Aer + run: python -m pip install -U . + - name: Run Tests + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + run: | + set -e + pip check + stestr run --slowest + shell: bash + if: runner.os != 'macOS' || matrix.python-version != '3.8' + - name: Run Tests macOS 3.8 + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + QISKIT_IN_PARALLEL: TRUE + run: | + set -e + pip check + stestr run --slowest + shell: bash + if: runner.os == 'macOS' && matrix.python-version == '3.8' diff --git a/.github/workflows/tests_windows.yml b/.github/workflows/tests_windows.yml new file mode 100644 index 0000000000..79666244e5 --- /dev/null +++ b/.github/workflows/tests_windows.yml @@ -0,0 +1,126 @@ +name: Tests Windows +on: + push: + branches: [master, 'stable/*'] + pull_request: + branches: [master, 'stable/*'] +jobs: + lint: + runs-on: windows-latest + strategy: + matrix: + python-version: [3.8] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~\AppData\Local\pip\Cache + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-lint-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-lint- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Install deps + run: | + set -e + pip install -U -c constraints.txt git+https://github.com/Qiskit/qiskit-terra + pip install -U -c constraints.txt -r requirements-dev.txt + shell: bash + - name: Run Lint + run: | + set -e + pycodestyle --ignore=E402,W504 --max-line-length=100 qiskit/providers/aer + pylint -j 2 -rn qiskit/providers/aer + shell: bash + sdist: + runs-on: ${{ matrix.platform.os }} + needs: ["lint"] + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + platform: [ + { os: "windows-latest", python-architecture: "x64"}, + { os: "windows-latest", python-architecture: "x86"}, + ] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + architecture: ${{ matrix.python-architecture }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-sdist- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + - name: Install Deps + run: python -m pip install -U setuptools wheel virtualenv + - name: Build Sdist + run: python setup.py sdist + - name: Install from sdist and test + run: | + set -e + mkdir out; cd out; virtualenv aer-test + aer-test/Scripts/pip install --find-links ../dist qiskit-aer + aer-test/Scripts/pip install -c ../constraints.txt git+https://github.com/Qiskit/qiskit-terra + aer-test/Scripts/python ../tools/verify_wheels.py + aer-test/Scripts/pip check + shell: bash + env: + CMAKE_GENERATOR: 'Visual Studio 16 2019' + tests: + runs-on: ${{ matrix.os }} + needs: ["lint"] + timeout-minutes: 30 + strategy: + matrix: + python-version: [3.6, 3.7, 3.8, 3.9] + os: ["windows-latest"] + env: + AER_THRUST_BACKEND: OMP + QISKIT_TEST_CAPTURE_STREAMS: 1 + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Pip cache + uses: actions/cache@v2 + with: + path: ~\AppData\Local\pip\Cache + key: ${{ runner.os }}-${{ matrix.python-version}}-pip-test-${{ hashFiles('setup.py','requirements-dev.txt','constraints.txt') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.python-version}}-pip-test- + ${{ runner.os }}-${{ matrix.python-version}}-pip- + ${{ runner.os }}-${{ matrix.python-version}}- + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + - name: Install Deps + run: python -m pip install -U -c constraints.txt -r requirements-dev.txt wheel git+https://github.com/Qiskit/qiskit-terra + - name: Install Aer Windows + run: | + set -e + python setup.py bdist_wheel -- -G 'Visual Studio 16 2019' + pip install --find-links=dist qiskit-aer + shell: bash + - name: Run Tests + env: + QISKIT_SUPPRESS_PACKAGING_WARNINGS: Y + run: | + set -e + pip check + stestr run --slowest + shell: bash diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..3a19882184 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +--- +notifications: + email: false + +cache: + pip: true + +os: linux +dist: bionic +language: python +python: 3.7 +jobs: + include: + - name: Build aarch64 wheels + arch: arm64 + services: + - docker + install: + - echo "" + env: + - CIBW_BEFORE_ALL_LINUX="yum install -y https://dl.fedoraproject.org/pub/epel/7/aarch64/Packages/e/epel-release-7-12.noarch.rpm && yum install -y openblas-devel" + - CIBW_SKIP="cp27-* cp34-* cp35-* pp*" + - TWINE_USERNAME=qiskit + - CIBW_TEST_COMMAND="python {project}/tools/verify_wheels.py" + if: tag IS present + script: + - pip install -U twine importlib-metadata keyring cibuildwheel==1.9.0 + - cibuildwheel --output-dir wheelhouse + - twine upload wheelhouse/* diff --git a/CMakeLists.txt b/CMakeLists.txt index 60728b4690..e443ee0e3c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,8 +8,10 @@ # For Mac, statically linking only happens with user libraries, system libraries cannot # be linked statically per Apple's indications. -cmake_minimum_required(VERSION 3.6) +cmake_minimum_required(VERSION 3.8 FATAL_ERROR) file(STRINGS "qiskit/providers/aer/VERSION.txt" VERSION_NUM) + +include(CheckLanguage) project(qasm_simulator VERSION ${VERSION_NUM} LANGUAGES CXX C) list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) @@ -23,6 +25,17 @@ if(NOT DEFINED AER_THRUST_BACKEND AND DEFINED ENV{AER_THRUST_BACKEND}) set(AER_THRUST_BACKEND $ENV{AER_THRUST_BACKEND}) endif() +if(AER_THRUST_BACKEND STREQUAL "CUDA") + include(nvcc_add_compiler_options) + set(CUDA_FOUND TRUE) + #include(FindCUDA) # for cuda_select_nvcc_arch_flags, CUDA_FOUND + include(FindCUDA/select_compute_arch) + enable_language(CUDA) +else() + # idiosyncrasy of CMake that it still creates a reference to this + set(CMAKE_CUDA_COMPILE_WHOLE_COMPILATION "") +endif() + # Warning: Because of a bug on CMake's FindBLAS or (it's not clear who's fault is) # libopenblas.a for Ubuntu (maybe others) we need to copy the file: @@ -32,11 +45,17 @@ option(STATIC_LINKING "Specify if we want statically link the executable (for redistribution mainly)" FALSE) option(BUILD_TESTS "Specify whether we want to build tests or not" FALSE) +# Allow disabling conan for downstream package managers. Requires all libraries to be present in path +# Default is value of environment variable if defined or ON +if(NOT DEFINED DISABLE_CONAN AND DEFINED ENV{DISABLE_CONAN}) + set(DISABLE_CONAN $ENV{DISABLE_CONAN}) +endif() + include(CTest) include(compiler_utils) include(Linter) include(findBLASInSpecificPath) -include(conan_utils) +include(dependency_utils) # Get version information get_version(${VERSION_NUM}) @@ -124,29 +143,36 @@ endif() # # Looking for external libraries # - -setup_conan() +set(BACKEND_REDIST_DEPS "") # List of redistributable dependencies +setup_dependencies() # If we do not set them with a space CMake fails afterwards if nothing is set for this vars! set(AER_LINKER_FLAGS " ") set(AER_COMPILER_FLAGS " ") +if(MSVC) + set(AER_COMPILER_FLAGS " /bigobj") +endif () -message(STATUS "Looking for OpenMP support...") -if(APPLE) - set(OPENMP_FOUND TRUE) - if(NOT SKBUILD) - set(AER_LIBRARIES ${AER_LIBRARIES} CONAN_PKG::llvm-openmp) - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CONAN_CXX_FLAGS_LLVM-OPENMP}") - set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${CONAN_INCLUDE_DIRS_LLVM-OPENMP}) - endif() -else() +if(NOT OPENMP_FOUND) # Could already be setup for macos with conan + message(STATUS "Looking for OpenMP support...") find_package(OpenMP QUIET) if(OPENMP_FOUND) set(AER_COMPILER_FLAGS "${AER_COMPILER_FLAGS} ${OpenMP_CXX_FLAGS}") set(AER_LINKER_FLAGS "${AER_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + if(APPLE) + set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${OpenMP_CXX_INCLUDE_DIRS}) + # On Apple and clang, we do need to link against the library unless we are building + # the Terra Addon, see issue: https://github.com/Qiskit/qiskit-aer/issues/1 + if(NOT SKBUILD) + set(AER_LIBRARIES "${AER_LIBRARIES}" "${OpenMP_${OpenMP_CXX_LIB_NAMES}_LIBRARY}") + message(STATUS "Adding Clang: ${OpenMP_${OpenMP_CXX_LIB_NAMES}_LIBRARY}") + else() + get_filename_component(OPENMP_LIB_TO_COPY ${OpenMP_${OpenMP_CXX_LIB_NAMES}_LIBRARY} REALPATH) #Needed to follow symlinks + set(BACKEND_REDIST_DEPS ${BACKEND_REDIST_DEPS} ${OPENMP_LIB_TO_COPY}) + endif() + endif() message(STATUS "OpenMP found!") message(STATUS "OpenMP_CXX_FLAGS = ${OpenMP_CXX_FLAGS}") message(STATUS "OpenMP_EXE_LINKER_FLAGS = ${OpenMP_EXE_LINKER_FLAGS}") @@ -167,8 +193,8 @@ if(STATIC_LINKING) set(BLA_STATIC TRUE) endif() -if(BLAS_LIB_PATH) - find_BLAS_in_specific_path(${BLAS_LIB_PATH}) +if(AER_BLAS_LIB_PATH) + find_BLAS_in_specific_path(${AER_BLAS_LIB_PATH}) else() if(APPLE) message(STATUS "Looking for Apple BLAS & Lapack library...") @@ -184,7 +210,7 @@ else() set(WIN_ARCH "win32") endif() execute_process(COMMAND ${CMAKE_COMMAND} -E tar "xvfj" "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/${WIN_ARCH}/lib/openblas.7z" WORKING_DIRECTORY "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/${WIN_ARCH}/lib/") - set(OPENBLAS_DLLs "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/${WIN_ARCH}/lib/libopenblas.dll") + set(BACKEND_REDIST_DEPS ${BACKEND_REDIST_DEPS} "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/${WIN_ARCH}/lib/libopenblas.dll") set(BLAS_LIBRARIES "${AER_SIMULATOR_CPP_SRC_DIR}/third-party/${WIN_ARCH}/lib/libopenblas.dll.a") # Seems CMake is unable to find it on its own set(BLAS_FOUND True) else() @@ -209,6 +235,14 @@ if(NOT MSVC) endif() endif() +if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") + if(APPLE OR UNIX) + set(SIMD_FLAGS_LIST "-mfma;-mavx2") + elseif(MSVC) + set(SIMD_FLAGS_LIST "/arch:AVX2") + endif() +endif() + set(AER_THRUST_SUPPORTED TRUE) if(AER_THRUST_SUPPORTED) if(AER_THRUST_BACKEND STREQUAL "CUDA") @@ -223,38 +257,32 @@ if(AER_THRUST_SUPPORTED) endif() endif() cuda_select_nvcc_arch_flags(AER_CUDA_ARCH_FLAGS ${AER_CUDA_ARCH}) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} -ccbin \"${CMAKE_CXX_COMPILER}\" ${AER_CUDA_ARCH_FLAGS} -DAER_THRUST_CUDA -std=c++14 -I${AER_SIMULATOR_CPP_SRC_DIR} -isystem ${AER_SIMULATOR_CPP_SRC_DIR}/third-party/headers -use_fast_math --expt-extended-lambda") - # We have to set SIMD flags globally because there seems to be a bug in FindCUDA which doesn't allow us to set per-file compilation flags. - # The implications is that users downloading the PyPi wheel package for the GPU, need to have a CPU with AVX2 support otherwise - # Aer will crash with: "Unknow instruction" exception. - # This will be fixed here: https://github.com/Qiskit/qiskit-aer/ - if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") - if(APPLE OR UNIX) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS}; --compiler-options;-mfma,-mavx2") - elseif(MSVC) - set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} /arch:AVX2") - endif() - endif() + + string(REPLACE ";" " " AER_CUDA_ARCH_FLAGS_EXPAND "${AER_CUDA_ARCH_FLAGS}") + set(CUDA_NVCC_FLAGS "${CUDA_NVCC_FLAGS} ${AER_CUDA_ARCH_FLAGS_EXPAND} -DAER_THRUST_CUDA -I${AER_SIMULATOR_CPP_SRC_DIR} -isystem ${AER_SIMULATOR_CPP_SRC_DIR}/third-party/headers -use_fast_math --expt-extended-lambda") + set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_CUDA) - set(THRUST_DEPENDANT_LIBS "") + set(THRUST_DEPENDENT_LIBS "") elseif(AER_THRUST_BACKEND STREQUAL "TBB") message(STATUS "TBB Support found!") - set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${CONAN_INCLUDE_DIRS_THRUST}) - set(THRUST_DEPENDANT_LIBS CONAN_PKG::tbb) + set(THRUST_DEPENDENT_LIBS AER_DEPENDENCY_PKG::tbb) set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_THRUST_CPU=TRUE) elseif(AER_THRUST_BACKEND STREQUAL "OMP") message(STATUS "Thrust library: Setting OMP backend") if(NOT OPENMP_FOUND) message(FATAL_ERROR "There's no OMP support. We cannot set Thrust backend to OMP!!") endif() - set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${CONAN_INCLUDE_DIRS_THRUST}) set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_THRUST_CPU=TRUE) # We don't need to add OMP because it's already an AER dependency - set(THRUST_DEPENDANT_LIBS "") + set(THRUST_DEPENDENT_LIBS "") else() message(STATUS "No Thrust supported backend") set(AER_THRUST_SUPPORTED FALSE) endif() + + if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") + endif() endif() if(AER_THRUST_SUPPORTED) @@ -263,66 +291,91 @@ else() message(STATUS "No Thrust support enabled") endif() +if(AER_DEBUG) + set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_DEBUG) +endif() + +if(AER_MPI) + find_package(MPI REQUIRED) + set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_MPI) + set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${MPI_CXX_INCLUDE_PATH}) + set(MPI_DEPENDANT_LIBS ${MPI_CXX_LIBRARIES}) + if(AER_DISABLE_GDR) + set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} AER_DISABLE_GDR) + endif() +else() + set(MPI_DEPENDANT_LIBS "") +endif() # Set dependent libraries set(AER_LIBRARIES ${AER_LIBRARIES} ${BLAS_LIBRARIES} - CONAN_PKG::nlohmann_json + AER_DEPENDENCY_PKG::nlohmann_json + AER_DEPENDENCY_PKG::spdlog Threads::Threads - CONAN_PKG::spdlog ${DL_LIB} - ${THRUST_DEPENDANT_LIBS}) + ${THRUST_DEPENDANT_LIBS} + ${MPI_DEPENDANT_LIBS}) set(AER_COMPILER_DEFINITIONS ${AER_COMPILER_DEFINITIONS} ${CONAN_DEFINES}) -# Cython build is only enabled if building through scikit-build. if(SKBUILD) # Terra Addon build - set(AER_LIBRARIES ${AER_LIBRARIES} CONAN_PKG::muparserx) - add_subdirectory(qiskit/providers/aer/pulse/qutip_extra_lite/cy) + set(AER_LIBRARIES ${AER_LIBRARIES} AER_DEPENDENCY_PKG::muparserx) add_subdirectory(qiskit/providers/aer/backends/wrappers) add_subdirectory(src/open_pulse) else() # Standalone build - # We build SIMD filed separately, because they will be reached only if the - # machine running the code has SIMD support if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") - set(SIMD_SOURCE_FILE "${PROJECT_SOURCE_DIR}/src/simulators/statevector/qv_avx2.cpp") - if(APPLE OR UNIX) - set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "-mfma -mavx2") - elseif(MSVC) - set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "/arch:AVX2") - endif() - endif() - - set(AER_SIMULATOR_SOURCES "${PROJECT_SOURCE_DIR}/contrib/standalone/qasm_simulator.cpp" - "${SIMD_SOURCE_FILE}") - set_source_files_properties(${AER_SIMULATOR_SOURCES} PROPERTIES CUDA_SOURCE_PROPERTY_FORMAT OBJ) + # 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() + set(AER_SIMULATOR_SOURCES "${PROJECT_SOURCE_DIR}/contrib/standalone/qasm_simulator.cpp") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - if(CUDA_FOUND) - cuda_add_executable(qasm_simulator ${AER_SIMULATOR_SOURCES}) + if(CUDA_FOUND AND AER_THRUST_BACKEND STREQUAL "CUDA") + set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES LANGUAGE CUDA) + set_source_files_properties(${AER_SIMULATOR_SOURCES} PROPERTIES LANGUAGE CUDA) + set_source_files_properties(${AER_SIMULATOR_SOURCES} 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() + add_executable(qasm_simulator ${AER_SIMULATOR_SOURCES} ${SIMD_SOURCE_FILE}) target_link_libraries(qasm_simulator ${AER_LIBRARIES}) - else(CUDA_FOUND) - add_executable(qasm_simulator ${AER_SIMULATOR_SOURCES}) + string(STRIP ${AER_COMPILER_FLAGS} AER_COMPILER_FLAGS_STRIPPED) + nvcc_add_compiler_options(${AER_COMPILER_FLAGS_STRIPPED} AER_COMPILER_FLAGS_OUT) + + set_target_properties(qasm_simulator 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) + else() + string(REPLACE ";" " " SIMD_FLAGS "${SIMD_FLAGS_LIST}") + set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "${SIMD_FLAGS}") + add_executable(qasm_simulator ${AER_SIMULATOR_SOURCES} ${SIMD_SOURCE_FILE}) target_link_libraries(qasm_simulator PRIVATE ${AER_LIBRARIES}) - endif(CUDA_FOUND) - set_target_properties(qasm_simulator 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) + set_target_properties(qasm_simulator 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) + endif() target_include_directories(qasm_simulator PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR} PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS}) target_compile_definitions(qasm_simulator PRIVATE ${AER_COMPILER_DEFINITIONS}) - if(WIN32 AND NOT BLAS_LIB_PATH) + if(WIN32 AND NOT AER_BLAS_LIB_PATH) add_custom_command(TARGET qasm_simulator POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different - ${OPENBLAS_DLLs} + ${BACKEND_REDIST_DEPS} $) - install(FILES ${OPENBLAS_DLLs} DESTINATION bin) + install(FILES ${BACKEND_REDIST_DEPS} DESTINATION bin) endif() install(TARGETS qasm_simulator DESTINATION bin) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ec7c435994..f82b61c5f6 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,46 +1,9 @@ -# Contributor Covenant Code of Conduct + -## Our Pledge +# Code of Conduct +All members of this project agree to adhere to the Qiskit Code of Conduct listed at [https://github.com/Qiskit/qiskit/blob/master/CODE_OF_CONDUCT.md](https://github.com/Qiskit/qiskit/blob/master/CODE_OF_CONDUCT.md) -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +---- -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at qiskit@qiskit.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), +Copyright Contributors to Qiskit. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5f8a0ace60..44a59da025 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,14 @@ # Contributing First read the overall project contributing guidelines. These are all -included in the qiskit documentation: +included in the Qiskit documentation: https://qiskit.org/documentation/contributing_to_qiskit.html ## Contributing to Qiskit Aer -In addition to the general guidelines there are specific details for -contributing to aer, these are documented below. +In addition to the general guidelines, there are specific details for +contributing to Aer. These are documented below. ### Pull request checklist @@ -23,21 +23,21 @@ please ensure that: *docstring* accordingly. 3. If it makes sense for your change that you have added new tests that cover the changes. -4. Ensure that if your change has an end user facing impact (new feature, - deprecation, removal etc) that you have added a reno release note for that +4. Ensure that if your change has an enduser-facing impact (new feature, + deprecation, removal, etc.), you have added a reno release note for that change and that the PR is tagged for the changelog. ### Changelog generation The changelog is automatically generated as part of the release process automation. This works through a combination of the git log and the pull -request. When a release is tagged and pushed to github the release automation +request. When a release is tagged and pushed to GitHub, the release automation bot looks at all commit messages from the git log for the release. It takes the PR numbers from the git log (assuming a squash merge) and checks if that PR had a `Changelog:` label on it. If there is a label it will add the git commit message summary line from the git log for the release to the changelog. -If there are multiple `Changelog:` tags on a PR the git commit message summary +If there are multiple `Changelog:` tags on a PR, the git commit message summary line from the git log will be used for each changelog category tagged. The current categories for each label are as follows: @@ -52,22 +52,22 @@ The current categories for each label are as follows: ### Release Notes -When making any end user facing changes in a contribution we have to make sure +When making any end user-facing changes in a contribution, we have to make sure we document that when we release a new version of qiskit-aer. The expectation -is that if your code contribution has user facing changes that you will write +is that if your code contribution has user-facing changes that you will write the release documentation for these changes. This documentation must explain what was changed, why it was changed, and how users can either use or adapt -to the change. The idea behind release documentation is that when a naive +to the change. The idea behind the release documentation is that when a naive user with limited internal knowledge of the project is upgrading from the previous release to the new one, they should be able to read the release notes, -understand if they need to update their program which uses qiskit, and how they +understand if they need to update their program which uses Qiskit, and how they would go about doing that. It ideally should explain why they need to make this change too, to provide the necessary context. -To make sure we don't forget a release note or if the details of user facing -changes over a release cycle we require that all user facing changes include -documentation at the same time as the code. To accomplish this we use the -[reno](https://docs.openstack.org/reno/latest/) tool which enables a git based +To make sure we don't forget a release note or if the details of user-facing +changes over a release cycle, we require that all user facing changes include +documentation at the same time as the code. To accomplish this, we use the +[reno](https://docs.openstack.org/reno/latest/) tool which enables a git-based workflow for writing and compiling release notes. #### Adding a new release note @@ -77,21 +77,21 @@ installed with:: pip install -U reno -Once you have reno installed you can make a new release note by running in +Once you have reno installed, you can make a new release note by running in your local repository checkout's root:: reno new short-description-string where short-description-string is a brief string (with no spaces) that describes what's in the release note. This will become the prefix for the release note -file. Once that is run it will create a new yaml file in releasenotes/notes. +file. Once that is run, it will create a new yaml file in releasenotes/notes. Then open that yaml file in a text editor and write the release note. The basic structure of a release note is restructured text in yaml lists under category keys. You add individual items under each category and they will be grouped automatically by release when the release notes are compiled. A single file can have as many entries in it as needed, but to avoid potential conflicts -you'll want to create a new file for each pull request that has user facing -changes. When you open the newly created file it will be a full template of +you'll want to create a new file for each pull request that has user-facing +changes. When you open the newly created file, it will be a full template of the different categories with a description of a category as a single entry in each category. You'll want to delete all the sections you aren't using and update the contents for those you are. For example, the end result should @@ -132,19 +132,19 @@ deprecations: You can also look at other release notes for other examples. You can use any restructured text feature in them (code sections, tables, -enumerated lists, bulleted list, etc) to express what is being changed as -needed. In general you want the release notes to include as much detail as +enumerated lists, bulleted list, etc.) to express what is being changed as +needed. In general, you want the release notes to include as much detail as needed so that users will understand what has changed, why it changed, and how they'll have to update their code. -After you've finished writing your release notes you'll want to add the note +After you've finished writing your release notes, you'll want to add the note file to your commit with `git add` and commit them to your PR branch to make sure they're included with the code in your PR. ##### Linking to issues -If you need to link to an issue or other github artifact as part of the release -note this should be done using an inline link with the text being the issue +If you need to link to an issue or other GitHub artifact as part of the release +note, this should be done using an inline link with the text being the issue number. For example you would write a release note with a link to issue 12345 as: @@ -158,12 +158,12 @@ fixes: #### Generating the release notes -After release notes have been added if you want to see what the full output of -the release notes. In general the output from reno that we'll get is a rst +After release notes have been added, if you want to see the full output of +the release notes, you'll get the output as an rst (ReStructuredText) file that can be compiled by -[sphinx](https://www.sphinx-doc.org/en/master/). To generate the rst file you -use the ``reno report`` command. If you want to generate the full aer release -notes for all releases (since we started using reno during 0.9) you just run:: +[sphinx](https://www.sphinx-doc.org/en/master/). To generate the rst file, you +use the ``reno report`` command. If you want to generate the full Aer release +notes for all releases (since we started using reno during 0.9), you just run:: reno report @@ -172,7 +172,7 @@ it has been tagged:: reno report --version 0.5.0 -At release time ``reno report`` is used to generate the release notes for the +At release time, ``reno report`` is used to generate the release notes for the release and the output will be submitted as a pull request to the documentation repository's [release notes file]( https://github.com/Qiskit/qiskit/blob/master/docs/release_notes.rst) @@ -180,18 +180,18 @@ https://github.com/Qiskit/qiskit/blob/master/docs/release_notes.rst) #### Building release notes locally Building The release notes are part of the standard qiskit-aer documentation -builds. To check what the rendered html output of the release notes will look -like for the current state of the repo you can run: `tox -edocs` which will +builds. To check what the rendered HTML output of the release notes will look +like for the current state of the repo, you can run: `tox -edocs` which will build all the documentation into `docs/_build/html` and the release notes in particular will be located at `docs/_build/html/release_notes.html` ### Development Cycle The development cycle for qiskit-aer is all handled in the open using -the project boards in Github for project management. We use milestones -in Github to track work for specific releases. The features or other changes -that we want to include in a release will be tagged and discussed in Github. -As we're preparing a new release we'll document what has changed since the +the project boards in GitHub for project management. We use milestones +in GitHub to track work for specific releases. The features or other changes +that we want to include in a release will be tagged and discussed in GitHub. +As we're preparing a new release, we'll document what has changed since the previous version in the release notes. ### Branches @@ -211,7 +211,7 @@ merged to it are bugfixes. ### Release cycle -When it is time to release a new minor version of qiskit-aer we will: +When it is time to release a new minor version of qiskit-aer, we will: 1. Create a new tag with the version number and push it to github 2. Change the `master` version to the next release version. @@ -222,7 +222,7 @@ the following steps: 1. Create a stable branch for the new minor version from the release tag on the `master` branch 2. Build and upload binary wheels to pypi -3. Create a github release page with a generated changelog +3. Create a GitHub release page with a generated changelog 4. Generate a PR on the meta-repository to bump the Aer version and meta-package version. @@ -249,7 +249,7 @@ window $ git clone https://github.com/Qiskit/qiskit-aer ``` -- Next, install the platform-specific dependencies for your operating system [Linux](#linux-dependencies) | [macOS](#mac-dependencies) | [Windows](#win-dependencies). +- Next, install the platform-specific dependencies for your operating system [Linux](#linux-dependencies) | [macOS](#mac-dependencies) | [Windows](#win-dependencies). - The common dependencies can then be installed via *pip*, using the `requirements-dev.txt` file, e.g.: @@ -258,11 +258,14 @@ window $ pip install -r requirements-dev.txt ``` -This will also install [**Conan**](https://conan.io/), a C/C++ package manager written in Python. This tool will handle -most of the dependencies needed by the C++ source code. Internet connection may be needed for the first build or -when dependencies are added/updated, in order to download the required packages if they are not in your **Conan** local +This will also install [**Conan**](https://conan.io/), a C/C++ package manager written in Python. This tool will handle +most of the dependencies needed by the C++ source code. Internet connection may be needed for the first build or +when dependencies are added/updated, in order to download the required packages if they are not in your **Conan** local repository. +> Note: Conan use can be disabled with the flag or environment variable ``DISABLE_CONAN=ON`` . +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**: @@ -272,7 +275,7 @@ You're now ready to build from source! Follow the instructions for your platform ### Linux -Qiskit is officially supported on Red Hat, CentOS, Fedora and Ubuntu distributions, as long as you can install a GCC version that is C++14 compatible and the few dependencies we need. +Qiskit is officially supported on Red Hat, CentOS, Fedora, and Ubuntu distributions, as long as you can install a GCC version that is C++14 compatible and a few dependencies we need. #### Dependencies @@ -307,7 +310,7 @@ Ubuntu $ sudo apt install libopenblas-dev -And of course, `git` is required in order to build from repositories +And of course, `git` is required to build from repositories CentOS/Red Hat @@ -325,17 +328,17 @@ Ubuntu There are two ways of building `Aer` simulators, depending on your goal: -1. Build a python extension that works with Terra. +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: +As any other Python package, we can install from source code by just running: qiskit-aer$ pip install . This will build and install `Aer` with the default options which is probably suitable for most of the users. -There's another pythonic approach to build and install software: build the wheels distributable file. +There's another Pythonic approach to build and install software: build the wheels distributable file. qiskit-aer$ python ./setup.py bdist_wheel @@ -371,9 +374,9 @@ the `dist/` directory, so next step is installing it: **Standalone Executable** -If we want to build a standalone executable, we have to use *CMake* directly. +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, we have to follow +source" build. So in order to build your standalone executable, you have to follow these steps: qiskit-aer$ mkdir out @@ -393,8 +396,8 @@ option): **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 in order to pass all the different -options we have on `Aer` to CMake we use it's native mechanism: +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 .. @@ -418,17 +421,17 @@ You further need to have *Xcode Command Line Tools* installed on macOS: There are two ways of building `Aer` simulators, depending on your goal: -1. Build a python extension that works with Terra; +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: +As any other Python package, we can install from source code by just running: qiskit-aer$ pip install . This will build and install `Aer` with the default options which is probably suitable for most of the users. -There's another pythonic approach to build and install software: build the wheels distributable file. +There's another Pythonic approach to build and install software: build the wheels distributable file. qiskit-aer$ python ./setup.py bdist_wheel @@ -464,9 +467,9 @@ the `dist/` directory, so next step is installing it: **Standalone Executable** -If we want to build a standalone executable, we have to use **CMake** directly. +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, we have to follow +source" build. So in order to build your standalone executable, you have to follow these steps: qiskit-aer$ mkdir out @@ -485,8 +488,8 @@ option): ***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 in order to pass all the different -options we have on `Aer` to CMake we use it's native mechanism: +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 .. @@ -496,7 +499,7 @@ options we have on `Aer` to CMake we use it's native mechanism: #### Dependencies -On Windows, you must have *Anaconda3* installed. We recommend also installing +On Windows, you must have *Anaconda3* installed. We also recommend installing *Visual Studio 2017 Community Edition* or *Visual Studio 2019 Community Edition*. >*Anaconda 3* can be installed from their web: @@ -515,19 +518,19 @@ create an Anaconda virtual environment or activate it if you already have create We only support *Visual Studio* compilers on Windows, so if you have others installed in your machine (MinGW, TurboC) you have to make sure that the path to the *Visual Studio* tools has precedence over others so that the build system can get the correct one. -There's a (recommended) way to force the build system to use the one you want by using CMake `-G` parameter. Will talk +There's a (recommended) way to force the build system to use the one you want by using CMake `-G` parameter. We will talk about this and other parameters later. #### Build **Python extension** -As any other python package, we can install from source code by just running: +As any other Python package, we can install from source code by just running: (QiskitDevEnv) qiskit-aer > pip install . This will build and install `Aer` with the default options which is probably suitable for most of the users. -There's another pythonic approach to build and install software: build the wheels distributable file. +There's another Pythonic approach to build and install software: build the wheels distributable file. (QiskitDevEnv) qiskit-aer > python ./setup.py bdist_wheel @@ -563,9 +566,9 @@ the `dist/` directory, so next step is installing it: **Standalone Executable** -If we want to build a standalone executable, we have to use **CMake** directly. +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, we have to follow +source" build. So in order to build our standalone executable, you have to follow these steps: (QiskitDevEnv) qiskit-aer> mkdir out @@ -584,8 +587,8 @@ option): ***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 in order to pass all the different -options we have on `Aer` to CMake we use it's native mechanism: +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 .. @@ -593,11 +596,11 @@ options we have on `Aer` to CMake we use it's native mechanism: ### Building with GPU support Qiskit Aer can exploit GPU's horsepower to accelerate some simulations, specially the larger ones. -GPU access is supported via CUDA® (NVIDIA® chipset), so in order to build with GPU support we need +GPU access is supported via CUDA® (NVIDIA® chipset), so to build with GPU support, you need to have CUDA® >= 10.1 preinstalled. See install instructions [here](https://developer.nvidia.com/cuda-toolkit-archive) Please note that we only support GPU acceleration on Linux platforms at the moment. -Once CUDA® is properly installed, we only need to set a flag so the build system knows what to do: +Once CUDA® is properly installed, you only need to set a flag so the build system knows what to do: ``` AER_THRUST_BACKEND=CUDA @@ -607,8 +610,8 @@ For example, qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_THRUST_BACKEND=CUDA -If we want to specify the CUDA® architecture instead of letting the build system -auto detect it, we can use the AER_CUDA_ARCH flag (can also be set as an ENV variable +If you want to specify the CUDA® architecture instead of letting the build system +auto detect it, you can use the AER_CUDA_ARCH flag (can also be set as an ENV variable with the same name, although the flag takes precedence). For example: qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_THRUST_BACKEND=CUDA -DAER_CUDA_ARCH="5.2" @@ -627,6 +630,166 @@ Few notes on GPU builds: 3. We don't need NVIDIA® drivers for building, but we need them for running simulations 4. Only Linux platforms are supported +### Building with MPI support + +Qiskit Aer can parallelize its simulation on the cluster systems by using MPI. +This can extend available memory space to simulate quantum circuits with larger number of qubits and also can accelerate the simulation by parallel computing. +To use MPI support, any MPI library (i.e. OpenMPI) should be installed and configured on the system. + +Qiskit Aer supports MPI both with and without GPU support. Currently following simulation methods are supported to be parallelized by MPI. + + - statevector + - statevector_thrust_gpu + - statevector_thrust_cpu + - density_matrix + - density_matrix_thrust_gpu + - density_matrix_thrust_cpu + - unitary_cpu + - unitary_thrust_gpu + - unitary_thrust_cpu + +To enable MPI support, the following flag is needed for build system based on CMake. + +``` +AER_MPI=True +``` + +For example, + + qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_MPI=True + +By default GPU direct RDMA is enable to exchange data between GPUs installed on the different nodes of a cluster. If the system does not support GPU direct RDMA the following flag disables this. + +``` +AER_DISABLE_GDR=True +``` + +For example, + + qiskit-aer$ python ./setup.py bdist_wheel -- -DAER_MPI=True -DAER_DISABLE_GDR=True + +### Running with multiple-GPUs and/or multiple nodes + +Qiskit Aer parallelizes simulations by distributing quantum states into distributed memory space. +To decrease data transfer between spaces the distributed states are managed as chunks that is a sub-state for smaller qubits than the input circuits. + +For example, +30-qubits circuit is distributed into 2^10 chunks with 20-qubits. + +To decrease data exchange between chunks and also to simplify the implementation, we are applying cache blocking technique. +This technique allows applying quantum gates to each chunk independently without data exchange, and serial simulation codes can be reused without special implementation. +Before the actual simulation, we apply transpilation to remap the input circuits to the equivalent circuits that has all the quantum gates on the lower qubits than the chunk's number of qubits. +And the (noiseless) swap gates are inserted to exchange data. + +Please refer to this paper (https://arxiv.org/abs/2102.02957) for more detailed algorithm and implementation of parallel simulation. + +So to simulate by using multiple GPUs or multiple nodes on the cluster, following configurations should be set to backend options. +(If there is not enough memory to simulate the input circuit, Qiskit Aer automatically set following options, but it is recommended to explicitly set them) + + - blocking_enable + + should be set to True for distributed parallelization. (Default = False) + + - blocking_qubits + + this flag sets the qubit number for chunk, should be smaller than the smallest memory space on the system (i.e. GPU). Set this parameter to satisfy `sizeof(complex)*2^(blocking_qubits+4) < size of the smallest memory space` in byte. + +Here is an example how we parallelize simulation with multiple GPUs. + +``` +circ = transpile(QuantumVolume(qubit, 10, seed = 0)) +circ.measure_all() +qobj = assemble(circ, shots=shots) +result = sim.run(qobj, method="statevector_gpu", blocking_enable=True, blocking_qubits=23).result() +``` + +To run Qiskit Aer with Python script with MPI parallelization, MPI executer such as mpirun should be used to submit a job on the cluster. Following example shows how to run Python script using 4 processes by using mpirun. + +``` +mpirun -np 4 python example.py +``` + +MPI_Init function is called inside Qiskit Aer, so you do not have to manage MPI processes in Python script. +Following metadatas are useful to find on which process is this script running. + + - num_mpi_processes : shows number of processes using for this simulation + - mpi_rank : shows zero based rank (process ID) + + +Here is an example how to get my rank. + +``` +result = sim.run(qobj, method="statevector_gpu", blocking_enable=True, blocking_qubits=23).result() +dict = result.to_dict() +meta = dict['metadata'] +myrank = meta['mpi_rank'] +``` + + +### Building a statically linked wheel + +If you encounter an error similar to the following, you may are likely in the need of compiling a +statically linked wheel. +``` + ImportError: libopenblas.so.0: cannot open shared object file: No such file or directory +``` +However, depending on your setup this can proof difficult at times. +Thus, here we present instructions which are known to work under Linux. + +In general, the workflow is: +1. Compile a wheel +``` + qiskit-aer$ python ./setup.py bdist_wheel +``` +2. Repair it with [auditwheel](https://github.com/pypa/auditwheel) +``` + qiskit-aer$ auditwheel repair dist/qiskit_aer*.whl +``` +> `auditwheel` vendors the shared libraries into the binary to make it fully self-contained. + +The command above will attempt to repair the wheel for a `manylinux*` platform and will store it +under `wheelhouse/` from where you can install it. + +It may happen that you encounter the following error: +``` + auditwheel: error: cannot repair "qiskit_aer-0.8.0-cp36-cp36m-linux_x86_64.whl" to "manylinux1_x86_64" ABI because of the presence of too-recent versioned symbols. You'll need to compile the wheel on an older toolchain. +``` +This means that your toolchain uses later versions of system libraries than are allowed by the +`manylinux*` platform specification (see also [1], [2] and [3]). +If you do not need your wheel to support the `manylinux*` platform you can resolve this issue by +limiting the compatibility of your wheel to your specific platform. +You can find out which platform this is through +``` + qiskit-aer$ auditwheel show dist/qiskit_aer*.whl +``` +This will list the _platform tag_ (e.g. `linux_x86_64`). +You can then repair the wheel for this specific platform using: +``` + qiskit-aer$ auditwheel repair --plat linux_x86_64 dist/qiskit_aer*.whl +``` +You can now go ahead and install the wheel stored in `wheelhouse/`. + +Should you encounter a runtime error like +``` + Inconsistency detected by ld.so: dl-version.c: 205: _dl_check_map_versions: Assertion `needed != NULL' failed! +``` +this means that your [patchelf](https://github.com/NixOS/patchelf) version (which is used by +`auditwheel` under the hood) is too old (https://github.com/pypa/auditwheel/issues/103) +Version `0.9` of `patchelf` is the earliest to include the patch +https://github.com/NixOS/patchelf/pull/85 which resolves this issue. +In the unlikely event that the `patchelf` package provided by your operating +system only provides an older version, fear not, because it is really easy to +[compile `patchelf` from source](https://github.com/NixOS/patchelf#compiling-and-testing). + +Hopefully, this information was helpful. +In case you need more detailed information on some of the errors which may occur be sure to read +through https://github.com/Qiskit/qiskit-aer/issues/1033. + +[1]: https://www.python.org/dev/peps/pep-0513/ +[2]: https://www.python.org/dev/peps/pep-0571/ +[3]: https://www.python.org/dev/peps/pep-0599/ + + ## Useful CMake flags @@ -640,7 +803,7 @@ pass them right after ``-D`` CMake argument. Example: qiskit-aer/out$ cmake -DUSEFUL_FLAG=Value .. ``` -In the case of building the Qiskit python extension, you have to pass these flags after writing +In the case of building the Qiskit Python extension, you have to pass these flags after writing ``--`` at the end of the python command line, eg: ``` @@ -660,8 +823,7 @@ These are the flags: * AER_BLAS_LIB_PATH Tells CMake the directory to look for the BLAS library instead of the usual paths. - If no BLAS library is found under that directory, CMake will raise an error and stop. - + If no BLAS library is found under that directory, CMake will raise an error and terminate. It can also be set as an ENV variable with the same name, although the flag takes precedence. Values: An absolute path. @@ -687,8 +849,8 @@ These are the flags: * AER_THRUST_BACKEND - We use Thrust library for GPU support through CUDA. If we want to build a version of `Aer` with GPU acceleration, we need to install CUDA and set this variable to the value: "CUDA". - There are other values that will use different CPU methods depending on the kind of backend we want to use: + We use Thrust library for GPU support through CUDA. If you want to build a version of `Aer` with GPU acceleration, you need to install CUDA and set this variable to the value: "CUDA". + There are other values that will use different CPU methods depending on the kind of backend you want to use: - "OMP": For OpenMP support - "TBB": For Intel Threading Building Blocks @@ -698,22 +860,63 @@ These are the flags: * AER_CUDA_ARCH - This flag allows us we to specify the CUDA architecture instead of letting the build system auto detect it. + This flag allows you to specify the CUDA architecture instead of letting the build system auto detect it. It can also be set as an ENV variable with the same name, although the flag takes precedence. - + Values: Auto | Common | All | List of valid CUDA architecture(s). Default: Auto Example: ``python ./setup.py bdist_wheel -- -DAER_THRUST_BACKEND=CUDA -DAER_CUDA_ARCH="5.2; 5.3"`` +* DISABLE_CONAN + + This flag allows disabling the Conan package manager. This will force CMake to look for + the libraries in use on your system path, relying on FindPackage CMake mechanism and + the appropriate configuration of libraries in order to use it. + If a specific version is not found, the build system will look for any version available, + although this may produce build errors or incorrect behaviour. + + __WARNING__: This is not the official procedure to build AER. Thus, the user is responsible + of providing all needed libraries and corresponding files to make them findable to CMake. + + This is also available as the environment variable ``DISABLE_CONAN``, which overrides + the CMake flag of the same name. + + Values: ON | OFF + Default: OFF + Example: ``python ./setup.py bdist_wheel -- -DDISABLE_CONAN=ON`` + +* AER_MPI + + This flag enables/disables parallelization using MPI to simulate circuits with large number of qubits + on the cluter systems. This option requires any MPI library and runtime installed on your system. + MPI parallelization can be used both with/without GPU support. + For GPU support GPU direct RDMA is enabled by default, see option AER_DISABLE_GDR below. + + Values: True|False + Default: False + Example: ``python ./setup.py bdist_wheel -- -DAER_MPI=True`` + +* AER_DISABLE_GDR + + This flag disables/enables GPU direct RDMA to exchange data between GPUs on different nodes. + If your system does not support GPU direct RDMA, please set True to this option. You do not need this option if you do not use GPU support. + You may also have to configure MPI to use GPU direct RDMA if you enable (AER_DISABLE_GDR=False) this option. + + Note: GPU direct between GPUs on the same node (peer-to-peer copy) is automatically enabled if supported GPUs are available. + + Values: True|False + Default: False + Example: ``python ./setup.py bdist_wheel -- -DAER_MPI=True -DAER_DISABLE_GDR=True`` + ## Tests -Code contribution are expected to include tests that provide coverage for the +Code contributions are expected to include tests that provide coverage for the changes being made. We have two types of tests in the codebase: Qiskit Terra integration tests and Standalone integration tests. -For Qiskit Terra integration tests, you first need to build and install the Qiskit python extension, and then run `unittest` Python framework. +For Qiskit Terra integration tests, you first need to build and install the Qiskit Python extension, and then run `unittest` Python framework. ``` qiskit-aer$ pip install . @@ -722,7 +925,7 @@ qiskit-aer$ stestr run Manual for `stestr` can be found [here](https://stestr.readthedocs.io/en/latest/MANUAL.html#). -The integration tests for Qiskit python extension are included in: `test/terra`. +The integration tests for Qiskit Python extension are included in: `test/terra`. ## C++ Tests @@ -740,6 +943,7 @@ The test executable will be placed into the source test directory and can be run qiskit-aer$ ./test/unitc_tests [Catch2-options] ``` + ## Platform support Bear in mind that every new feature/change needs to be compatible with all our @@ -750,17 +954,17 @@ corresponding tests to verify this compatibility. ## Debug -We have to build in debug mode if we want to start a debugging session with tools like `gdb` or `lldb`. -In order to create a Debug build for all platforms, we just need to pass a parameter while invoking the build to +You have to build in debug mode if you want to start a debugging session with tools like `gdb` or `lldb`. +To create a Debug build for all platforms, you just need to pass a parameter while invoking the build to create the wheel file: qiskit-aer$> python ./setup.py bdist_wheel --build-type=Debug -If you want to debug the standalone executable, then the parameter changes to: +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`, which parameters are: +There are three different build configurations: `Release`, `Debug`, and `Release with Debug Symbols`, whose parameters are: `Release`, `Debug`, `RelWithDebInfo` respectively. We recommend building in verbose mode and dump all the output to a file so it's easier to inspect possible build issues: @@ -774,7 +978,7 @@ On Windows: qisikt-aer> set VERBOSE=1 qiskit-aer> python ./setup.py bdist_wheel --build-type=Debug 1> build.log 2>&1 -We encourage to always send the whole `build.log` file when reporting a build issue, otherwise we will ask for it :) +We encourage you to always send the whole `build.log` file when reporting a build issue, otherwise we will ask for it :) **Stepping through the code** @@ -784,9 +988,9 @@ Standalone version doesn't require anything special, just use your debugger like qiskit-aer/out/Debug$ gdb qasm_simulator Stepping through the code of a Python extension is another story, trickier, but possible. This is because Python interpreters -usually load Python extensions dynamically, so we need to start debugging the python interpreter and set our breakpoints ahead of time, before any of our python extension symbols are loaded into the process. +usually load Python extensions dynamically, so we need to start debugging the Python interpreter and set our breakpoints ahead of time, before any of our Python extension symbols are loaded into the process. -Once built and installed we have to run the debugger with the python interpreter: +Once built and installed, we have to run the debugger with the Python interpreter: $ lldb python @@ -802,9 +1006,9 @@ Then we have to set our breakpoints: Breakpoint 1: no locations (pending). WARNING: Unable to resolve breakpoint to any actual locations. -Here the message is clear, it can't find the function: `AER::controller_execute` because our python extension hasn't been loaded yet - by the python interpreter, so it's "on-hold" hoping to find the function later in the execution. -Now we can run the python interpreter and pass the arguments (the python file to execute): +Here the message is clear, it can't find the function: `AER::controller_execute` because our Python extension hasn't been loaded yet + by the Python interpreter, so it's "on-hold" hoping to find the function later in the execution. +Now we can run the Python interpreter and pass the arguments (the python file to execute): (lldb) r test_qiskit_program.py Process 24896 launched: '/opt/anaconda3/envs/aer37/bin/python' (x86_64) diff --git a/Makefile.Thrust b/Makefile.Thrust deleted file mode 100644 index 890b143be8..0000000000 --- a/Makefile.Thrust +++ /dev/null @@ -1,51 +0,0 @@ - -PROJECT_SOURCE_DIR=. -AER_SIMULATOR_CPP_SRC_DIR=$(PROJECT_SOURCE_DIR)/src -AER_SIMULATOR_CPP_MAIN=$(PROJECT_SOURCE_DIR)/contrib/standalone/qasm_simulator.cpp - - - -CUCC = /usr/local/cuda/bin/nvcc -CXX = /gpfs/wscgpfs01/doichan/gcc/bin/g++ - -CXXFLAGS= -ccbin $(CXX) -DQASM_THRUST -gencode arch=compute_70,code=sm_70 -std c++14 -I$(AER_SIMULATOR_CPP_SRC_DIR) -Xcompiler "-O3 -fopenmp -ffast-math -isystem $(AER_SIMULATOR_CPP_SRC_DIR)/third-party/headers" -use_fast_math -expt-extended-lambda -I/usr/local/cuda/include -x cu - -#-DQASM_DEBUG -DQASM_TIMING - -LDFLAGS=-O3 -fopenmp -std=c++14 - -LDLIBS = -lpthread -L/usr/local/cuda/lib64 -lcudart -lopenblas - -LD = $(CXX) - - -#// program =================================================================== - -MODULE = qasm_simulator - -SRCSC = \ - $(AER_SIMULATOR_CPP_MAIN) \ - -OBJSC = ${SRCSC:.cpp=.o} - -#// rules ===================================================================== - -all: $(MODULE) - -.SUFFIXES: .o .cpp - -.cpp.o: - $(CUCC) $(CXXFLAGS) -c $< -o $@ - - -$(MODULE): $(OBJSC) - rm -f $(MODULE) - $(LD) $(OBJSC) $(LDFLAGS) $(LDLIBS) -o $@ - - -clean: - rm -f $(OBJSC) $(MODULE) - - - - diff --git a/README.md b/README.md index 12dde86f57..dce7fad753 100755 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ To install from source, follow the instructions in the [contribution guidelines] ## Installing GPU support -In order to install and run the GPU supported simulators, you need CUDA® 10.1 or newer previously installed. +In order to install and run the GPU supported simulators on Linux, you need CUDA® 10.1 or newer previously installed. CUDA® itself would require a set of specific GPU drivers. Please follow CUDA® installation procedure in the NVIDIA® [web](https://www.nvidia.com/drivers). If you want to install our GPU supported simulators, you have to install this other package: @@ -33,6 +33,11 @@ This will overwrite your current `qiskit-aer` package installation giving you the same functionality found in the canonical `qiskit-aer` package, plus the ability to run the GPU supported simulators: statevector, density matrix, and unitary. +**Note**: This package is only available on x86_64 Linux. For other platforms +that have CUDA support you will have to build from source. You can refer to +the [contributing guide](https://github.com/Qiskit/qiskit-aer/blob/master/CONTRIBUTING.md#building-with-gpu-support) +for instructions on doing this. + ## Simulating your first quantum program with Qiskit Aer Now that you have Qiskit Aer installed, you can start simulating quantum circuits with noise. Here is a basic example: @@ -41,43 +46,41 @@ $ python ``` ```python -from qiskit import QuantumCircuit, execute -from qiskit import Aer, IBMQ -from qiskit.providers.aer.noise import NoiseModel - -# Choose a real device to simulate from IBMQ provider -provider = IBMQ.load_account() -backend = provider.get_backend('ibmq_vigo') -coupling_map = backend.configuration().coupling_map - -# Generate an Aer noise model for device -noise_model = NoiseModel.from_backend(backend) -basis_gates = noise_model.basis_gates +import qiskit +from qiskit import IBMQ +from qiskit.providers.aer import QasmSimulator # Generate 3-qubit GHZ state -num_qubits = 3 -circ = QuantumCircuit(3, 3) +circ = qiskit.QuantumCircuit(3, 3) circ.h(0) circ.cx(0, 1) circ.cx(1, 2) circ.measure([0, 1, 2], [0, 1 ,2]) +# Construct an ideal simulator +sim = QasmSimulator() + +# Perform an ideal simulation +result_ideal = qiskit.execute(circ, sim).result() +counts_ideal = result_ideal.get_counts(0) +print('Counts(ideal):', counts_ideal) +# Counts(ideal): {'000': 493, '111': 531} + +# Construct a noisy simulator backend from an IBMQ backend +# This simulator backend will be automatically configured +# using the device configuration and noise model +provider = IBMQ.load_account() +vigo_backend = provider.get_backend('ibmq_vigo') +vigo_sim = QasmSimulator.from_backend(vigo_backend) + # Perform noisy simulation -backend = Aer.get_backend('qasm_simulator') -job = execute(circ, backend, - coupling_map=coupling_map, - noise_model=noise_model, - basis_gates=basis_gates) -result = job.result() - -print(result.get_counts(0)) -``` +result_noise = qiskit.execute(circ, vigo_sim).result() +counts_noise = result_noise.get_counts(0) -```python -{'000': 495, '001': 18, '010': 8, '011': 18, '100': 2, '101': 14, '110': 28, '111': 441} +print('Counts(noise):', counts_noise) +# Counts(noise): {'000': 492, '001': 6, '010': 8, '011': 14, '100': 3, '101': 14, '110': 18, '111': 469} ``` - ## Contribution Guidelines If you'd like to contribute to Qiskit, please take a look at our diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 5ac8194946..0000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,289 +0,0 @@ -# Python package -# Create and test a Python package on multiple Python versions. -# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/python - -trigger: - branches: - include: - - master - - stable/* - tags: - include: - - '*' -pr: - autoCancel: true - branches: - include: - - '*' -stages: - - stage: 'Wheel_Builds' - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags') - jobs: - - job: 'Windows_Wheel_Builds' - pool: {vmImage: 'vs2017-win2016'} - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags') - variables: - TWINE_USERNAME: qiskit - steps: - - checkout: self - submodules: true - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - - bash: | - set -x - set -e - for version in 3.6 3.7 3.8 ; do - conda create --yes --quiet --name qiskit-aer-$version python=$version - done - displayName: Create Anaconda environments - - bash: | - set -x - set -e - mkdir wheelhouse - for version in 3.6 3.7 3.8 ; do - source activate qiskit-aer-$version - conda update --yes -n base conda - conda config --add channels conda-forge - conda install --yes --quiet --name qiskit-aer-$version python=$version numpy cmake virtualenv pip setuptools pybind11 cython scipy - python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" - virtualenv test-$version - test-$version/Scripts/pip install -c constraints.txt dist/*whl - test-$version/Scripts/python tools/verify_wheels.py - mv dist/*whl wheelhouse/. - rm -rf test-$version - rm -rf _skbuild - rm -rf .eggs - rm -rf qiskit_aer.egg-info - rm -rf contrib/standalone/version.hpp - done - - displayName: Build wheels - - task: PublishBuildArtifacts@1 - inputs: {pathtoPublish: 'wheelhouse'} - condition: succeededOrFailed() - - bash: | - pip install -U twine - twine upload wheelhouse/* - env: - TWINE_PASSWORD: $(TWINE_PASSWORD) - - job: 'Windows_win32_Wheel_Builds' - pool: {vmImage: 'vs2017-win2016'} - condition: startsWith(variables['Build.SourceBranch'], 'refs/tags') - variables: - TWINE_USERNAME: qiskit - steps: - - checkout: self - submodules: true - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - - bash: | - set -x - set -e - conda update --yes -n base conda - conda config --add channels conda-forge - displayName: Update Conda - - bash: | - set -x - set -e - for version in 3.6 3.7 3.8 ; do - conda create --yes --quiet --name qiskit-aer-$version python=$version - done - displayName: Create Anaconda environments - env: - CONDA_FORCE_32BIT: 1 - - bash: | - set -x - set -e - mkdir wheelhouse - for version in 3.6 3.7 3.8 ; do - source activate qiskit-aer-$version - conda install --yes --quiet --name qiskit-aer-$version python=$version numpy cmake virtualenv pip setuptools pybind11 cython scipy - python -m pip install -U setuptools wheel - python setup.py bdist_wheel -- -G "Visual Studio 15 2017" - virtualenv test-$version - test-$version/Scripts/pip install -c constraints.txt dist/*whl - test-$version/Scripts/python tools/verify_wheels.py - mv dist/*whl wheelhouse/. - rm -rf test-$version - rm -rf _skbuild - rm -rf .eggs - rm -rf qiskit_aer.egg-info - rm -rf contrib/standalone/version.hpp - done - displayName: Build wheels - env: - CONDA_FORCE_32BIT: 1 - - task: PublishBuildArtifacts@1 - inputs: {pathtoPublish: 'wheelhouse'} - condition: succeededOrFailed() - - bash: | - pip install -U twine - twine upload wheelhouse/* - env: - TWINE_PASSWORD: $(TWINE_PASSWORD) - - - - stage: 'Compile' - dependsOn: [] - jobs: - - job: 'Windows_Wheel_Builds' - pool: {vmImage: 'vs2017-win2016'} - strategy: - maxParallel: 2 - matrix: - Python36: - python.version: '3.6' - Python37: - python.version: '3.7' - Python38: - python.version: '3.8' - variables: - PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip - steps: - - checkout: self - submodules: true - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - - task: Cache@2 - inputs: - key: 'pip | "$(Agent.OS)" | "$(python.version)" |"$(Build.BuildNumber)"' - restoreKeys: | - pip | "$(Agent.OS)" | "$(python.version)" - pip | "$(Agent.OS)" - pip - path: $(PIP_CACHE_DIR) - displayName: Cache pip - - bash: | - set -x - set -e - conda create --yes --quiet --name qiskit-aer python=$(python.version) - source activate qiskit-aer - conda update --yes -n base conda - conda config --add channels conda-forge - conda install --yes --quiet --name qiskit-aer python=$(python.version) numpy cmake virtualenv pip setuptools pybind11 cython scipy - displayName: Create Anaconda environments - - bash: | - set -x - set -e - source activate qiskit-aer - python setup.py bdist_wheel -- -G "Visual Studio 15 2017 Win64" - displayName: Build wheels - - bash: | - set -x - set -e - source activate qiskit-aer - virtualenv test-wheel - test-wheel/Scripts/pip install -c constraints.txt dist/*whl - test-wheel/Scripts/pip install -c constraints.txt git+https://github.com/Qiskit/qiskit-terra - test-wheel/Scripts/python tools/verify_wheels.py - displayName: Verify wheels - - job: 'Windows_win32_Wheel_Builds' - pool: {vmImage: 'vs2017-win2016'} - strategy: - matrix: - Python37: - python.version: '3.7' - variables: - PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip - steps: - - checkout: self - submodules: true - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - - task: Cache@2 - inputs: - key: 'pip | "$(Agent.OS)" | "$(python.version)" |"$(Build.BuildNumber)"' - restoreKeys: | - pip | "$(Agent.OS)" | "$(python.version)" - pip | "$(Agent.OS)" - pip - path: $(PIP_CACHE_DIR) - displayName: Cache pip - - bash: | - set -x - set -e - conda update --yes -n base conda - conda config --add channels conda-forge - displayName: Update Conda - - bash: | - set -x - set -e - conda create --yes --quiet --name qiskit-aer python=$(python.version) - source activate qiskit-aer - conda install --yes --quiet --name qiskit-aer python=$(python.version) numpy cmake virtualenv pip setuptools pybind11 cython scipy - displayName: Create Anaconda environments - env: - CONDA_FORCE_32BIT: 1 - - bash: | - set -x - set -e - source activate qiskit-aer - pip install -U setuptools wheel - python setup.py bdist_wheel -- -G "Visual Studio 15 2017" - displayName: Build wheels - env: - CONDA_FORCE_32BIT: 1 - - bash: | - set -x - set -e - source activate qiskit-aer - virtualenv test-wheel - test-wheel/Scripts/pip install -c constraints.txt dist/*whl - test-wheel/Scripts/pip install -c constraints.txt git+https://github.com/Qiskit/qiskit-terra - test-wheel/Scripts/python tools/verify_wheels.py - displayName: Verify wheels - env: - CONDA_FORCE_32BIT: 1 - - - job: 'Windows_sdist_Builds' - pool: {vmImage: 'vs2017-win2016'} - strategy: - maxParallel: 1 - matrix: - Python37: - python.version: '3.7' - Python38: - python.version: '3.8' - variables: - PIP_CACHE_DIR: $(Pipeline.Workspace)/.pip - steps: - - checkout: self - submodules: true - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Add conda to PATH - - task: Cache@2 - inputs: - key: 'pip | "$(Agent.OS)" | "$(python.version)" |"$(Build.BuildNumber)"' - restoreKeys: | - pip | "$(Agent.OS)" | "$(python.version)" - pip | "$(Agent.OS)" - pip - path: $(PIP_CACHE_DIR) - displayName: Cache pip - - bash: | - set -x - set -e - conda create --yes --quiet --name qiskit-aer python=$(python.version) - source activate qiskit-aer - conda update --yes -n base conda - conda config --add channels conda-forge - conda install --yes --quiet --name qiskit-aer python=$(python.version) numpy cmake virtualenv pip setuptools pybind11 cython scipy - displayName: Create Anaconda environments - - bash: | - set -x - set -e - source activate qiskit-aer - python setup.py sdist -- -G "Visual Studio 15 2017 Win64" - displayName: Build sdist - - bash: | - set -x - set -e - source activate qiskit-aer - pip install -U setuptools virtualenv wheel - pip install dist/*tar.gz - pip install git+https://github.com/Qiskit/qiskit-terra - python tools/verify_wheels.py - env: - CMAKE_GENERATOR: "Visual Studio 15 2017 Win64" - displayName: Install from sdist and verify diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake deleted file mode 100644 index b8ae305323..0000000000 --- a/cmake/FindCython.cmake +++ /dev/null @@ -1,78 +0,0 @@ -#.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) - diff --git a/cmake/FindPybind11.cmake b/cmake/FindPybind11.cmake index b8be02c045..60ae0be53b 100644 --- a/cmake/FindPybind11.cmake +++ b/cmake/FindPybind11.cmake @@ -40,11 +40,7 @@ function(basic_pybind11_add_module target_name) set(exclude_from_all EXCLUDE_FROM_ALL) endif() - if(CUDA_FOUND) - cuda_add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) - else() - add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) - endif() + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) # This sets various properties (python include dirs) and links to python libs target_include_directories(${target_name} PRIVATE ${PYTHON_INCLUDE_DIRS}) diff --git a/cmake/UseCython.cmake b/cmake/UseCython.cmake deleted file mode 100644 index a38eb65c35..0000000000 --- a/cmake/UseCython.cmake +++ /dev/null @@ -1,396 +0,0 @@ -#.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( [] -# [EMBED_MAIN] -# [C | CXX] -# [PY2 | PY3] -# [OUTPUT_VAR ]) -# -# ```` is the name of the new target, and ```` -# 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 ```` is provided, and it ends in the ".pyx" extension, then it -# is assumed to be the ````. The name of the input without the -# extension is used as the target name. If only ```` is provided, and it -# does not end in the ".pyx" extension, then the ```` is assumed to -# be ``.pyx``. -# -# The Cython include search path is amended with any entries found in the -# ``INCLUDE_DIRECTORIES`` property of the directory containing the -# ```` 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 `` -# Set the variable ```` in the parent scope to the path to the -# generated source file. By default, ```` is used as the output -# variable name. -# -# Defined variables: -# -# ```` -# 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() - diff --git a/cmake/conan_utils.cmake b/cmake/conan_utils.cmake index 9f9720a60b..1d89aed4e0 100644 --- a/cmake/conan_utils.cmake +++ b/cmake/conan_utils.cmake @@ -1,13 +1,20 @@ include(conan) +macro(_rename_conan_lib package) + add_library(AER_DEPENDENCY_PKG::${package} INTERFACE IMPORTED) + target_link_libraries(AER_DEPENDENCY_PKG::${package} PUBLIC INTERFACE CONAN_PKG::${package}) +endmacro() + macro(setup_conan) # Right now every dependency shall be static set(CONAN_OPTIONS ${CONAN_OPTIONS} "*:shared=False") set(REQUIREMENTS nlohmann_json/3.1.1 spdlog/1.5.0) + list(APPEND AER_CONAN_LIBS nlohmann_json spdlog) if(APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(REQUIREMENTS ${REQUIREMENTS} llvm-openmp/8.0.1) + list(APPEND AER_CONAN_LIBS llvm-openmp) if(SKBUILD) set(CONAN_OPTIONS ${CONAN_OPTIONS} "llvm-openmp:shared=True") endif() @@ -15,6 +22,7 @@ macro(setup_conan) if(SKBUILD) set(REQUIREMENTS ${REQUIREMENTS} muparserx/4.0.8) + list(APPEND AER_CONAN_LIBS muparserx) if(NOT MSVC) set(CONAN_OPTIONS ${CONAN_OPTIONS} "muparserx:fPIC=True") endif() @@ -22,17 +30,19 @@ macro(setup_conan) if(AER_THRUST_BACKEND AND NOT AER_THRUST_BACKEND STREQUAL "CUDA") set(REQUIREMENTS ${REQUIREMENTS} thrust/1.9.5) + list(APPEND AER_CONAN_LIBS thrust) string(TOLOWER ${AER_THRUST_BACKEND} THRUST_BACKEND) set(CONAN_OPTIONS ${CONAN_OPTIONS} "thrust:device_system=${THRUST_BACKEND}") + if(THRUST_BACKEND MATCHES "tbb") + list(APPEND AER_CONAN_LIBS tbb) + endif() endif() if(BUILD_TESTS) set(REQUIREMENTS ${REQUIREMENTS} catch2/2.12.1) + list(APPEND AER_CONAN_LIBS catch2) endif() - # Add Appleclang-12 until officially supported by Conan - conan_config_install(ITEM ${PROJECT_SOURCE_DIR}/conan_settings) - conan_cmake_run(REQUIRES ${REQUIREMENTS} OPTIONS ${CONAN_OPTIONS} ENV CONAN_CMAKE_PROGRAM=${CMAKE_COMMAND} @@ -40,4 +50,25 @@ macro(setup_conan) CMAKE_TARGETS KEEP_RPATHS BUILD missing) + + # Headers includes + if(AER_THRUST_BACKEND AND NOT AER_THRUST_BACKEND STREQUAL "CUDA") + set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${CONAN_INCLUDE_DIRS_THRUST}) + endif() + + # Reassign targets from CONAN_PKG to AER_DEPENDENCY_PKG + foreach(CONAN_LIB ${AER_CONAN_LIBS}) + _rename_conan_lib(${CONAN_LIB}) + endforeach() + + if(APPLE) + set(OPENMP_FOUND TRUE) + if(NOT SKBUILD) + set(AER_LIBRARIES ${AER_LIBRARIES} AER_DEPENDENCY_PKG::llvm-openmp) + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CONAN_CXX_FLAGS_LLVM-OPENMP}") + set(AER_SIMULATOR_CPP_EXTERNAL_LIBS ${AER_SIMULATOR_CPP_EXTERNAL_LIBS} ${CONAN_INCLUDE_DIRS_LLVM-OPENMP}) + set(BACKEND_REDIST_DEPS ${BACKEND_REDIST_DEPS} "${CONAN_LIB_DIRS_LLVM-OPENMP}/libomp.dylib") + endif() + endif() endmacro() diff --git a/cmake/cython_utils.cmake b/cmake/cython_utils.cmake deleted file mode 100644 index 32370ef08c..0000000000 --- a/cmake/cython_utils.cmake +++ /dev/null @@ -1,128 +0,0 @@ -find_package(PythonExtensions REQUIRED) -find_package(Cython REQUIRED) -find_package(PythonLibs REQUIRED) -set(Python_EXECUTABLE ${PYTHON_EXECUTABLE}) -find_package(Python COMPONENTS NumPy) -if(NOT Python_NumPy_FOUND) - find_package(NumPy) - set(Python_NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIRS}) -endif() - -# Variables for input user data: -# -# CYTHON_USER_INCLUDE_DIRS: -# - For Cython modules that need to import some header file not in the paths, example: -# set(CYTHON_USER_INCLUDE_DIRS "/opt/my/include") -# CYTHON_USER_LIB_DIRS: -# - For Cython modules that need to link with external libs, example: -# set(CYTHON_USER_LIB_DIRS "/opt/my/lib") -# CYTHON_INSTALL_DIR: -# - Where to install the resulting shared libraries -# set(CYTHON_INSTALL_DIR "/opt/my/lib") - - -# Set default values -set(CYTHON_USER_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) -unset(CYTHON_USER_LIB_DIRS) -set(CYTHON_INSTALL_DIR "qiskit/providers/aer/backends") - -function(add_cython_module module) - add_cython_target(${module} ${module}.pyx CXX) - - # Avoid warnings in cython cpp generated code - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set_source_files_properties(${module}.cxx PROPERTIES COMPILE_FLAGS -Wno-everything) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set_source_files_properties(${module}.cxx PROPERTIES COMPILE_FLAGS -w) - elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set_source_files_properties(${module}.cxx PROPERTIES COMPILE_FLAGS /w) - endif() - - set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) - cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) - if(ARG_MODULE AND ARG_SHARED) - message(FATAL_ERROR "Can't be both MODULE and SHARED") - elseif(ARG_SHARED) - set(lib_type SHARED) - else() - set(lib_type MODULE) - endif() - - if(ARG_EXCLUDE_FROM_ALL) - set(exclude_from_all EXCLUDE_FROM_ALL) - endif() - - if(CUDA_FOUND) - cuda_add_library(${module} ${lib_type} ${exclude_from_all} ${module} ${ARG_UNPARSED_ARGUMENTS}) - set_source_files_properties(${module} PROPERTIES - CUDA_SOURCE_PROPERTY_FORMAT OBJ) - else() - add_library(${module} ${lib_type} ${exclude_from_all} ${module} ${ARG_UNPARSED_ARGUMENTS}) - endif() - - - # We only need to pass the linter once, as the codebase is the same for - # all controllers - # add_linter(target) - target_include_directories(${module} PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR}) - target_include_directories(${module} PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS}) - target_include_directories(${module} PRIVATE ${PYTHON_INCLUDE_DIRS}) - target_include_directories(${module} PRIVATE ${Python_NumPy_INCLUDE_DIRS}) - target_include_directories(${module} PRIVATE ${CYTHON_USER_INCLUDE_DIRS}) - - target_link_libraries(${module} ${AER_LIBRARIES} ${CYTHON_USER_LIB_DIRS}) - - if(WIN32 OR CYGWIN) - # Link against the Python shared library on Windows - target_link_libraries(${module} ${PYTHON_LIBRARIES}) - elseif(APPLE) - # It's quite common to have multiple copies of the same Python version - # installed on one's system. E.g.: one copy from the OS and another copy - # that's statically linked into an application like Blender or Maya. - # If we link our plugin library against the OS Python here and import it - # into Blender or Maya later on, this will cause segfaults when multiple - # conflicting Python instances are active at the same time (even when they - # are of the same version). - - # Windows is not affected by this issue since it handles DLL imports - # differently. The solution for Linux and Mac OS is simple: we just don't - # link against the Python library. The resulting shared library will have - # missing symbols, but that's perfectly fine -- they will be resolved at - # import time. - # Set some general flags - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(AER_LINKER_FLAGS "${AER_LINKER_FLAGS} -undefined dynamic_lookup") - else() - # -flat_namespace linker flag is needed otherwise dynamic symbol resolution doesn't work as expected with GCC. - # Symbols with the same name exist in different .so, so the loader just takes the first one it finds, - # which is usually the one from the first .so loaded. - # See: Two-Leve namespace symbol resolution - set(AER_LINKER_FLAGS "${AER_LINKER_FLAGS} -undefined dynamic_lookup -flat_namespace") - endif() - if(ARG_SHARED) - set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) - endif() - endif() - - set_target_properties(${module} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") - set_target_properties(${module} PROPERTIES SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") - set_target_properties(${module} PROPERTIES - CXX_STANDARD 14 - LINKER_LANGUAGE CXX) - # Warning: Do not merge PROPERTIES when one of the variables can be empty, it breaks - # the rest of the properties so they are not properly added. - set_target_properties(${module} PROPERTIES LINK_FLAGS ${AER_LINKER_FLAGS}) - set_target_properties(${module} PROPERTIES COMPILE_FLAGS ${AER_COMPILER_FLAGS}) - target_compile_definitions(${module} PRIVATE ${AER_COMPILER_DEFINITIONS}) - - python_extension_module(${module} - FORWARD_DECL_MODULES_VAR fdecl_module_list) - - python_modules_header(modules - FORWARD_DECL_MODULES_LIST ${fdecl_module_list}) - - include_directories(${modules_INCLUDE_DIRS}) - - # TODO Where to put the target files - install(TARGETS ${module} LIBRARY DESTINATION ${CYTHON_INSTALL_DIR}) -endfunction() diff --git a/cmake/dependency_utils.cmake b/cmake/dependency_utils.cmake new file mode 100644 index 0000000000..0952da0a2e --- /dev/null +++ b/cmake/dependency_utils.cmake @@ -0,0 +1,64 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 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. + + +macro(setup_dependencies) + # Defines AER_DEPENDENCY_PKG alias which refers to either conan-provided or system libraries. + if(DISABLE_CONAN) + _use_system_libraries() + else() + include(conan_utils) + setup_conan() + endif() +endmacro() + +macro(_use_system_libraries) + # Use system libraries + _import_aer_system_dependency(nlohmann_json 3.1.1) + _import_aer_system_dependency(spdlog 1.5.0) + + if(SKBUILD) + _import_aer_system_dependency(muparserx 4.0.8) + endif() + + if(AER_THRUST_BACKEND AND NOT AER_THRUST_BACKEND STREQUAL "CUDA") + string(TOLOWER ${AER_THRUST_BACKEND} THRUST_BACKEND) + _import_aer_system_dependency(Thrust 1.9.5) + endif() + + if(BUILD_TESTS) + _import_aer_system_dependency(Catch2 2.12.1) + endif() + + if(APPLE) + # Fix linking. See https://stackoverflow.com/questions/54068035 + link_directories(/usr/local/lib) #brew + link_directories(/opt/local/lib) #ports + endif() +endmacro() + +macro(_import_aer_system_dependency package version) + # Arguments: + # package: name of package to search for using find_package() + # version: version of package to search for + + find_package(${package} ${version} EXACT QUIET) + if(NOT ${package}_FOUND) + message(STATUS "${package} ${version} NOT found! Looking for any other version available.") + find_package(${package} REQUIRED) + message(STATUS "${package} version found: ${${package}_VERSION}. WARNING: This version may not work!!!") + endif() + string(TOLOWER ${package} PACKAGE_LOWER) # Conan use lowercase for every lib + add_library(AER_DEPENDENCY_PKG::${PACKAGE_LOWER} INTERFACE IMPORTED) + target_link_libraries(AER_DEPENDENCY_PKG::${PACKAGE_LOWER} PUBLIC INTERFACE ${package}) + message(STATUS "Using system-provided ${PACKAGE_LOWER} library") +endmacro() diff --git a/cmake/nvcc_add_compiler_options.cmake b/cmake/nvcc_add_compiler_options.cmake new file mode 100644 index 0000000000..c943fe4543 --- /dev/null +++ b/cmake/nvcc_add_compiler_options.cmake @@ -0,0 +1,13 @@ +function(nvcc_add_compiler_options_list inList outVarName) + set(L "${inList}") + list(TRANSFORM L PREPEND " --compiler-options ") + string(REPLACE ";" " " TMP ${L}) + set(${outVarName} ${TMP} PARENT_SCOPE) +endfunction() + +function(nvcc_add_compiler_options inStr outVarName) + string(REPLACE " " ";" L "${inStr}") + list(TRANSFORM L PREPEND " --compiler-options ") + string(REPLACE ";" " " TMP ${L}) + set(${outVarName} ${TMP} PARENT_SCOPE) +endfunction() diff --git a/conan_settings/settings.yml b/conan_settings/settings.yml deleted file mode 100644 index 67fdaa7213..0000000000 --- a/conan_settings/settings.yml +++ /dev/null @@ -1,98 +0,0 @@ - -# Only for cross building, 'os_build/arch_build' is the system that runs Conan -os_build: [Windows, WindowsStore, Linux, Macos, FreeBSD, SunOS, AIX] -arch_build: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, sh4le] - -# Only for building cross compilation tools, 'os_target/arch_target' is the system for -# which the tools generate code -os_target: [Windows, Linux, Macos, Android, iOS, watchOS, tvOS, FreeBSD, SunOS, AIX, Arduino, Neutrino] -arch_target: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le] - -# Rest of the settings are "host" settings: -# - For native building/cross building: Where the library/program will run. -# - For building cross compilation tools: Where the cross compiler will run. -os: - Windows: - subsystem: [None, cygwin, msys, msys2, wsl] - WindowsStore: - version: ["8.1", "10.0"] - WindowsCE: - platform: ANY - version: ["5.0", "6.0", "7.0", "8.0"] - Linux: - Macos: - version: [None, "10.6", "10.7", "10.8", "10.9", "10.10", "10.11", "10.12", "10.13", "10.14", "10.15"] - Android: - api_level: ANY - iOS: - version: ["7.0", "7.1", "8.0", "8.1", "8.2", "8.3", "9.0", "9.1", "9.2", "9.3", "10.0", "10.1", "10.2", "10.3", "11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4", "13.0", "13.1", "13.2", "13.3", "13.4", "13.5", "13.6"] - watchOS: - version: ["4.0", "4.1", "4.2", "4.3", "5.0", "5.1", "5.2", "5.3", "6.0", "6.1"] - tvOS: - version: ["11.0", "11.1", "11.2", "11.3", "11.4", "12.0", "12.1", "12.2", "12.3", "12.4", "13.0"] - FreeBSD: - SunOS: - AIX: - Arduino: - board: ANY - Emscripten: - Neutrino: - version: ["6.4", "6.5", "6.6", "7.0", "7.1"] -arch: [x86, x86_64, ppc32be, ppc32, ppc64le, ppc64, armv4, armv4i, armv5el, armv5hf, armv6, armv7, armv7hf, armv7s, armv7k, armv8, armv8_32, armv8.3, sparc, sparcv9, mips, mips64, avr, s390, s390x, asm.js, wasm, sh4le] -compiler: - sun-cc: - version: ["5.10", "5.11", "5.12", "5.13", "5.14", "5.15"] - threads: [None, posix] - libcxx: [libCstd, libstdcxx, libstlport, libstdc++] - gcc: &gcc - version: ["4.1", "4.4", "4.5", "4.6", "4.7", "4.8", "4.9", - "5", "5.1", "5.2", "5.3", "5.4", "5.5", - "6", "6.1", "6.2", "6.3", "6.4", "6.5", - "7", "7.1", "7.2", "7.3", "7.4", "7.5", - "8", "8.1", "8.2", "8.3", "8.4", - "9", "9.1", "9.2", "9.3", - "10", "10.1"] - libcxx: [libstdc++, libstdc++11] - threads: [None, posix, win32] # Windows MinGW - exception: [None, dwarf2, sjlj, seh] # Windows MinGW - cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20] - Visual Studio: &visual_studio - runtime: [MD, MT, MTd, MDd] - version: ["8", "9", "10", "11", "12", "14", "15", "16"] - toolset: [None, v90, v100, v110, v110_xp, v120, v120_xp, - v140, v140_xp, v140_clang_c2, LLVM-vs2012, LLVM-vs2012_xp, - LLVM-vs2013, LLVM-vs2013_xp, LLVM-vs2014, LLVM-vs2014_xp, - LLVM-vs2017, LLVM-vs2017_xp, v141, v141_xp, v141_clang_c2, v142, - llvm, ClangCL] - cppstd: [None, 14, 17, 20] - clang: - version: ["3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "3.9", "4.0", - "5.0", "6.0", "7.0", "7.1", - "8", "9", "10"] - libcxx: [None, libstdc++, libstdc++11, libc++, c++_shared, c++_static] - cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20] - runtime: [None, MD, MT, MTd, MDd] - apple-clang: &apple_clang - version: ["5.0", "5.1", "6.0", "6.1", "7.0", "7.3", "8.0", "8.1", "9.0", "9.1", "10.0", "11.0", "12.0"] - libcxx: [libstdc++, libc++] - cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20] - intel: - version: ["11", "12", "13", "14", "15", "16", "17", "18", "19", "19.1"] - base: - gcc: - <<: *gcc - threads: [None] - exception: [None] - Visual Studio: - <<: *visual_studio - apple-clang: - <<: *apple_clang - qcc: - version: ["4.4", "5.4", "8.3"] - libcxx: [cxx, gpp, cpp, cpp-ne, accp, acpp-ne, ecpp, ecpp-ne] - cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17] - -build_type: [None, Debug, Release, RelWithDebInfo, MinSizeRel] - - -cppstd: [None, 98, gnu98, 11, gnu11, 14, gnu14, 17, gnu17, 20, gnu20] # Deprecated, use compiler.cppstd diff --git a/constraints.txt b/constraints.txt index bd559cec81..2740ed6cd8 100644 --- a/constraints.txt +++ b/constraints.txt @@ -1,5 +1,6 @@ pylint==2.4.4 astroid==2.3.3 six>1.10,<=1.14 -numpy>=1.16.3,<1.19.0;python_version<"3.6" -scipy>=1.0,<1.5.0;python_version<"3.6" +numpy>=1.16.3 +scipy>=1.0 +cvxpy<1.1.8 diff --git a/contrib/standalone/qasm_simulator.cpp b/contrib/standalone/qasm_simulator.cpp index a821b95edf..7456932641 100755 --- a/contrib/standalone/qasm_simulator.cpp +++ b/contrib/standalone/qasm_simulator.cpp @@ -16,11 +16,8 @@ #include #include #include - -#ifdef _MSC_VER -#include -#elif defined(__GNUC__) -#include +#ifdef AER_MPI +#include #endif #include "version.hpp" @@ -98,6 +95,14 @@ int main(int argc, char **argv) { int indent = 4; json_t qobj; json_t config; + int myrank=0,nprocs=1; + +#ifdef AER_MPI + int prov; + 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 @@ -149,7 +154,9 @@ int main(int argc, char **argv) { // Initialize simulator AER::Simulator::QasmController sim; auto result = sim.execute(qobj).to_json(); - out << result.dump(4) << std::endl; + if(myrank == 0){ + out << result.dump(4) << std::endl; + } // Check if execution was successful. bool success = false; @@ -157,6 +164,9 @@ int main(int argc, char **argv) { 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 @@ -165,8 +175,14 @@ int main(int argc, char **argv) { 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 diff --git a/docs/apidocs/aer.rst b/docs/apidocs/aer.rst index fe662fbb13..2d3f204327 100644 --- a/docs/apidocs/aer.rst +++ b/docs/apidocs/aer.rst @@ -8,7 +8,8 @@ Qiskit Aer API Reference :maxdepth: 1 aer_provider - aer_extensions + aer_library aer_noise aer_pulse aer_utils + aer_extensions diff --git a/docs/apidocs/aer_library.rst b/docs/apidocs/aer_library.rst new file mode 100644 index 0000000000..1dc50571d4 --- /dev/null +++ b/docs/apidocs/aer_library.rst @@ -0,0 +1,6 @@ +.. _aer-library: + +.. automodule:: qiskit.providers.aer.library + :no-members: + :no-inherited-members: + :no-special-members: \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index acd01254f4..9d9f3b146e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,7 +46,7 @@ # The short X.Y version version = '' # The full version, including alpha/beta/rc tags -release = '0.7.0' +release = '0.8.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 04b39f580f..04b0f998d9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,13 @@ [build-system] -requires = ["setuptools", "wheel", "conan>=1.22.2", "scikit-build", "cmake!=3.17.1,!=3.17.0", "ninja", "pybind11>2.4", "Cython>0.27.1"] +requires = [ + "setuptools", + "wheel", + "conan>=1.31.2", + "scikit-build", + "cmake!=3.17.1,!=3.17.0", + "ninja", + "pybind11>2.6", + "oldest-supported-numpy; python_version>'3.7' or platform_machine=='aarch64' or platform_python_implementation=='PyPy'", + "numpy==1.16.3; python_version<='3.7' and platform_machine!='aarch64' or platform_python_implementation=='PyPy'", +] +build-backend = "setuptools.build_meta" diff --git a/qiskit/providers/aer/VERSION.txt b/qiskit/providers/aer/VERSION.txt index faef31a435..a3df0a6959 100644 --- a/qiskit/providers/aer/VERSION.txt +++ b/qiskit/providers/aer/VERSION.txt @@ -1 +1 @@ -0.7.0 +0.8.0 diff --git a/qiskit/providers/aer/__init__.py b/qiskit/providers/aer/__init__.py index 871e2c2f90..ee562147ee 100644 --- a/qiskit/providers/aer/__init__.py +++ b/qiskit/providers/aer/__init__.py @@ -65,6 +65,7 @@ from .aerjob import AerJob from .aererror import AerError from .backends import * +from . import library from . import pulse from . import noise from . import utils diff --git a/qiskit/providers/aer/aerjob.py b/qiskit/providers/aer/aerjob.py index 275544e03b..5912f2b505 100644 --- a/qiskit/providers/aer/aerjob.py +++ b/qiskit/providers/aer/aerjob.py @@ -14,6 +14,7 @@ """This module implements the job class used for AerBackend objects.""" +import warnings from concurrent import futures import logging import functools @@ -55,6 +56,10 @@ def __init__(self, backend, job_id, fn, qobj, *args): super().__init__(backend, job_id) self._fn = fn self._qobj = qobj + if args: + warnings.warn('Using *args for AerJob is deprecated. All backend' + ' options should be contained in the assembled Qobj.', + DeprecationWarning) self._args = args self._future = None @@ -70,7 +75,9 @@ def submit(self): if self._future is not None: raise JobError("We have already submitted the job!") - self._future = self._executor.submit(self._fn, self._job_id, self._qobj, + self._future = self._executor.submit(self._fn, + self._qobj, + self._job_id, *self._args) @requires_submit diff --git a/qiskit/providers/aer/aerprovider.py b/qiskit/providers/aer/aerprovider.py index bf73347ca0..31f1f900b5 100644 --- a/qiskit/providers/aer/aerprovider.py +++ b/qiskit/providers/aer/aerprovider.py @@ -29,22 +29,26 @@ class AerProvider(BaseProvider): def __init__(self, *args, **kwargs): super().__init__(args, kwargs) - # Populate the list of Aer simulator providers. - self._backends = [QasmSimulator(provider=self), - StatevectorSimulator(provider=self), - UnitarySimulator(provider=self), - PulseSimulator(provider=self)] + # Populate the list of Aer simulator backends. + self._backends = [ + ('qasm_simulator', QasmSimulator), + ('statevector_simulator', StatevectorSimulator), + ('unitary_simulator', UnitarySimulator), + ('pulse_simulator', PulseSimulator) + ] def get_backend(self, name=None, **kwargs): return super().get_backend(name=name, **kwargs) def backends(self, name=None, filters=None, **kwargs): # pylint: disable=arguments-differ - backends = self._backends - if name: - backends = [backend for backend in backends if backend.name() == name] - - return filter_backends(backends, filters=filters, **kwargs) + # Instantiate a new backend instance so if config options + # are set they will only last as long as that backend object exists + backends = [] + for backend_name, backend_cls in self._backends: + if name is None or backend_name == name: + backends.append(backend_cls(provider=self)) + return filter_backends(backends, filters=filters) def __str__(self): return 'AerProvider' diff --git a/qiskit/providers/aer/backends/aerbackend.py b/qiskit/providers/aer/backends/aerbackend.py index c3abcc6069..b2198cc6a1 100644 --- a/qiskit/providers/aer/backends/aerbackend.py +++ b/qiskit/providers/aer/backends/aerbackend.py @@ -9,34 +9,30 @@ # 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. - """ Qiskit Aer qasm simulator backend. """ +import copy import json import logging import datetime -import os import time import uuid +import warnings +from abc import ABC, abstractmethod from numpy import ndarray from qiskit.providers import BaseBackend from qiskit.providers.models import BackendStatus -from qiskit.qobj import validate_qobj_against_schema from qiskit.result import Result -from qiskit.util import local_hardware_info from ..aerjob import AerJob +from ..aererror import AerError # Logger logger = logging.getLogger(__name__) -# Location where we put external libraries that will be loaded at runtime -# by the simulator extension -LIBRARY_DIR = os.path.dirname(__file__) - class AerJSONEncoder(json.JSONEncoder): """ @@ -59,10 +55,15 @@ def default(self, obj): return super().default(obj) -class AerBackend(BaseBackend): +class AerBackend(BaseBackend, ABC): """Qiskit Aer Backend class.""" - - def __init__(self, controller, configuration, provider=None): + def __init__(self, + configuration, + properties=None, + defaults=None, + available_methods=None, + backend_options=None, + provider=None): """Aer class for backends. This method should initialize the module and its configuration, and @@ -70,142 +71,339 @@ def __init__(self, controller, configuration, provider=None): not available. Args: - controller (function): Aer controller to be executed - configuration (BackendConfiguration): backend configuration - provider (BaseProvider): provider responsible for this backend + configuration (BackendConfiguration): backend configuration. + properties (BackendProperties or None): Optional, backend properties. + defaults (PulseDefaults or None): Optional, backend pulse defaults. + available_methods (list or None): Optional, the available simulation methods + if backend supports multiple methods. + provider (BaseProvider): Optional, provider responsible for this backend. + backend_options (dict or None): Optional set custom backend options. Raises: - FileNotFoundError if backend executable is not available. AerError: if there is no name in the configuration """ + # Init configuration and provider in BaseBackend + configuration.simulator = True super().__init__(configuration, provider=provider) - self._controller = controller + + # Initialize backend properties and pulse defaults. + self._properties = properties + self._defaults = defaults + + # Custom configuration, properties, and pulse defaults which will store + # any configured modifications to the base simulator values. + self._custom_configuration = None + self._custom_properties = None + self._custom_defaults = None + + # Set available methods + self._available_methods = [] if available_methods is None else available_methods + + # Set custom configured options from backend_options dictionary + self._options = {} + if backend_options is not None: + for key, val in backend_options.items(): + self._set_option(key, val) # pylint: disable=arguments-differ - def run(self, qobj, backend_options=None, noise_model=None, validate=False): + def run(self, + qobj, + backend_options=None, # DEPRECATED + validate=False, + **run_options): """Run a qobj on the backend. Args: qobj (QasmQobj): The Qobj to be executed. - backend_options (dict or None): dictionary of backend options + backend_options (dict or None): DEPRECATED dictionary of backend options for the execution (default: None). - noise_model (NoiseModel or None): noise model to use for - simulation (default: None). - validate (bool): validate the Qobj before running (default: True). + validate (bool): validate the Qobj before running (default: False). + run_options (kwargs): additional run time backend options. Returns: AerJob: The simulation job. Additional Information: + * kwarg options specified in ``run_options`` will temporarily override + any set options of the same name for the current run. + * The entries in the ``backend_options`` will be combined with the ``Qobj.config`` dictionary with the values of entries in - ``backend_options`` taking precedence. - - * If present the ``noise_model`` will override any noise model - specified in the ``backend_options`` or ``Qobj.config``. + ``backend_options`` taking precedence. This kwarg is deprecated + and direct kwarg's should be used for options to pass them to + ``run_options``. """ + # DEPRECATED + if backend_options is not None: + warnings.warn( + 'Using `backend_options` kwarg has been deprecated as of' + ' qiskit-aer 0.7.0 and will be removed no earlier than 3' + ' months from that release date. Runtime backend options' + ' should now be added directly using kwargs for each option.', + DeprecationWarning, + stacklevel=3) + + # Add backend options to the Job qobj + qobj = self._format_qobj( + qobj, backend_options=backend_options, **run_options) + + # Optional validation + if validate: + self._validate(qobj) + # Submit job job_id = str(uuid.uuid4()) - aer_job = AerJob(self, job_id, self._run_job, qobj, - backend_options, noise_model, validate) + aer_job = AerJob(self, job_id, self._run, qobj) aer_job.submit() return aer_job + def configuration(self): + """Return the simulator backend configuration. + + Returns: + BackendConfiguration: the configuration for the backend. + """ + if self._custom_configuration is not None: + return self._custom_configuration + return self._configuration + + def properties(self): + """Return the simulator backend properties if set. + + Returns: + BackendProperties: The backend properties or ``None`` if the + backend does not have properties set. + """ + if self._custom_properties is not None: + return self._custom_properties + return self._properties + + def defaults(self): + """Return the simulator backend pulse defaults. + + Returns: + PulseDefaults: The backend pulse defaults or ``None`` if the + backend does not support pulse. + """ + if self._custom_defaults is not None: + return self._custom_defaults + return self._defaults + + @property + def options(self): + """Return the current simulator options""" + return self._options + + def set_options(self, **backend_options): + """Set the simulator options""" + for key, val in backend_options.items(): + self._set_option(key, val) + + def clear_options(self): + """Reset the simulator options to default values.""" + self._custom_configuration = None + self._custom_properties = None + self._custom_defaults = None + self._options = {} + + def available_methods(self): + """Return the available simulation methods.""" + return self._available_methods + def status(self): """Return backend status. Returns: BackendStatus: the status of the backend. """ - return BackendStatus(backend_name=self.name(), - backend_version=self.configuration().backend_version, - operational=True, - pending_jobs=0, - status_msg='') + return BackendStatus( + backend_name=self.name(), + backend_version=self.configuration().backend_version, + operational=True, + pending_jobs=0, + status_msg='') def _run_job(self, job_id, qobj, backend_options, noise_model, validate): """Run a qobj job""" - start = time.time() + warnings.warn( + 'The `_run_job` method has been deprecated. Use `_run` instead.', + DeprecationWarning) if validate: - validate_qobj_against_schema(qobj) - self._validate(qobj, backend_options, noise_model) - output = self._controller(self._format_qobj(qobj, backend_options, noise_model)) - end = time.time() - return Result.from_dict(self._format_results(job_id, output, end - start)) - - def _format_qobj(self, qobj, backend_options, noise_model): - """Format qobj string for qiskit aer controller""" - # Convert qobj to dict so as to avoid editing original - output = qobj.to_dict() - # Add new parameters to config from backend options - config = output["config"] - - if backend_options is not None: - for key, val in backend_options.items(): - config[key] = val if not hasattr(val, 'to_dict') else val.to_dict() + warnings.warn( + 'The validate arg of `_run_job` has been removed. Use ' + 'validate=True in the `run` method instead.', + DeprecationWarning) # Add default OpenMP options - if 'statevector_parallel_threshold' not in config and hasattr( + if 'statevector_parallel_threshold' not in backend_options and hasattr( self, '_statevector_parallel_threshold'): - config['statevector_parallel_threshold'] = self._statevector_parallel_threshold + backend_options['statevector_parallel_threshold'] = self._statevector_parallel_threshold # Add default fusion options - if 'fusion_threshold' not in config: - if 'gpu' in config.get('method', '') and hasattr(self, '_fusion_threshold_gpu'): - # Set GPU fusion threshold - config['fusion_threshold'] = self._fusion_threshold_gpu - elif hasattr(self, '_fusion_threshold'): - # Set CPU fusion threshold - config['fusion_threshold'] = self._fusion_threshold - - # Add noise model to config - if noise_model is not None: - config["noise_model"] = noise_model - - # Add runtime config - if 'library_dir' not in config: - config['library_dir'] = LIBRARY_DIR - if "max_memory_mb" not in config: - max_memory_mb = int(local_hardware_info()['memory'] * 1024 / 2) - config['max_memory_mb'] = max_memory_mb - - self._validate_config(config) - # Return output - return output - - def _validate_config(self, config): - # sanity checks on config- should be removed upon fixing of assemble w.r.t. backend_options - if 'backend_options' in config: - if isinstance(config['backend_options'], dict): - for key, val in config['backend_options'].items(): - if hasattr(val, 'to_dict'): - config['backend_options'][key] = val.to_dict() - elif not isinstance(config['backend_options'], list): - raise ValueError("config[backend_options] must be a dict or list!") - # Double-check noise_model is a dict type - if 'noise_model' in config and not isinstance(config["noise_model"], dict): - if hasattr(config["noise_model"], 'to_dict'): - config["noise_model"] = config["noise_model"].to_dict() - else: - raise ValueError("noise_model must be a dict : " + str(type(config["noise_model"]))) - - def _format_results(self, job_id, output, time_taken): - """Construct Result object from simulator output.""" + attr_postfix = '_gpu' if 'gpu' in backend_options.get('method', '') else '' + if 'fusion_threshold' not in backend_options and hasattr( + self, f'_fusion_threshold{attr_postfix}'): + # Set fusion threshold + backend_options['fusion_threshold'] = getattr(self, f'_fusion_threshold{attr_postfix}') + + # The new function swaps positional args qobj and job id so we do a + # type check to swap them back + if not isinstance(job_id, str) and isinstance(qobj, str): + job_id, qobj = qobj, job_id + run_qobj = self._format_qobj(qobj, backend_options=backend_options, + noise_model=noise_model) + return self._run(run_qobj, job_id) + + def _run(self, qobj, job_id=''): + """Run a job""" + # Start timer + start = time.time() + + # Run simulation + output = self._execute(qobj) + + # 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 - output["time_taken"] = time_taken - return output - def _validate(self, qobj, backend_options, noise_model): - """Validate the qobj, backend_options, noise_model for the backend""" + # Add execution time + output["time_taken"] = time.time() - start + return Result.from_dict(output) + + @abstractmethod + def _execute(self, qobj): + """Execute a qobj on the backend. + + Args: + qobj (QasmQobj or PulseQobj): simulator input. + + Returns: + dict: return a dictionary of results. + """ pass + def _validate(self, qobj): + """Validate the qobj for the backend""" + pass + + def _set_option(self, key, value): + """Special handling for setting backend options. + + This method should be extended by sub classes to + update special option values. + + Args: + key (str): key to update + value (any): value to update. + + Raises: + AerError: if key is 'method' and val isn't in available methods. + """ + # Check for key in configuration, properties, and defaults + # If the key requires modification of one of these fields a copy + # will be generated that can store the modified values without + # changing the original object + if hasattr(self._configuration, key): + self._set_configuration_option(key, value) + return + + if hasattr(self._properties, key): + self._set_properties_option(key, value) + return + + if hasattr(self._defaults, key): + self._set_defaults_option(key, value) + return + + # If key is method, we validate it is one of the available methods + if key == 'method' and value not in self._available_methods: + raise AerError("Invalid simulation method {}. Available methods" + " are: {}".format(value, self._available_methods)) + + # Add all other options to the options dict + # TODO: in the future this could be replaced with an options class + # for the simulators like configuration/properties to show all + # available options + if value is not None: + # Only add an option if its value is not None + self._options[key] = value + elif key in self._options: + # If setting an existing option to None remove it from options dict + self._options.pop(key) + + def _set_configuration_option(self, key, value): + """Special handling for setting backend configuration options.""" + if self._custom_configuration is None: + self._custom_configuration = copy.copy(self._configuration) + setattr(self._custom_configuration, key, value) + + def _set_properties_option(self, key, value): + """Special handling for setting backend properties options.""" + if self._custom_properties is None: + self._custom_properties = copy.copy(self._properties) + setattr(self._custom_properties, key, value) + + def _set_defaults_option(self, key, value): + """Special handling for setting backend defaults options.""" + if self._custom_defaults is None: + self._custom_defaults = copy.copy(self._defaults) + setattr(self._custom_defaults, key, value) + + def _format_qobj(self, qobj, + backend_options=None, # DEPRECATED + **run_options): + """Return execution sim config dict from backend options.""" + # Add options to qobj config overriding any existing fields + config = qobj.config + + # Add options + for key, val in self.options.items(): + setattr(config, key, val) + + # DEPRECATED backend options + if backend_options is not None: + for key, val in backend_options.items(): + setattr(config, key, val) + + # Override with run-time options + for key, val in run_options.items(): + setattr(config, key, val) + + return qobj + + def _run_config( + self, + backend_options=None, # DEPRECATED + **run_options): + """Return execution sim config dict from backend options.""" + # Get sim config + run_config = self._options.copy() + + # DEPRECATED backend options + if backend_options is not None: + for key, val in backend_options.items(): + run_config[key] = val + + # Override with run-time options + for key, val in run_options.items(): + run_config[key] = val + return run_config + def __repr__(self): - """Official string representation of an AerBackend.""" - display = "{}('{}')".format(self.__class__.__name__, self.name()) - provider = self.provider() - if provider is not None: - display = display + " from {}()".format(provider) - return "<" + display + ">" + """String representation of an AerBackend.""" + display = "backend_name='{}'".format(self.name()) + if self.provider(): + display += ', provider={}()'.format(self.provider()) + for key, val in self.options.items(): + display += ',\n {}={}'.format(key, repr(val)) + return '{}(\n{})'.format(self.__class__.__name__, display) diff --git a/qiskit/providers/aer/backends/backend_utils.py b/qiskit/providers/aer/backends/backend_utils.py new file mode 100644 index 0000000000..6d080cc465 --- /dev/null +++ b/qiskit/providers/aer/backends/backend_utils.py @@ -0,0 +1,69 @@ +# 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=invalid-name +""" +Qiskit Aer simulator backend utils +""" +import os +from math import log2 +from qiskit.util import local_hardware_info +from qiskit.circuit import QuantumCircuit +from qiskit.compiler import assemble + +# Available system memory +SYSTEM_MEMORY_GB = local_hardware_info()['memory'] + +# Max number of qubits for complex double statevector +# given available system memory +MAX_QUBITS_STATEVECTOR = int(log2(SYSTEM_MEMORY_GB * (1024**3) / 16)) + +# Location where we put external libraries that will be +# loaded at runtime by the simulator extension +LIBRARY_DIR = os.path.dirname(__file__) + + +def cpp_execute(controller, qobj): + """Execute qobj_dict on C++ controller wrapper""" + # Convert qobj to dict + qobj_dict = qobj.to_dict() + + # Convert noise model to dict + noise_model = qobj_dict['config'].pop('noise_model', None) + if noise_model is not None: + if not isinstance(noise_model, dict): + noise_model = noise_model.to_dict() + qobj_dict['config']['noise_model'] = noise_model + + # Location where we put external libraries that will be + # loaded at runtime by the simulator extension + qobj_dict['config']['library_dir'] = LIBRARY_DIR + + return controller(qobj_dict) + + +def available_methods(controller, methods): + """Check available simulation methods by running a dummy circuit.""" + # Test methods are available using the controller + dummy_circ = QuantumCircuit(1) + dummy_circ.i(0) + + valid_methods = [] + for method in methods: + qobj = assemble(dummy_circ, + optimization_level=0, + shots=1, + method=method) + result = cpp_execute(controller, qobj) + if result.get('success', False): + valid_methods.append(method) + return valid_methods diff --git a/qiskit/providers/aer/backends/pulse_simulator.py b/qiskit/providers/aer/backends/pulse_simulator.py index b97bc4b350..5dc9bdabae 100644 --- a/qiskit/providers/aer/backends/pulse_simulator.py +++ b/qiskit/providers/aer/backends/pulse_simulator.py @@ -10,25 +10,43 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. # pylint: disable=arguments-differ, missing-return-type-doc - """ Qiskit Aer pulse simulator backend. """ -import uuid -import time -import datetime +import copy import logging +from warnings import warn from numpy import inf -from qiskit.result import Result + from qiskit.providers.models import BackendConfiguration, PulseDefaults -from .aerbackend import AerBackend -from ..aerjob import AerJob + from ..version import __version__ +from ..aererror import AerError from ..pulse.controllers.pulse_controller import pulse_controller +from ..pulse.system_models.pulse_system_model import PulseSystemModel +from .aerbackend import AerBackend logger = logging.getLogger(__name__) +DEFAULT_CONFIGURATION = { + 'backend_name': 'pulse_simulator', + 'backend_version': __version__, + 'n_qubits': 20, + 'coupling_map': None, + 'url': 'https://github.com/Qiskit/qiskit-aer', + 'simulator': True, + 'meas_levels': [1, 2], + 'local': True, + 'conditional': True, + 'open_pulse': True, + 'memory': False, + 'max_shots': int(1e6), + 'description': 'A Pulse-based Hamiltonian simulator for Pulse Qobj files', + 'gates': [], + 'basis_gates': [] +} + class PulseSimulator(AerBackend): r"""Pulse schedule simulator backend. @@ -38,12 +56,26 @@ class PulseSimulator(AerBackend): physical system specified by :class:`~qiskit.providers.aer.pulse.PulseSystemModel` objects. Results are returned in the same format as when jobs are submitted to actual devices. - **Example** + **Examples** + + The minimal information a ``PulseSimulator`` needs to simulate is a + :class:`~qiskit.providers.aer.pulse.PulseSystemModel`, which can be supplied either by + setting the backend option before calling ``run``, e.g.: + + .. code-block:: python + + backend_sim = qiskit.providers.aer.PulseSimulator() + + # Set the pulse system model for the simulator + backend_sim.set_options(system_model=system_model) - To use the simulator, first :func:`~qiskit.assemble` a :class:`PulseQobj` object - from a list of pulse :class:`~qiskit.Schedule` objects, using ``backend=PulseSimulator()``. - Call the simulator with the :class:`PulseQobj` and a - :class:`~qiskit.providers.aer.pulse.PulseSystemModel` object representing the physical system. + # Assemble schedules using PulseSimulator as the backend + pulse_qobj = assemble(schedules, backend=backend_sim) + + # Run simulation + results = backend_sim.run(pulse_qobj) + + or by supplying the system model at runtime, e.g.: .. code-block:: python @@ -53,7 +85,23 @@ class PulseSimulator(AerBackend): pulse_qobj = assemble(schedules, backend=backend_sim) # Run simulation on a PulseSystemModel object - results = backend_sim.run(pulse_qobj, system_model) + results = backend_sim.run(pulse_qobj, system_model=system_model) + + Alternatively, an instance of the ``PulseSimulator`` may be further configured to contain more + information present in a real backend. The simplest way to do this is to instantiate the + ``PulseSimulator`` from a real backend: + + .. code-block:: python + + armonk_sim = qiskit.providers.aer.PulseSimulator.from_backend(FakeArmonk()) + pulse_qobj = assemble(schedules, backend=armonk_sim) + armonk_sim.run(pulse_qobj) + + In the above example, the ``PulseSimulator`` copies all configuration and default data from + ``FakeArmonk()``, and as such has the same affect as ``FakeArmonk()`` when passed as an + argument to ``assemble``. Furthermore it constructs a + :class:`~qiskit.providers.aer.pulse.PulseSystemModel` from the model details in the supplied + backend, which is then used in simulation. **Supported PulseQobj parameters** @@ -77,91 +125,205 @@ class PulseSimulator(AerBackend): **Other options** - :meth:`PulseSimulator.run` takes an additional ``dict`` argument ``backend_options`` for - customization. Accepted keys: + Additional valid keyword arguments for ``run()``: * ``'solver_options'``: A ``dict`` for solver options. Accepted keys are ``'atol'``, ``'rtol'``, ``'nsteps'``, ``'max_step'``, ``'num_cpus'``, ``'norm_tol'``, and ``'norm_steps'``. """ + def __init__(self, + configuration=None, + properties=None, + defaults=None, + provider=None, + **backend_options): + + if configuration is None: + configuration = BackendConfiguration.from_dict( + DEFAULT_CONFIGURATION) + else: + configuration = copy.copy(configuration) + configuration.meas_levels = self._meas_levels(configuration.meas_levels) + + if defaults is None: + defaults = PulseDefaults(qubit_freq_est=[inf], + meas_freq_est=[inf], + buffer=0, + cmd_def=[], + pulse_library=[]) + + super().__init__(configuration, + properties=properties, + defaults=defaults, + provider=provider, + backend_options=backend_options) - DEFAULT_CONFIGURATION = { - 'backend_name': 'pulse_simulator', - 'backend_version': __version__, - 'n_qubits': 20, - 'coupling_map': None, - 'url': 'https://github.com/Qiskit/qiskit-aer', - 'simulator': True, - 'meas_levels': [0, 1, 2], - 'local': True, - 'conditional': True, - 'open_pulse': True, - 'memory': False, - 'max_shots': int(1e6), - 'description': 'A pulse-based Hamiltonian simulator for Pulse Qobj files', - 'gates': [], - 'basis_gates': [] - } - - def __init__(self, configuration=None, provider=None): - - # purpose of defaults is to pass assemble checks - self._defaults = PulseDefaults(qubit_freq_est=[inf], - meas_freq_est=[inf], - buffer=0, - cmd_def=[], - pulse_library=[]) - super().__init__(self, - BackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), - provider=provider) - - def run(self, qobj, system_model, backend_options=None, validate=False): - """Run a qobj on system_model. + # Set up default system model + subsystem_list = backend_options.get('subsystem_list', None) + if backend_options.get('system_model') is None: + if hasattr(configuration, 'hamiltonian'): + system_model = PulseSystemModel.from_config( + configuration, subsystem_list) + self._set_system_model(system_model) + + # pylint: disable=arguments-differ, missing-param-doc + def run(self, + qobj, + *args, + backend_options=None, # DEPRECATED + validate=True, + **run_options): + """Run a qobj on the backend. Args: - qobj (PulseQobj): Qobj for pulse Schedules to run - system_model (PulseSystemModel): Physical model to run simulation on - backend_options (dict): Other options - validate (bool): Flag for validation checks + qobj (QasmQobj): The Qobj to be executed. + backend_options (dict or None): DEPRECATED dictionary of backend options + for the execution (default: None). + validate (bool): validate the Qobj before running (default: True). + run_options (kwargs): additional run time backend options. Returns: - Result: results of simulation + AerJob: The simulation job. + + Additional Information: + * kwarg options specified in ``run_options`` will override options + of the same kwarg specified in the simulator options, the + ``backend_options`` and the ``Qobj.config``. + + * The entries in the ``backend_options`` will be combined with + the ``Qobj.config`` dictionary with the values of entries in + ``backend_options`` taking precedence. This kwarg is deprecated + and direct kwarg's should be used for options to pass them to + ``run_options``. """ - # Submit job - job_id = str(uuid.uuid4()) - aer_job = AerJob(self, job_id, self._run_job, qobj, system_model, - backend_options, validate) - aer_job.submit() - return aer_job - - def _run_job(self, job_id, qobj, system_model, backend_options, validate): - """Run a qobj job""" - start = time.time() - if validate: - self._validate(qobj, backend_options, noise_model=None) - # Send problem specification to pulse_controller and get results - results = pulse_controller(qobj, system_model, backend_options) - end = time.time() - return self._format_results(job_id, results, end - start, qobj.qobj_id) - - def _format_results(self, job_id, results, time_taken, qobj_id): - """Construct Result object from simulator output.""" - # Add result metadata - output = {} - output['qobj_id'] = qobj_id - output['results'] = results - output['success'] = True - output["job_id"] = job_id - output["date"] = datetime.datetime.now().isoformat() - output["backend_name"] = self.name() - output["backend_version"] = self.configuration().backend_version - output["time_taken"] = time_taken - return Result.from_dict(output) - - def defaults(self): - """Return defaults. + if args: + if isinstance(args[0], PulseSystemModel): + warn( + 'Passing `system_model` as a positional argument to' + ' `PulseSimulator.run` has been deprecated as of' + ' qiskit-aer 0.7.0 and will be removed no earlier than 3' + ' months from that release date. Pass `system_model` as a kwarg' + ' `system_model=model` instead.', + DeprecationWarning, + stacklevel=3) + run_options['system_model'] = args[0] + if len(args) > 1: + backend_options = args[1] + if len(args) > 2: + validate = args[3] + elif isinstance(args[0], bool): + validate = args[0] + if len(args) > 1: + backend_options = args[1] + return super().run(qobj, backend_options=backend_options, validate=validate, + **run_options) + + @property + def _system_model(self): + return self._options.get('system_model') + + @classmethod + def from_backend(cls, backend, **options): + """Initialize simulator from backend.""" + configuration = copy.copy(backend.configuration()) + defaults = copy.copy(backend.defaults()) + properties = copy.copy(backend.properties()) + + backend_name = 'pulse_simulator({})'.format(configuration.backend_name) + description = 'A Pulse-based simulator configured from the backend: ' + description += configuration.backend_name + + sim = cls(configuration=configuration, + properties=properties, + defaults=defaults, + backend_name=backend_name, + description=description, + **options) + return sim + + def _execute(self, qobj): + """Execute a qobj on the backend. + + Args: + qobj (PulseQobj): simulator input. Returns: - PulseDefaults: object for passing assemble. + dict: return a dictionary of results. """ - return self._defaults + qobj.config.qubit_freq_est = self.defaults().qubit_freq_est + return pulse_controller(qobj) + + def _set_option(self, key, value): + """Set pulse simulation options and update backend.""" + if key == 'meas_levels': + self._set_configuration_option(key, self._meas_levels(value)) + return + + # Handle cases that require updating two places + if key in ['dt', 'u_channel_lo']: + self._set_configuration_option(key, value) + if self._system_model is not None: + setattr(self._system_model, key, value) + return + + if key == 'hamiltonian': + # if option is hamiltonian, set in configuration and reconstruct pulse system model + subsystem_list = self._options.get('subsystem_list', None) + system_model = PulseSystemModel.from_config(self.configuration(), + subsystem_list) + super()._set_option('system_model', system_model) + self._set_configuration_option(key, value) + return + + # if system model is specified directly + if key == 'system_model': + if hasattr(self.configuration(), 'hamiltonian'): + warn('Specifying both a configuration with a Hamiltonian and a ' + 'system model may result in inconsistencies.') + # Set config dt and u_channel_lo to system model values + self._set_system_model(value) + return + + # Set all other options from AerBackend + super()._set_option(key, value) + + def _set_system_model(self, system_model): + """Set system model option""" + self._set_configuration_option( + 'dt', getattr(system_model, 'dt', [])) + self._set_configuration_option( + 'u_channel_lo', getattr(system_model, 'u_channel_lo', [])) + super()._set_option('system_model', system_model) + + def _validate(self, qobj): + """Validation of qobj. + + Ensures that exactly one Acquire instruction is present in each + schedule. Checks SystemModel is in qobj config + """ + if getattr(qobj.config, 'system_model', None) is None: + raise AerError("PulseSimulator requires a system model to run.") + + for exp in qobj.experiments: + num_acquires = 0 + for instruction in exp.instructions: + if instruction.name == 'acquire': + num_acquires += 1 + + if num_acquires > 1: + raise AerError("PulseSimulator does not support multiple Acquire " + "instructions in a single schedule.") + + if num_acquires == 0: + raise AerError("PulseSimulator requires at least one Acquire " + "instruction per schedule.") + + @staticmethod + def _meas_levels(meas_levels): + """Function for setting meas_levels in a pulse simulator configuration.""" + if 0 in meas_levels: + warn('Measurement level 0 not supported in pulse simulator.') + tmp = copy.copy(meas_levels) + tmp.remove(0) + return tmp + return meas_levels diff --git a/qiskit/providers/aer/backends/qasm_simulator.py b/qiskit/providers/aer/backends/qasm_simulator.py index 5f960a3cce..adf49d8f7b 100644 --- a/qiskit/providers/aer/backends/qasm_simulator.py +++ b/qiskit/providers/aer/backends/qasm_simulator.py @@ -13,14 +13,16 @@ Qiskit Aer qasm simulator backend. """ +import copy import logging -from math import log2 -from qiskit.util import local_hardware_info from qiskit.providers.models import QasmBackendConfiguration + +from ..version import __version__ from .aerbackend import AerBackend -# pylint: disable=import-error +from .backend_utils import (cpp_execute, available_methods, + MAX_QUBITS_STATEVECTOR) +# pylint: disable=import-error, no-name-in-module from .controller_wrappers import qasm_controller_execute -from ..version import __version__ logger = logging.getLogger(__name__) @@ -29,34 +31,41 @@ class QasmSimulator(AerBackend): """ Noisy quantum circuit simulator backend. + **Configurable Options** + The `QasmSimulator` supports multiple simulation methods and - configurable options for each simulation method. These options are - specified in a dictionary which may be passed to the simulator using - the ``backend_options`` kwarg for :meth:`QasmSimulator.run` or - ``qiskit.execute``. + configurable options for each simulation method. These may be set using the + appropriate kwargs during initialization. They can also be set of updated + using the :meth:`set_options` method. - The default behavior chooses a simulation method automatically based on - the input circuit and noise model. A custom method can be specified using the - ``"method"`` field in ``backend_options`` as illustrated in the following - example. Available simulation methods and additional backend options are - listed below. + Run-time options may also be specified as kwargs using the :meth:`run` method. + These will not be stored in the backend and will only apply to that execution. + They will also override any previously set options. - **Example** + For example, to configure a density matrix simulator with a custom noise + model to use for every execution .. code-block:: python - backend = QasmSimulator() - backend_options = {"method": "statevector"} + noise_model = NoiseModel.from_backend(backend) + backend = QasmSimulator(method='density_matrix', + noise_model=noise_model) - # Circuit execution - job = execute(circuits, backend, backend_options=backend_options) + **Simulating an IBMQ Backend** + + The simulator can be automatically configured to mimic an IBMQ backend using + the :meth:`from_backend` method. This will configure the simulator to use the + basic device :class:`NoiseModel` for that backend, and the same basis gates + and coupling map. + + .. code-block:: python - # Qobj execution - job = backend.run(qobj, backend_options=backend_options) + backend = QasmSimulator.from_backend(backend) - **Simulation method** + **Simulation Method Option** - Available simulation methods are: + The simulation method is set using the ``method`` kwarg. + Supported simulation methods are: * ``"statevector"``: A dense statevector simulation that can sample measurement outcomes from *ideal* circuits with all measurements at @@ -95,36 +104,33 @@ class QasmSimulator(AerBackend): automatically for each circuit based on the circuit instructions, number of qubits, and noise model. - **Backend options** + **Additional Backend Options** - The following backend options may be used with in the - ``backend_options`` kwarg for :meth:`QasmSimulator.run` or - ``qiskit.execute``: + The following simulator specific backend options are supported - * ``"method"`` (str): Set the simulation method. See backend methods - for additional information (Default: "automatic"). + * ``method`` (str): Set the simulation method (Default: ``"automatic"``). - * ``"precision"`` (str): Set the floating point precision for - certain simulation methods to either "single" or "double" - precision (default: "double"). + * ``precision`` (str): Set the floating point precision for + certain simulation methods to either ``"single"`` or ``"double"`` + precision (default: ``"double"``). - * ``"zero_threshold"`` (double): Sets the threshold for truncating + * ``zero_threshold`` (double): Sets the threshold for truncating small values to zero in the result data (Default: 1e-10). - * ``"validation_threshold"`` (double): Sets the threshold for checking + * ``validation_threshold`` (double): Sets the threshold for checking if initial states are valid (Default: 1e-8). - * ``"max_parallel_threads"`` (int): Sets the maximum number of CPU + * ``max_parallel_threads`` (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). - * ``"max_parallel_experiments"`` (int): Sets the maximum number of + * ``max_parallel_experiments`` (int): Sets the maximum number of qobj 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). - * ``"max_parallel_shots"`` (int): Sets the maximum number of + * ``max_parallel_shots`` (int): Sets the maximum number of shots that may be executed in parallel during each experiment execution, up to the max_parallel_threads value. If set to 1 parallel shot execution will be disabled. If set to 0 the @@ -132,18 +138,18 @@ class QasmSimulator(AerBackend): Note that this cannot be enabled at the same time as parallel experiment execution (Default: 0). - * ``"max_memory_mb"`` (int): Sets the maximum size of memory + * ``max_memory_mb`` (int): Sets the maximum size of memory to store a state vector. If a state vector needs more, an error is thrown. In general, a state vector of n-qubits uses 2^n complex values (16 Bytes). If set to 0, the maximum will be automatically - set to half the system memory size (Default: 0). + set to the system memory size (Default: 0). - * ``"optimize_ideal_threshold"`` (int): Sets the qubit threshold for + * ``optimize_ideal_threshold`` (int): Sets the qubit threshold for applying circuit optimization passes on ideal circuits. Passes include gate fusion and truncation of unused qubits (Default: 5). - * ``"optimize_noise_threshold"`` (int): Sets the qubit threshold for + * ``optimize_noise_threshold`` (int): Sets the qubit threshold for applying circuit optimization passes on ideal circuits. Passes include gate fusion and truncation of unused qubits (Default: 12). @@ -151,7 +157,7 @@ class QasmSimulator(AerBackend): These backend options only apply when using the ``"statevector"`` simulation method: - * ``"statevector_parallel_threshold"`` (int): Sets the threshold that + * ``statevector_parallel_threshold`` (int): Sets the threshold that the number of qubits must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit or shot execution is enabled @@ -159,7 +165,7 @@ class QasmSimulator(AerBackend): max_parallel_threads. Note that setting this too low can reduce performance (Default: 14). - * ``"statevector_sample_measure_opt"`` (int): Sets the threshold that + * ``statevector_sample_measure_opt`` (int): Sets the threshold that the number of qubits must be greater than to enable a large qubit optimized implementation of measurement sampling. Note that setting this two low can reduce performance (Default: 10) @@ -167,7 +173,7 @@ class QasmSimulator(AerBackend): These backend options only apply when using the ``"stabilizer"`` simulation method: - * ``"stabilizer_max_snapshot_probabilities"`` (int): set the maximum + * ``stabilizer_max_snapshot_probabilities`` (int): set the maximum qubit number for the `~qiskit.providers.aer.extensions.SnapshotProbabilities` instruction (Default: 32). @@ -175,14 +181,32 @@ class QasmSimulator(AerBackend): These backend options only apply when using the ``"extended_stabilizer"`` simulation method: - * ``"extended_stabilizer_measure_sampling"`` (bool): Enable measure - sampling optimization on supported circuits. This prevents the - simulator from re-running the measure monte-carlo step for each - shot. Enabling measure sampling may reduce accuracy of the - measurement counts if the output distribution is strongly - peaked (Default: False). - - * ``"extended_stabilizer_mixing_time"`` (int): Set how long the + * ``extended_stabilizer_sampling_methid`` (string): Choose how to simulate + measurements on qubits. The performance of the simulator depends + significantly on this choice. In the following, let n be the number of + qubits in the circuit, m the number of qubits measured, and S be the + number of shots. (Default: resampled_metropolis) + + * ``"metropolis"``: Use a Monte-Carlo method to sample many output + strings from the simulator at once. To be accurate, this method + requires that all the possible output strings have a non-zero + probability. It will give inaccurate results on cases where + the circuit has many zero-probability outcomes. + This method has an overall runtime that scales as n^{2} + (S-1)n. + + * ``"resampled_metropolis"``: A variant of the metropolis method, + where the Monte-Carlo method is reinitialised for every shot. This + gives better results for circuits where some outcomes have zero + probability, but will still fail if the output distribution + is sparse. The overall runtime scales as Sn^{2}. + + * ``"norm_estimation"``: An alternative sampling method using + random state inner products to estimate outcome probabilites. This + method requires twice as much memory, and significantly longer + runtimes, but gives accurate results on circuits with sparse + output distributions. The overall runtime scales as Sn^{3}m^{3}. + + * ``extended_stabilizer_metropolis_mixing_time`` (int): Set how long the monte-carlo method runs before performing measurements. If the output distribution is strongly peaked, this can be decreased alongside setting extended_stabilizer_disable_measurement_opt @@ -193,49 +217,65 @@ class QasmSimulator(AerBackend): smaller error needs more memory and computational time (Default: 0.05). - * ``"extended_stabilizer_norm_estimation_samples"`` (int): Number of - samples used to compute the correct normalization for a - statevector snapshot (Default: 100). + * ``extended_stabilizer_norm_estimation_samples`` (int): The default number + of samples for the norm estimation sampler. The method will use the + default, or 4m^{2} samples where m is the number of qubits to be + measured, whichever is larger (Default: 100). + + * ``extended_stabilizer_norm_estimation_repetitions`` (int): The number + of times to repeat the norm estimation. The median of these reptitions + is used to estimate and sample output strings (Default: 3). - * ``"extended_stabilizer_parallel_threshold"`` (int): Set the minimum + * ``extended_stabilizer_parallel_threshold`` (int): Set the minimum size of the extended stabilizer decomposition before we enable OpenMP parallelization. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads (Default: 100). - These backend options apply in circuit optimization passes: - - * ``"fusion_enable"`` (bool): Enable fusion optimization in circuit - optimization passes [Default: True] - * ``"fusion_verbose"`` (bool): Output gates generated in fusion optimization - into metadata [Default: False] - * ``"fusion_max_qubit"`` (int): Maximum number of qubits for a operation generated - in a fusion optimization [Default: 5] - * ``"fusion_threshold"`` (int): Threshold that number of qubits must be greater - than or equal to enable fusion optimization [Default: 20] + * ``extended_stabilizer_probabilities_snapshot_samples`` (int): If using + the metropolis or resampled_metropolis sampling method, set the number of + samples used to estimate probabilities in a probabilities snapshot + (Default: 3000). These backend options only apply when using the ``"matrix_product_state"`` simulation method: - * ``"matrix_product_state_max_bond_dimension"`` (int): Sets a limit + * ``matrix_product_state_max_bond_dimension`` (int): Sets a limit on the number of Schmidt coefficients retained at the end of the svd algorithm. Coefficients beyond this limit will be discarded. (Default: None, i.e., no limit on the bond dimension). - * ``"matrix_product_state_truncation_threshold"`` (double): + * ``matrix_product_state_truncation_threshold`` (double): Discard the smallest coefficients for which the sum of their squares is smaller than this threshold. (Default: 1e-16). - """ + * ``mps_sample_measure_algorithm`` (str): + Choose which algorithm to use for ``"sample_measure"``. ``"mps_probabilities"`` + means all state probabilities are computed and measurements are based on them. + It is more efficient for a large number of shots, small number of qubits and low + entanglement. ``"mps_apply_measure"`` creates a copy of the mps structure and + makes a measurement on it. It is more effients for a small number of shots, high + number of qubits, and low entanglement. If the user does not specify the algorithm, + a heuristic algorithm is used to select between the two algorithms. + (Default: "mps_heuristic"). - MAX_QUBIT_MEMORY = int( - log2(local_hardware_info()['memory'] * (1024**3) / 16)) + These backend options apply in circuit optimization passes: - DEFAULT_CONFIGURATION = { + * ``fusion_enable`` (bool): Enable fusion optimization in circuit + optimization passes [Default: True] + * ``fusion_verbose`` (bool): Output gates generated in fusion optimization + into metadata [Default: False] + * ``fusion_max_qubit`` (int): Maximum number of qubits for a operation generated + in a fusion optimization [Default: 5] + * ``fusion_threshold`` (int): Threshold that number of qubits must be greater + than or equal to enable fusion optimization [Default: 14] + """ + + _DEFAULT_CONFIGURATION = { 'backend_name': 'qasm_simulator', 'backend_version': __version__, - 'n_qubits': MAX_QUBIT_MEMORY, + 'n_qubits': MAX_QUBITS_STATEVECTOR, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, @@ -243,27 +283,162 @@ class QasmSimulator(AerBackend): 'open_pulse': False, 'memory': True, 'max_shots': int(1e6), - 'description': 'A C++ simulator with realistic noise for QASM Qobj files', + 'description': 'A C++ QasmQobj simulator with noise', 'coupling_map': None, - 'basis_gates': [ - 'u1', 'u2', 'u3', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', 'y', - 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', 'cy', - 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', 'rzz', - 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', 'mcp', - 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', 'mcr', - 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'initialize', - 'kraus', 'superop', 'roerror', 'delay' - ], + 'basis_gates': sorted([ + 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', + 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', + 'cy', 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', + 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', + 'mcphase', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', + 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', + 'initialize', 'delay', 'pauli', 'mcx_gray', + # Custom instructions + 'kraus', 'roerror', 'snapshot', 'save_expval', 'save_expval_var', + 'save_probabilities', 'save_probabilities_dict', 'save_state', + 'save_density_matrix', 'save_statevector', + 'save_amplitudes', 'save_amplitudes_sq', 'save_stabilizer' + ]), + 'custom_instructions': sorted([ + 'roerror', 'kraus', 'snapshot', 'save_expval', 'save_expval_var', + 'save_probabilities', 'save_probabilities_dict', + 'save_state', 'save_density_matrix', 'save_statevector', + 'save_amplitudes', 'save_amplitudes_sq', 'save_stabilizer']), 'gates': [] } - def __init__(self, configuration=None, provider=None): - super().__init__( - qasm_controller_execute(), - QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), - provider=provider) + _AVAILABLE_METHODS = None + + def __init__(self, + configuration=None, + properties=None, + provider=None, + **backend_options): + + self._controller = qasm_controller_execute() + + # Update available methods for class + if QasmSimulator._AVAILABLE_METHODS is None: + QasmSimulator._AVAILABLE_METHODS = available_methods( + self._controller, [ + 'automatic', 'statevector', 'statevector_gpu', + 'statevector_thrust', 'density_matrix', + 'density_matrix_gpu', 'density_matrix_thrust', + 'stabilizer', 'matrix_product_state', 'extended_stabilizer' + ]) + + if configuration is None: + configuration = self._method_configuration() + elif not hasattr(configuration, 'custom_instructions'): + configuration.custom_instructions = [] + + super().__init__(configuration, + properties=properties, + available_methods=QasmSimulator._AVAILABLE_METHODS, + provider=provider, + backend_options=backend_options) + + @classmethod + def from_backend(cls, backend, **options): + """Initialize simulator from backend.""" + # pylint: disable=import-outside-toplevel + # Avoid cyclic import + from ..noise.noise_model import NoiseModel + + # Get configuration and properties from backend + configuration = copy.copy(backend.configuration()) + properties = copy.copy(backend.properties()) + + # Customize configuration name + name = configuration.backend_name + configuration.backend_name = 'qasm_simulator({})'.format(name) + + # Basis gates and Custom instructions + basis_gates = set(configuration.basis_gates) + custom_instr = cls._DEFAULT_CONFIGURATION['custom_instructions'] + configuration.custom_instructions = sorted(custom_instr) + configuration.basis_gates = sorted(basis_gates.union(custom_instr)) + + # Use automatic noise model if none is provided + if 'noise_model' not in options: + noise_model = NoiseModel.from_backend(backend) + if not noise_model.is_ideal(): + options['noise_model'] = noise_model + + # Initialize simulator + sim = cls(configuration=configuration, + properties=properties, + **options) + return sim + + def _execute(self, qobj): + """Execute a qobj on the backend. + + Args: + qobj (QasmQobj): simulator input. + + Returns: + dict: return a dictionary of results. + """ + return cpp_execute(self._controller, qobj) + + def _set_option(self, key, value): + """Set the simulation method and update configuration. - def _validate(self, qobj, backend_options, noise_model): + Args: + key (str): key to update + value (any): value to update. + + Raises: + AerError: if key is 'method' and val isn't in available methods. + """ + # If key is noise_model we also change the simulator config + # to use the noise_model basis gates by default. + if key == 'noise_model' and value is not None: + basis_gates = set(self._configuration.basis_gates) # Method basis gates + intersection = basis_gates.intersection(value.basis_gates) + self._check_basis_gates(basis_gates, value.basis_gates, intersection) + self._set_option('basis_gates', intersection) + + # If key is method we update our configurations + if key == 'method': + method_config = self._method_configuration(value) + self._set_configuration_option('description', method_config.description) + self._set_configuration_option('backend_name', method_config.backend_name) + self._set_configuration_option('n_qubits', method_config.n_qubits) + self._set_configuration_option('custom_instructions', + method_config.custom_instructions) + # Take intersection of method basis gates with configuration + # basis gates and noise model basis gates + basis_gates = set(self._configuration.basis_gates) + basis_gates = basis_gates.intersection(method_config.basis_gates) + if 'noise_model' in self.options: + noise_gates = self.options['noise_model'].basis_gates + intersection = basis_gates.intersection(noise_gates) + self._check_basis_gates(basis_gates, noise_gates, intersection) + basis_gates = intersection + self._set_option('basis_gates', basis_gates) + + # When setting basis gates always append custom simulator instructions for + # the current method + if key == 'basis_gates': + value = sorted(set(value).union(self.configuration().custom_instructions)) + + # Set all other options from AerBackend + super()._set_option(key, value) + + @staticmethod + def _check_basis_gates(method_gates, noise_gates, intersection=None): + """Check if intersection of method basis gates and noise basis gates is empty""" + if intersection is None: + intersection = set(method_gates).intersection(noise_gates) + if not intersection: + logger.warning( + "The intersection of NoiseModel basis gates (%s) and " + "backend basis gates (%s) is empty", + sorted(noise_gates), sorted(method_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. @@ -285,3 +460,72 @@ def _validate(self, qobj, backend_options, noise_model): 'No measurements in circuit "%s": ' 'count data will return all zeros.', experiment.header.name) + + @staticmethod + def _method_configuration(method=None): + """Return QasmBackendConfiguration.""" + # Default configuration + config = QasmBackendConfiguration.from_dict( + QasmSimulator._DEFAULT_CONFIGURATION) + + # Statevector methods + if method in ['statevector', 'statevector_gpu', 'statevector_thrust']: + config.description = 'A C++ QasmQobj statevector simulator with noise' + + # Density Matrix methods + elif method in [ + 'density_matrix', 'density_matrix_gpu', 'density_matrix_thrust' + ]: + config.n_qubits = config.n_qubits // 2 + config.description = 'A C++ QasmQobj density matrix simulator with noise' + config.custom_instructions = sorted([ + 'roerror', 'snapshot', 'kraus', 'superop', 'save_expval', 'save_expval_var', + 'save_probabilities', 'save_probabilities_dict', 'save_density_matrix', + 'save_amplitudes_sq', 'save_state']) + config.basis_gates = sorted([ + 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', + 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', + 'cy', 'cz', 'cp', 'cu1', 'rxx', 'ryy', 'rzz', 'rzx', 'ccx', + 'unitary', 'diagonal', 'delay', 'pauli', + ] + config.custom_instructions) + + # Matrix product state method + elif method == 'matrix_product_state': + config.description = 'A C++ QasmQobj matrix product state simulator with noise' + config.custom_instructions = sorted([ + 'roerror', 'snapshot', 'kraus', 'save_expval', 'save_expval_var', + 'save_probabilities', 'save_probabilities_dict', + 'save_density_matrix', 'save_statevector', + 'save_amplitudes', 'save_amplitudes_sq']) + config.basis_gates = sorted([ + 'u1', 'u2', 'u3', 'u', 'p', 'cp', 'cx', 'cy', 'cz', 'id', 'x', 'y', 'z', 'h', 's', + 'sdg', 'sx', 't', 'tdg', 'swap', 'ccx', 'unitary', 'roerror', 'delay', + 'r', 'rx', 'ry', 'rz', 'rxx', 'ryy', 'rzz', 'rzx', 'csx', 'cswap', 'diagonal', + 'initialize' + ] + config.custom_instructions) + + # Stabilizer method + elif method == 'stabilizer': + config.n_qubits = 5000 # TODO: estimate from memory + config.description = 'A C++ QasmQobj Clifford stabilizer simulator with noise' + config.custom_instructions = sorted([ + 'roerror', 'snapshot', 'save_expval', 'save_expval_var', + 'save_probabilities', 'save_probabilities_dict', + 'save_amplitudes_sq', 'save_stabilizer', 'save_state']) + config.basis_gates = sorted([ + 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', 'cx', 'cy', 'cz', + 'swap', 'delay', + ] + config.custom_instructions) + + # Extended stabilizer method + elif method == 'extended_stabilizer': + config.n_qubits = 63 # TODO: estimate from memory + config.description = 'A C++ QasmQobj ranked stabilizer simulator with noise' + config.custom_instructions = sorted(['roerror', 'snapshot', 'save_statevector', + 'save_expval', 'save_expval_var']) + config.basis_gates = sorted([ + 'cx', 'cz', 'id', 'x', 'y', 'z', 'h', 's', 'sdg', 'sx', + 'swap', 'u0', 't', 'tdg', 'u1', 'p', 'ccx', 'ccz', 'delay' + ] + config.custom_instructions) + + return config diff --git a/qiskit/providers/aer/backends/statevector_simulator.py b/qiskit/providers/aer/backends/statevector_simulator.py index cf2b97bdaa..7791db02b8 100644 --- a/qiskit/providers/aer/backends/statevector_simulator.py +++ b/qiskit/providers/aer/backends/statevector_simulator.py @@ -9,20 +9,21 @@ # 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. - """ Qiskit Aer statevector simulator backend. """ import logging -from math import log2 from qiskit.util import local_hardware_info from qiskit.providers.models import QasmBackendConfiguration -from .aerbackend import AerBackend -# pylint: disable=import-error -from .controller_wrappers import statevector_controller_execute + from ..aererror import AerError from ..version import __version__ +from .aerbackend import AerBackend +from .backend_utils import (cpp_execute, available_methods, + MAX_QUBITS_STATEVECTOR) +# pylint: disable=import-error, no-name-in-module +from .controller_wrappers import statevector_controller_execute # Logger logger = logging.getLogger(__name__) @@ -31,49 +32,81 @@ class StatevectorSimulator(AerBackend): """Ideal quantum circuit statevector simulator - **Backend options** + **Configurable Options** + + The `StatevectorSimulator` supports CPU and GPU simulation methods and + additional configurable options. These may be set using the appropriate kwargs + during initialization. They can also be set of updated using the + :meth:`set_options` method. + + Run-time options may also be specified as kwargs using the :meth:`run` method. + These will not be stored in the backend and will only apply to that execution. + They will also override any previously set options. + + For example, to configure a a single-precision simulator - The following backend options may be used with in the - ``backend_options`` kwarg for :meth:`StatevectorSimulator.run` or - ``qiskit.execute``. + .. code-block:: python - * ``"zero_threshold"`` (double): Sets the threshold for truncating + backend = StatevectorSimulator(precision='single') + + **Backend Options** + + The following configurable backend options are supported + + * ``method`` (str): Set the simulation method supported methods are + ``"statevector"`` for CPU simulation, and ``"statevector_gpu"`` + for GPU simulation (Default: ``"statevector"``). + + * ``precision`` (str): Set the floating point precision for + certain simulation methods to either ``"single"`` or ``"double"`` + precision (default: ``"double"``). + + * ``zero_threshold`` (double): Sets the threshold for truncating small values to zero in the result data (Default: 1e-10). - * ``"validation_threshold"`` (double): Sets the threshold for checking + * ``validation_threshold`` (double): Sets the threshold for checking if the initial statevector is valid (Default: 1e-8). - * ``"max_parallel_threads"`` (int): Sets the maximum number of CPU + * ``max_parallel_threads`` (int): Sets the maximum number of CPU cores used by OpenMP for parallelization. If set to 0 the maximum will be set to the number of CPU cores (Default: 0). - * ``"max_parallel_experiments"`` (int): Sets the maximum number of + * ``max_parallel_experiments`` (int): Sets the maximum number of qobj 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). - * ``"max_memory_mb"`` (int): Sets the maximum size of memory + * ``max_memory_mb`` (int): Sets the maximum size of memory to store a state vector. If a state vector needs more, an error is thrown. In general, a state vector of n-qubits uses 2^n complex values (16 Bytes). If set to 0, the maximum will be automatically - set to half the system memory size (Default: 0). + set to the system memory size (Default: 0). - * ``"statevector_parallel_threshold"`` (int): Sets the threshold that + * ``statevector_parallel_threshold`` (int): Sets the threshold that "n_qubits" must be greater than to enable OpenMP parallelization for matrix multiplication during execution of an experiment. If parallel circuit or shot execution is enabled this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 14). - """ - MAX_QUBIT_MEMORY = int(log2(local_hardware_info()['memory'] * (1024 ** 3) / 16)) + These backend options apply in circuit optimization passes: + + * ``fusion_enable`` (bool): Enable fusion optimization in circuit + optimization passes [Default: True] + * ``fusion_verbose`` (bool): Output gates generated in fusion optimization + into metadata [Default: False] + * ``fusion_max_qubit`` (int): Maximum number of qubits for a operation generated + in a fusion optimization [Default: 5] + * ``fusion_threshold`` (int): Threshold that number of qubits must be greater + than or equal to enable fusion optimization [Default: 14] + """ - DEFAULT_CONFIGURATION = { + _DEFAULT_CONFIGURATION = { 'backend_name': 'statevector_simulator', 'backend_version': __version__, - 'n_qubits': MAX_QUBIT_MEMORY, + 'n_qubits': MAX_QUBITS_STATEVECTOR, 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, @@ -81,30 +114,66 @@ class StatevectorSimulator(AerBackend): 'open_pulse': False, 'memory': True, 'max_shots': int(1e6), # Note that this backend will only ever - # perform a single shot. This value is just - # so that the default shot value for execute - # will not raise an error when trying to run - # a simulation + # perform a single shot. This value is just + # so that the default shot value for execute + # will not raise an error when trying to run + # a simulation 'description': 'A C++ statevector simulator for QASM Qobj files', 'coupling_map': None, 'basis_gates': [ - 'u1', 'u2', 'u3', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', 'y', - 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', 'cy', - 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', 'rzz', - 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', 'mcp', - 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', 'mcr', - 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'initialize', - 'kraus', 'roerror', 'delay' + 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', + 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', + 'cy', 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', + 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', + 'mcp', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', + 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', + 'initialize', 'kraus', 'roerror', 'delay', 'pauli', + 'save_expval', 'save_density_matrix', 'save_statevector', + 'save_probs', 'save_probs_ket', 'save_amplitudes', + 'save_amplitudes_sq', 'save_state' ], 'gates': [] } - def __init__(self, configuration=None, provider=None): - super().__init__(statevector_controller_execute(), - QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), - provider=provider) + # Cache available methods + _AVAILABLE_METHODS = None + + def __init__(self, + configuration=None, + properties=None, + provider=None, + **backend_options): + + self._controller = statevector_controller_execute() + + if StatevectorSimulator._AVAILABLE_METHODS is None: + StatevectorSimulator._AVAILABLE_METHODS = available_methods( + self._controller, [ + 'automatic', 'statevector', 'statevector_gpu', + 'statevector_thrust' + ]) + if configuration is None: + configuration = QasmBackendConfiguration.from_dict( + StatevectorSimulator._DEFAULT_CONFIGURATION) + super().__init__( + configuration, + properties=properties, + available_methods=StatevectorSimulator._AVAILABLE_METHODS, + provider=provider, + backend_options=backend_options) + + def _execute(self, qobj): + """Execute a qobj on the backend. + + Args: + qobj (QasmQobj): simulator input. + + Returns: + dict: return a dictionary of results. + """ + return cpp_execute(self._controller, qobj) - def _validate(self, qobj, backend_options, noise_model): + def _validate(self, qobj): """Semantic validations of the qobj which cannot be done via schemas. Some of these may later move to backend schemas. @@ -112,7 +181,7 @@ def _validate(self, qobj, backend_options, noise_model): 2. Check number of qubits will fit in local memory. """ name = self.name() - if noise_model is not None: + if getattr(qobj.config, 'noise_model', None) is not None: raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits @@ -120,7 +189,8 @@ def _validate(self, qobj, backend_options, noise_model): if n_qubits > max_qubits: raise AerError( 'Number of qubits ({}) is greater than max ({}) for "{}" with {} GB system memory.' - .format(n_qubits, max_qubits, name, int(local_hardware_info()['memory']))) + .format(n_qubits, max_qubits, name, + int(local_hardware_info()['memory']))) if qobj.config.shots != 1: logger.info('"%s" only supports 1 shot. Setting shots=1.', name) @@ -129,7 +199,7 @@ def _validate(self, qobj, backend_options, noise_model): 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) + logger.info( + '"%s" only supports 1 shot. ' + 'Setting shots=1 for circuit "%s".', name, exp_name) experiment.config.shots = 1 diff --git a/qiskit/providers/aer/backends/unitary_simulator.py b/qiskit/providers/aer/backends/unitary_simulator.py index 1b6324af03..abed526b3c 100644 --- a/qiskit/providers/aer/backends/unitary_simulator.py +++ b/qiskit/providers/aer/backends/unitary_simulator.py @@ -16,15 +16,16 @@ """ import logging -from math import log2, sqrt from qiskit.util import local_hardware_info from qiskit.providers.models import QasmBackendConfiguration -from .aerbackend import AerBackend from ..aererror import AerError -# pylint: disable=import-error -from .controller_wrappers import unitary_controller_execute from ..version import __version__ +from .aerbackend import AerBackend +from .backend_utils import (cpp_execute, available_methods, + MAX_QUBITS_STATEVECTOR) +# pylint: disable=import-error, no-name-in-module +from .controller_wrappers import unitary_controller_execute # Logger logger = logging.getLogger(__name__) @@ -33,11 +34,34 @@ class UnitarySimulator(AerBackend): """Ideal quantum circuit unitary simulator. - **Backend options** + **Configurable Options** + + The `UnitarySimulator` supports CPU and GPU simulation methods and + additional configurable options. These may be set using the appropriate kwargs + during initialization. They can also be set of updated using the + :meth:`set_options` method. + + Run-time options may also be specified as kwargs using the :meth:`run` method. + These will not be stored in the backend and will only apply to that execution. + They will also override any previously set options. + + For example, to configure a a single-precision simulator + + .. code-block:: python + + backend = UnitarySimulator(precision='single') + + **Backend Options** + + The following configurable backend options are supported + + * ``method`` (str): Set the simulation method supported methods are + ``"unitary"`` for CPU simulation, and ``"untiary_gpu"`` + for GPU simulation (Default: ``"unitary"``). - The following backend options may be used with in the - ``backend_options`` kwarg for :meth:`UnitarySimulator.run` or - ``qiskit.execute``. + * ``precision`` (str): Set the floating point precision for + certain simulation methods to either ``"single"`` or ``"double"`` + precision (default: ``"double"``). * ``"initial_unitary"`` (matrix_like): Sets a custom initial unitary matrix for the simulation instead of identity (Default: None). @@ -63,7 +87,7 @@ class UnitarySimulator(AerBackend): to store a state vector. If a state vector needs more, an error is thrown. In general, a state vector of n-qubits uses 2^n complex values (16 Bytes). If set to 0, the maximum will be automatically - set to half the system memory size (Default: 0). + set to the system memory size (Default: 0). * ``"statevector_parallel_threshold"`` (int): Sets the threshold that 2 * "n_qubits" must be greater than to enable OpenMP @@ -72,17 +96,24 @@ class UnitarySimulator(AerBackend): this will only use unallocated CPU cores up to max_parallel_threads. Note that setting this too low can reduce performance (Default: 14). - """ - MAX_QUBIT_MEMORY = int( - log2(sqrt(local_hardware_info()['memory'] * (1024**3) / 16))) + These backend options apply in circuit optimization passes: + + * ``fusion_enable`` (bool): Enable fusion optimization in circuit + optimization passes [Default: True] + * ``fusion_verbose`` (bool): Output gates generated in fusion optimization + into metadata [Default: False] + * ``fusion_max_qubit`` (int): Maximum number of qubits for a operation generated + in a fusion optimization [Default: 5] + * ``fusion_threshold`` (int): Threshold that number of qubits must be greater + than or equal to enable fusion optimization [Default: 7] + """ - DEFAULT_CONFIGURATION = { + _DEFAULT_CONFIGURATION = { 'backend_name': 'unitary_simulator', 'backend_version': __version__, - 'n_qubits': MAX_QUBIT_MEMORY, - 'url': - 'https://github.com/Qiskit/qiskit-aer', + 'n_qubits': MAX_QUBITS_STATEVECTOR // 2, + 'url': 'https://github.com/Qiskit/qiskit-aer', 'simulator': True, 'local': True, 'conditional': False, @@ -96,22 +127,54 @@ class UnitarySimulator(AerBackend): 'description': 'A C++ unitary simulator for QASM Qobj files', 'coupling_map': None, 'basis_gates': [ - 'u1', 'u2', 'u3', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', 'y', - 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', 'cy', - 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', 'rzz', - 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', 'mcp', - 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', 'mcr', - 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'delay' + 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', + 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg', 'swap', 'cx', + 'cy', 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', 'ryy', + 'rzz', 'rzx', 'ccx', 'cswap', 'mcx', 'mcy', 'mcz', 'mcsx', + 'mcp', 'mcu1', 'mcu2', 'mcu3', 'mcrx', 'mcry', 'mcrz', + 'mcr', 'mcswap', 'unitary', 'diagonal', 'multiplexer', 'delay', 'pauli', + 'save_unitary', 'save_state' ], 'gates': [] } - def __init__(self, configuration=None, provider=None): - super().__init__(unitary_controller_execute(), - QasmBackendConfiguration.from_dict(self.DEFAULT_CONFIGURATION), - provider=provider) + _AVAILABLE_METHODS = None + + def __init__(self, + configuration=None, + properties=None, + provider=None, + **backend_options): + + self._controller = unitary_controller_execute() + + if UnitarySimulator._AVAILABLE_METHODS is None: + UnitarySimulator._AVAILABLE_METHODS = available_methods( + self._controller, + ['automatic', 'unitary', 'unitary_gpu', 'unitary_thrust']) + + if configuration is None: + configuration = QasmBackendConfiguration.from_dict( + UnitarySimulator._DEFAULT_CONFIGURATION) + + super().__init__(configuration, + properties=properties, + available_methods=UnitarySimulator._AVAILABLE_METHODS, + provider=provider, + backend_options=backend_options) + + def _execute(self, qobj): + """Execute a qobj on the backend. + + Args: + qobj (QasmQobj): simulator input. + + Returns: + dict: return a dictionary of results. + """ + return cpp_execute(self._controller, qobj) - def _validate(self, qobj, backend_options, noise_model): + 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 @@ -119,7 +182,7 @@ def _validate(self, qobj, backend_options, noise_model): 3. Check number of qubits will fit in local memory. """ name = self.name() - if noise_model is not None: + if getattr(qobj.config, 'noise_model', None) is not None: raise AerError("{} does not support noise.".format(name)) n_qubits = qobj.config.n_qubits diff --git a/qiskit/providers/aer/backends/wrappers/CMakeLists.txt b/qiskit/providers/aer/backends/wrappers/CMakeLists.txt index c771c1d2fa..e3cff03581 100644 --- a/qiskit/providers/aer/backends/wrappers/CMakeLists.txt +++ b/qiskit/providers/aer/backends/wrappers/CMakeLists.txt @@ -6,26 +6,37 @@ find_package(Pybind11 REQUIRED) # dependencies we can, so some of these dependencies are linked statically into our # shared library. string(REPLACE " -static " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + if(CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "AMD64") - # We build SIMD file separately, because they will be reached only if the - # machine running the code has SIMD support - set(SIMD_SOURCE_FILE "../../../../../src/simulators/statevector/qv_avx2.cpp") - if(APPLE OR UNIX) - set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "-mfma -mavx2") - elseif(MSVC) - set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "/arch:AVX2") - endif() + # We build SIMD filed separately, because they will be reached only if the + # machine running the code has SIMD support + set(SIMD_SOURCE_FILE "../../../../../src/simulators/statevector/qv_avx2.cpp") endif() -set(AER_SIMULATOR_SOURCES "bindings.cc" - "${SIMD_SOURCE_FILE}") +set(AER_SIMULATOR_SOURCES "bindings.cc" "${SIMD_SOURCE_FILE}") +basic_pybind11_add_module(controller_wrappers "${AER_SIMULATOR_SOURCES}") -if(CUDA_FOUND) - set_source_files_properties(${AER_SIMULATOR_SOURCES} PROPERTIES - CUDA_SOURCE_PROPERTY_FORMAT OBJ) -endif() +if(AER_THRUST_BACKEND STREQUAL "CUDA") + include(nvcc_add_compiler_options) + set_source_files_properties(bindings.cc PROPERTIES LANGUAGE CUDA) + set_source_files_properties(bindings.cc PROPERTIES COMPILE_FLAGS "${CUDA_NVCC_FLAGS}") + + if(DEFINED SIMD_SOURCE_FILE) + set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES LANGUAGE CUDA) + nvcc_add_compiler_options_list("${SIMD_FLAGS_LIST}" SIMD_FLAGS_OUT) + set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "${CUDA_NVCC_FLAGS} ${SIMD_FLAGS_OUT}") + endif() -basic_pybind11_add_module(controller_wrappers ${AER_SIMULATOR_SOURCES}) + string(STRIP ${AER_COMPILER_FLAGS} AER_COMPILER_FLAGS_STRIPPED) + nvcc_add_compiler_options(${AER_COMPILER_FLAGS_STRIPPED} AER_COMPILER_FLAGS_OUT) + set_target_properties(controller_wrappers PROPERTIES COMPILE_FLAGS "${AER_COMPILER_FLAGS_OUT}") +else() + if(DEFINED SIMD_SOURCE_FILE) + string(REPLACE ";" " " SIMD_FLAGS "${SIMD_FLAGS_LIST}") + set_source_files_properties(${SIMD_SOURCE_FILE} PROPERTIES COMPILE_FLAGS "${SIMD_FLAGS}") + endif() + set_target_properties(controller_wrappers PROPERTIES COMPILE_FLAGS "${AER_COMPILER_FLAGS}") +endif() target_include_directories(controller_wrappers PRIVATE ${AER_SIMULATOR_CPP_SRC_DIR} PRIVATE ${AER_SIMULATOR_CPP_EXTERNAL_LIBS}) target_link_libraries(controller_wrappers ${AER_LIBRARIES}) @@ -33,8 +44,4 @@ target_compile_definitions(controller_wrappers PRIVATE ${AER_COMPILER_DEFINITION install(TARGETS controller_wrappers LIBRARY DESTINATION qiskit/providers/aer/backends) # Install redistributable dependencies -if(APPLE) - install(FILES "${CONAN_LIB_DIRS_LLVM-OPENMP}/libomp.dylib" DESTINATION qiskit/providers/aer/backends) -elseif(WIN32 AND NOT BLAS_LIB_PATH) - install(FILES ${OPENBLAS_DLLs} DESTINATION qiskit/providers/aer/backends) -endif() +install(FILES ${BACKEND_REDIST_DEPS} DESTINATION qiskit/providers/aer/backends) diff --git a/qiskit/providers/aer/backends/wrappers/bindings.cc b/qiskit/providers/aer/backends/wrappers/bindings.cc index 637398c6b8..57a64e08e8 100644 --- a/qiskit/providers/aer/backends/wrappers/bindings.cc +++ b/qiskit/providers/aer/backends/wrappers/bindings.cc @@ -1,19 +1,20 @@ #include -#include "misc/common_macros.hpp" -#if defined(_MSC_VER) -#include -#elif defined(GNUC_AVX2) -#include + +#ifdef AER_MPI +#include #endif #include "misc/warnings.hpp" DISABLE_WARNING_PUSH #include DISABLE_WARNING_POP +#if defined(_MSC_VER) + #undef snprintf +#endif #include "framework/matrix.hpp" #include "framework/types.hpp" -#include "framework/pybind_json.hpp" +#include "framework/results/pybind_result.hpp" #include "controllers/qasm_controller.hpp" #include "controllers/statevector_controller.hpp" @@ -24,11 +25,16 @@ template class ControllerExecutor { public: ControllerExecutor() = default; - py::object operator()(const py::object &qobj) { return AerToPy::from_result(AER::controller_execute(qobj)); } + py::object operator()(const py::object &qobj) { return AerToPy::to_python(AER::controller_execute(qobj)); } }; PYBIND11_MODULE(controller_wrappers, m) { +#ifdef AER_MPI + int prov; + MPI_Init_thread(nullptr,nullptr,MPI_THREAD_MULTIPLE,&prov); +#endif + py::class_ > qasm_ctrl (m, "qasm_controller_execute"); qasm_ctrl.def(py::init<>()); qasm_ctrl.def("__call__", &ControllerExecutor::operator()); diff --git a/qiskit/providers/aer/extensions/__init__.py b/qiskit/providers/aer/extensions/__init__.py index 10f2635620..c308a51ff3 100644 --- a/qiskit/providers/aer/extensions/__init__.py +++ b/qiskit/providers/aer/extensions/__init__.py @@ -20,6 +20,12 @@ Snapshots ========= +.. note: + + Snapshot extensions will be deprecated after qiskit-aer 0.8 release. + They have been superceded by the save instructions in the + :mod:`qiskit.providers.aer.library` module. + Snapshot instructions allow taking a snapshot of the current state of the simulator without effecting the outcome of the simulation. These can be used with the `QasmSimulator` backend to return the expectation value of diff --git a/qiskit/providers/aer/extensions/snapshot.py b/qiskit/providers/aer/extensions/snapshot.py index 23539bacbb..ff027bb4f6 100644 --- a/qiskit/providers/aer/extensions/snapshot.py +++ b/qiskit/providers/aer/extensions/snapshot.py @@ -25,6 +25,8 @@ class Snapshot(Instruction): """Simulator snapshot instruction.""" + _directive = True + def __init__(self, label, snapshot_type='statevector', @@ -65,15 +67,14 @@ def inverse(self): @staticmethod def define_snapshot_register(circuit, - label, + label=None, qubits=None): """Defines qubits to snapshot for all snapshot methods""" # Convert label to string for backwards compatibility - if not isinstance(label, str): + if label is not None: warnings.warn( - "Snapshot label should be a string, " - "implicit conversion is deprecated.", DeprecationWarning) - label = str(label) + "The 'label' arg of `define_snapshot_register` has been deprecated" + "as of qiskit-aer 0.7.0.", DeprecationWarning) # If no qubits are specified we add all qubits so it acts as a barrier # This is needed for full register snapshots like statevector if isinstance(qubits, QuantumRegister): @@ -136,7 +137,7 @@ def snapshot(self, Raises: ExtensionError: malformed command """ - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + snapshot_register = Snapshot.define_snapshot_register(self, qubits=qubits) return self.append( Snapshot( diff --git a/qiskit/providers/aer/extensions/snapshot_density_matrix.py b/qiskit/providers/aer/extensions/snapshot_density_matrix.py index 3f7434268f..8ba258c7c9 100644 --- a/qiskit/providers/aer/extensions/snapshot_density_matrix.py +++ b/qiskit/providers/aer/extensions/snapshot_density_matrix.py @@ -14,8 +14,9 @@ Simulator command to snapshot internal simulator representation. """ +from warnings import warn from qiskit import QuantumCircuit -from qiskit.providers.aer.extensions import Snapshot +from .snapshot import Snapshot class SnapshotDensityMatrix(Snapshot): @@ -30,7 +31,16 @@ def __init__(self, label, num_qubits): Raises: ExtensionError: if snapshot is invalid. + + .. note:: + + This instruction will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :class:`qiskit.providers.aer.library.SaveDensityMatrix` instruction. """ + warn('`The `SnapshotDensityMatrix` instruction will be deprecated in the' + 'future. It has been superseded by the `SaveDensityMatrix`' + ' instructions.', PendingDeprecationWarning) super().__init__(label, snapshot_type='density_matrix', num_qubits=num_qubits) @@ -49,9 +59,18 @@ def snapshot_density_matrix(self, label, qubits=None): Raises: ExtensionError: if snapshot is invalid. - """ - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + .. note:: + + This method will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :func:`qiskit.providers.aer.library.save_density_matrix` + circuit method. + """ + warn('`The `save_density_matrix` circuit method will be deprecated in the' + ' future. It has been superseded by the `save_density_matrix`' + ' circuit method.', PendingDeprecationWarning) + snapshot_register = Snapshot.define_snapshot_register(self, qubits=qubits) return self.append( SnapshotDensityMatrix(label, num_qubits=len(snapshot_register)), diff --git a/qiskit/providers/aer/extensions/snapshot_expectation_value.py b/qiskit/providers/aer/extensions/snapshot_expectation_value.py index 1603e70ff1..d482e31a9e 100644 --- a/qiskit/providers/aer/extensions/snapshot_expectation_value.py +++ b/qiskit/providers/aer/extensions/snapshot_expectation_value.py @@ -13,6 +13,7 @@ """ Simulator command to snapshot internal simulator representation. """ +from warnings import warn import math import numpy from qiskit import QuantumCircuit @@ -20,7 +21,7 @@ from qiskit.extensions.exceptions import ExtensionError from qiskit.qobj import QasmQobjInstruction from qiskit.quantum_info.operators import Pauli, Operator -from qiskit.providers.aer.extensions import Snapshot +from .snapshot import Snapshot class SnapshotExpectationValue(Snapshot): @@ -37,7 +38,24 @@ def __init__(self, label, op, single_shot=False, variance=False): Raises: ExtensionError: if snapshot is invalid. + + .. note:: + + This instruction will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :class:`qiskit.providers.aer.library.SaveExpectationValue` and + :class:`qiskit.providers.aer.library.SaveExpectationValueVariance` + instructions. """ + warn('The `SnapshotExpectationValue` instruction will be deprecated in the' + ' future. It has been superseded by the `SaveExpectationValue` and' + ' `SaveExpectationValueVariance` instructions.', + PendingDeprecationWarning) + if variance: + warn('The snapshot `variance` kwarg has been deprecated and will' + ' be removed in qiskit-aer 0.8. To compute variance use' + ' `single_shot=True` and compute manually in post-processing', + DeprecationWarning) pauli_op = self._format_pauli_op(op) if pauli_op: # Pauli expectation value @@ -134,9 +152,20 @@ def snapshot_expectation_value(self, label, op, qubits, Raises: ExtensionError: if snapshot is invalid. - """ - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + .. note:: + + This method will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :func:`qiskit.providers.aer.library.save_expectation_value` and + :func:`qiskit.providers.aer.library.save_expectation_value_variance` + circuit methods. + """ + warn('The `snapshot_expectation_value` circuit method will be deprecated ' + ' in the future. It has been superseded by the `save_expectation_value`' + ' and `save_expectation_value_variance` circuit methods.', + PendingDeprecationWarning) + snapshot_register = Snapshot.define_snapshot_register(self, qubits=qubits) return self.append( SnapshotExpectationValue(label, op, diff --git a/qiskit/providers/aer/extensions/snapshot_probabilities.py b/qiskit/providers/aer/extensions/snapshot_probabilities.py index cbcf7807d0..c7069683ea 100644 --- a/qiskit/providers/aer/extensions/snapshot_probabilities.py +++ b/qiskit/providers/aer/extensions/snapshot_probabilities.py @@ -14,8 +14,9 @@ Simulator command to snapshot internal simulator representation. """ +from warnings import warn from qiskit import QuantumCircuit -from qiskit.providers.aer.extensions import Snapshot +from .snapshot import Snapshot class SnapshotProbabilities(Snapshot): @@ -31,7 +32,22 @@ def __init__(self, label, num_qubits, variance=False): Raises: ExtensionError: if snapshot is invalid. + + .. note:: + + This instruction will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :class:`qiskit.providers.aer.library.SaveProbabilities` and + :class:`qiskit.providers.aer.library.SaveProbabilitiesDict` + instructions. """ + warn('The `SnapshotProbabilities` instruction will be deprecated in the' + ' future. It has been superseded by the `SaveProbabilities` and' + ' `SaveProbabilitiesDict` instructions.', + PendingDeprecationWarning) + if variance: + warn('The snapshot `variance` kwarg has been deprecated and will be removed' + ' in qiskit-aer 0.8.', DeprecationWarning) snapshot_type = 'probabilities_with_variance' if variance else 'probabilities' super().__init__(label, snapshot_type=snapshot_type, num_qubits=num_qubits) @@ -50,8 +66,20 @@ def snapshot_probabilities(self, label, qubits, variance=False): Raises: ExtensionError: if snapshot is invalid. + + .. note:: + + This method will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :func:`qiskit.providers.aer.library.save_probabilities` and + :func:`qiskit.providers.aer.library.save_probabilities_dict` + circuit methods. """ - snapshot_register = Snapshot.define_snapshot_register(self, label, qubits) + warn('The `snapshot_probabilities` circuit method will be deprecated ' + ' in the future. It has been superseded by the `save_probabilities`' + ' and `save_probabilities_dict` circuit methods.', + PendingDeprecationWarning) + snapshot_register = Snapshot.define_snapshot_register(self, qubits=qubits) return self.append( SnapshotProbabilities(label, diff --git a/qiskit/providers/aer/extensions/snapshot_stabilizer.py b/qiskit/providers/aer/extensions/snapshot_stabilizer.py index 86ce2ca5c8..1f5d4c5cff 100644 --- a/qiskit/providers/aer/extensions/snapshot_stabilizer.py +++ b/qiskit/providers/aer/extensions/snapshot_stabilizer.py @@ -14,8 +14,9 @@ Simulator command to snapshot internal simulator representation. """ +from warnings import warn from qiskit import QuantumCircuit -from qiskit.providers.aer.extensions import Snapshot +from .snapshot import Snapshot class SnapshotStabilizer(Snapshot): @@ -36,7 +37,16 @@ def __init__(self, label, num_qubits=0): The number of qubits parameter specifies the size of the instruction as a barrier and should be set to the number of qubits in the circuit. + + .. note:: + + This instruction will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :class:`qiskit.providers.aer.library.SaveStabilizer` instruction. """ + warn('The `SnapshotStabilizer` instruction will be deprecated in the' + ' future. It has been superseded by the `save_stabilizer`' + ' instructions.', PendingDeprecationWarning) super().__init__(label, snapshot_type='stabilizer', num_qubits=num_qubits) @@ -57,9 +67,18 @@ def snapshot_stabilizer(self, label): The number of qubits parameter specifies the size of the instruction as a barrier and should be set to the number of qubits in the circuit. - """ - snapshot_register = Snapshot.define_snapshot_register(self, label) + .. note:: + + This method will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :func:`qiskit.providers.aer.library.save_stabilizer` circuit + method. + """ + warn('`The `save_stabilizer` circuit method will be deprecated in the' + ' future. It has been superseded by the `save_stabilizer`' + ' circuit method.', PendingDeprecationWarning) + snapshot_register = Snapshot.define_snapshot_register(self) return self.append( SnapshotStabilizer(label, num_qubits=len(snapshot_register)), diff --git a/qiskit/providers/aer/extensions/snapshot_statevector.py b/qiskit/providers/aer/extensions/snapshot_statevector.py index 19bf1550d1..01ea152678 100644 --- a/qiskit/providers/aer/extensions/snapshot_statevector.py +++ b/qiskit/providers/aer/extensions/snapshot_statevector.py @@ -13,9 +13,9 @@ """ Simulator command to snapshot internal simulator representation. """ - +from warnings import warn from qiskit import QuantumCircuit -from qiskit.providers.aer.extensions import Snapshot +from .snapshot import Snapshot class SnapshotStatevector(Snapshot): @@ -36,7 +36,16 @@ def __init__(self, label, num_qubits=0): The number of qubits parameter specifies the size of the instruction as a barrier and should be set to the number of qubits in the circuit. + + .. note:: + + This instruction will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :class:`qiskit.providers.aer.library.SaveStatevector` instruction. """ + warn('`The `SnapshotStatevector` instruction will be deprecated in the' + 'future. It has been superseded by the `SaveStatevector`' + ' instructions.', PendingDeprecationWarning) super().__init__(label, snapshot_type='statevector', num_qubits=num_qubits) @@ -57,10 +66,20 @@ def snapshot_statevector(self, label): The number of qubits parameter specifies the size of the instruction as a barrier and should be set to the number of qubits in the circuit. + + .. note:: + + This method will be deprecated after the qiskit-aer 0.8 release. + It has been superseded by the + :func:`qiskit.providers.aer.library.save_statevector` circuit + method. """ + warn('`The `save_statevector` circuit method will be deprecated in the' + ' future. It has been superseded by the `save_statevector`' + ' circuit method.', PendingDeprecationWarning) # Statevector snapshot acts as a barrier across all qubits in the # circuit - snapshot_register = Snapshot.define_snapshot_register(self, label) + snapshot_register = Snapshot.define_snapshot_register(self) return self.append( SnapshotStatevector(label, num_qubits=len(snapshot_register)), diff --git a/qiskit/providers/aer/library/__init__.py b/qiskit/providers/aer/library/__init__.py new file mode 100644 index 0000000000..61742e881a --- /dev/null +++ b/qiskit/providers/aer/library/__init__.py @@ -0,0 +1,119 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" +========================================================= +Instruction Library (:mod:`qiskit.providers.aer.library`) +========================================================= + +.. currentmodule:: qiskit.providers.aer.library + +This library contains custom qiskit :class:`~qiskit.QuantumCircuit` +:class:`~qiskit.circuit.Instruction` subclasses that can be used +with the Aer circuit simulator backends. + +Saving Simulator Data +===================== + +The following classes can be used to directly save data from the +simulator to the returned result object. + +Instruction Classes +------------------- + +.. autosummary:: + :toctree: ../stubs/ + + SaveState + SaveExpectationValue + SaveExpectationValueVariance + SaveProbabilities + SaveProbabilitiesDict + SaveDensityMatrix + SaveStatevector + SaveStatevectorDict + SaveAmplitudes + SaveAmplitudesSquared + SaveStabilizer + SaveUnitary + +Then can also be used using custom QuantumCircuit methods + +QuantumCircuit Methods +---------------------- + +.. autosummary:: + :toctree: ../stubs/ + + save_state + save_expectation_value + save_expectation_value_variance + save_probabilities + save_probabilities_dict + save_density_matrix + save_statevector + save_statevector_dict + save_amplitudes + save_amplitudes_squared + save_stabilizer + save_unitary + +.. note :: + + **Save Instruction Labels** + + Each save instruction has a default label for accessing from the + circuit result data, however duplicate labels in results will result + in an exception being raised. If you use more than 1 instance of a + specific save instruction you must set a custom label for the + additional instructions. + +.. note :: + + **Pershot Data with Measurement Sampling Optimization** + + When saving pershot data by using the ``pershot=True`` kwarg + in the above instructions, the resulting list may only contain + a single value rather than the number of shots. This + happens when a run circuit supports measurement sampling because + it is either + + 1. An ideal simulation with all measurements at the end. + + 2. A noisy simulation using the density matrix method with all + measurements at the end. + + In both these cases only a single shot is actually simulated and + measurement samples for all shots are calculated from the final + state. +""" + +__all__ = ['SaveState', + 'SaveExpectationValue', 'SaveExpectationValueVariance', + 'SaveProbabilities', 'SaveProbabilitiesDict', + 'SaveStatevector', 'SaveStatevectorDict', 'SaveDensityMatrix', + 'SaveAmplitudes', 'SaveAmplitudesSquared', 'SaveUnitary', + 'SaveStabilizer'] + +from .save_state import SaveState, save_state +from .save_expectation_value import ( + SaveExpectationValue, save_expectation_value, + SaveExpectationValueVariance, save_expectation_value_variance) +from .save_probabilities import (SaveProbabilities, save_probabilities, + SaveProbabilitiesDict, save_probabilities_dict) +from .save_statevector import (SaveStatevector, save_statevector, + SaveStatevectorDict, save_statevector_dict) +from .save_density_matrix import SaveDensityMatrix, save_density_matrix +from .save_amplitudes import (SaveAmplitudes, save_amplitudes, + SaveAmplitudesSquared, save_amplitudes_squared) +from .save_stabilizer import SaveStabilizer, save_stabilizer +from .save_unitary import SaveUnitary, save_unitary diff --git a/qiskit/providers/aer/library/save_amplitudes.py b/qiskit/providers/aer/library/save_amplitudes.py new file mode 100644 index 0000000000..5278fb1095 --- /dev/null +++ b/qiskit/providers/aer/library/save_amplitudes.py @@ -0,0 +1,160 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save statevector amplitudes and amplitudes squared. +""" + +from qiskit.circuit import QuantumCircuit +from qiskit.extensions.exceptions import ExtensionError +from .save_data import SaveSingleData, SaveAverageData, default_qubits + + +class SaveAmplitudes(SaveSingleData): + """Save complex statevector amplitudes.""" + def __init__(self, + num_qubits, + params, + label="amplitudes", + pershot=False, + conditional=False): + """Instruction to save complex statevector amplitudes. + + Args: + num_qubits (int): the number of qubits for the snapshot type. + params (list): list of entries to vale. + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of amplitudes vectors for each + shot of the simulation rather than the a single + amplitude vector [Default: False]. + conditional (bool): if True save the amplitudes vector conditional + on the current classical register values + [Default: False]. + + Raises: + ExtensionError: if params is invalid for the specified number of qubits. + """ + params = _format_amplitude_params(params, num_qubits) + super().__init__("save_amplitudes", num_qubits, label, + pershot=pershot, + conditional=conditional, + params=params) + + +class SaveAmplitudesSquared(SaveAverageData): + """Save squared statevector amplitudes (probabilities).""" + def __init__(self, + num_qubits, + params, + label="amplitudes_squared", + unnormalized=False, + pershot=False, + conditional=False): + """Instruction to save squared statevector amplitudes (probabilities). + + Args: + num_qubits (int): the number of qubits for the snapshot type. + params (list): list of entries to vale. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probability vectors for each + shot of the simulation rather than the a single + amplitude vector [Default: False]. + conditional (bool): if True save the probability vector conditional + on the current classical register values + [Default: False]. + + Raises: + ExtensionError: if params is invalid for the specified number of qubits. + """ + params = _format_amplitude_params(params, num_qubits) + super().__init__("save_amplitudes_sq", + num_qubits, + label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params) + + +def save_amplitudes(self, params, label="amplitudes", pershot=False, conditional=False): + """Save complex statevector amplitudes. + + Args: + params (List[int] or List[str]): the basis states to return amplitudes for. + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of amplitudes vectors for each + shot of the simulation rather than the a single + amplitude vector [Default: False]. + conditional (bool): if True save the amplitudes vector conditional + on the current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + Raises: + ExtensionError: if params is invalid for the specified number of qubits. + """ + qubits = default_qubits(self) + instr = SaveAmplitudes(len(qubits), params, label=label, + pershot=pershot, conditional=conditional) + return self.append(instr, qubits) + + +def save_amplitudes_squared(self, params, label="amplitudes_squared", + unnormalized=False, + pershot=False, + conditional=False): + """Save squared statevector amplitudes (probabilities). + + Args: + params (List[int] or List[str]): the basis states to return amplitudes for. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probability vectors for each + shot of the simulation rather than the a single + amplitude vector [Default: False]. + conditional (bool): if True save the probability vector conditional + on the current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + Raises: + ExtensionError: if params is invalid for the specified number of qubits. + """ + qubits = default_qubits(self) + instr = SaveAmplitudesSquared(len(qubits), params, label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +def _format_amplitude_params(params, num_qubits=None): + """Format amplitude params as a interger list.""" + if isinstance(params[0], str): + if params[0].find('0x') == 0: + params = [int(i, 16) for i in params] + else: + params = [int(i, 2) for i in params] + if num_qubits and max(params) >= 2 ** num_qubits: + raise ExtensionError( + "Param values contain a state larger than the number of qubits") + return params + + +QuantumCircuit.save_amplitudes = save_amplitudes +QuantumCircuit.save_amplitudes_squared = save_amplitudes_squared diff --git a/qiskit/providers/aer/library/save_data.py b/qiskit/providers/aer/library/save_data.py new file mode 100644 index 0000000000..aeab322ec9 --- /dev/null +++ b/qiskit/providers/aer/library/save_data.py @@ -0,0 +1,183 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save custom internal data to results. +""" + +import copy + +from qiskit.circuit import QuantumCircuit, QuantumRegister +from qiskit.circuit import Instruction +from qiskit.extensions.exceptions import ExtensionError + + +class SaveData(Instruction): + """Pragma Instruction to save simulator data.""" + + _directive = True + _allowed_subtypes = set([ + 'single', 'c_single', 'list', 'c_list', + 'average', 'c_average', 'accum', 'c_accum' + ]) + + def __init__(self, name, num_qubits, label, subtype='single', params=None): + """Create new save data instruction. + + Args: + name (str): the name of hte save instruction. + num_qubits (int): the number of qubits for the snapshot type. + label (str): the key for retrieving saved data from results. + subtype (str): the data subtype for the instruction [Default: 'single']. + params (list or None): Optional, the parameters for instruction + [Default: None]. + + Raises: + ExtensionError: if the subtype string is invalid. + + Additional Information: + The supported subtypes are 'single', 'list', 'c_list', 'average', + 'c_average', 'accum', 'c_accum'. + """ + if params is None: + params = {} + + if subtype not in self._allowed_subtypes: + raise ExtensionError( + "Invalid data subtype for SaveData instruction.") + + if not isinstance(label, str): + raise ExtensionError( + f"Invalid label for save data instruction, {label} must be a string.") + + self._label = label + self._subtype = subtype + super().__init__(name, num_qubits, 0, params) + + 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): + """Special case. Return self.""" + return copy.copy(self) + + +class SaveAverageData(SaveData): + """Save averageble data""" + def __init__(self, + name, + num_qubits, + label, + unnormalized=False, + pershot=False, + conditional=False, + params=None): + """Create new save data instruction. + + Args: + name (str): the name of hte save instruction. + num_qubits (int): the number of qubits for the snapshot type. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated data over all shot. + [Default: False]. + pershot (bool): if True save a list of data for each shot of the + simulation rather than the average over all shots + [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + params (list or None): Optional, the parameters for instruction + [Default: None]. + """ + if pershot: + subtype = 'list' + elif unnormalized: + subtype = 'accum' + else: + subtype = 'average' + if conditional: + subtype = 'c_' + subtype + super().__init__(name, num_qubits, label, subtype=subtype, params=params) + + +class SaveSingleData(SaveData): + """Save non-averagable single data type.""" + + def __init__(self, + name, + num_qubits, + label, + pershot=False, + conditional=False, + params=None): + """Create new save data instruction. + + Args: + name (str): the name of the save instruction. + num_qubits (int): the number of qubits for the snapshot type. + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of data for each shot of the + simulation [Default: False]. + conditional (bool): if True save data conditional on the + current classical register values + [Default: False]. + params (list or None): Optional, the parameters for instruction + [Default: None]. + """ + subtype = 'list' if pershot else 'single' + if conditional: + subtype = 'c_' + subtype + super().__init__(name, num_qubits, label, subtype=subtype, params=params) + + +def default_qubits(circuit, qubits=None): + """Helper method to return list of qubits. + + Args: + circuit (QuantumCircuit): a quantum circuit. + qubits (list or QuantumRegister): Optional, qubits argument, + If None the returned list will be all qubits in the circuit. + [Default: None] + + Raises: + ExtensionError: if default qubits fails. + + Returns: + list: qubits list. + """ + # Convert label to string for backwards compatibility + # If no qubits are specified we add all qubits so it acts as a barrier + # This is needed for full register snapshots like statevector + if isinstance(qubits, QuantumRegister): + qubits = qubits[:] + if not qubits: + tuples = [] + if isinstance(circuit, QuantumCircuit): + for register in circuit.qregs: + tuples.append(register) + if not tuples: + raise ExtensionError('no qubits for snapshot') + qubits = [] + for tuple_element in tuples: + if isinstance(tuple_element, QuantumRegister): + for j in range(tuple_element.size): + qubits.append(tuple_element[j]) + else: + qubits.append(tuple_element) + + return qubits diff --git a/qiskit/providers/aer/library/save_density_matrix.py b/qiskit/providers/aer/library/save_density_matrix.py new file mode 100644 index 0000000000..e502a2f494 --- /dev/null +++ b/qiskit/providers/aer/library/save_density_matrix.py @@ -0,0 +1,84 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save a density matrix. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveAverageData, default_qubits + + +class SaveDensityMatrix(SaveAverageData): + """Save a reduced density matrix.""" + def __init__(self, + num_qubits, + label="density_matrix", + unnormalized=False, + pershot=False, + conditional=False): + """Create new instruction to save the simulator reduced density matrix. + + Args: + num_qubits (int): the number of qubits for the save instruction. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated density matrix over + all shots [Default: False]. + pershot (bool): if True save a list of density matrices for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + """ + super().__init__("save_density_matrix", num_qubits, label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + + +def save_density_matrix(self, + qubits=None, + label="density_matrix", + unnormalized=False, + pershot=False, + conditional=False): + """Save the current simulator quantum state as a density matrix. + + Args: + qubits (list or None): the qubits to save reduced density matrix on. + If None the full density matrix of qubits will + be saved [Default: None]. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated density matrix over + all shots [Default: False]. + pershot (bool): if True save a list of density matrices for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + """ + qubits = default_qubits(self, qubits=qubits) + instr = SaveDensityMatrix(len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_density_matrix = save_density_matrix diff --git a/qiskit/providers/aer/library/save_expectation_value.py b/qiskit/providers/aer/library/save_expectation_value.py new file mode 100644 index 0000000000..71fd85bd8a --- /dev/null +++ b/qiskit/providers/aer/library/save_expectation_value.py @@ -0,0 +1,241 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save exact operator expectation value. +""" + +from numpy import allclose +from qiskit.quantum_info import Pauli, SparsePauliOp, Operator +from qiskit.circuit import QuantumCircuit +from qiskit.extensions.exceptions import ExtensionError +from .save_data import SaveAverageData + + +class SaveExpectationValue(SaveAverageData): + """Save expectation value of an operator.""" + def __init__(self, + operator, + label="expectation_value", + unnormalized=False, + pershot=False, + conditional=False): + r"""Instruction to save the expectation value of a Hermitian operator. + + The expectation value of a Hermitian operator :math:`H` for a simulator + in quantum state :math`\rho`is given by + :math:`\langle H\rangle = \mbox{Tr}[H.\rho]`. + + Args: + operator (Pauli or SparsePauliOp or Operator): a Hermitian operator. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated expectation value + over all shot [Default: False]. + pershot (bool): if True save a list of expectation values for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + + Raises: + ExtensionError: if the input operator is invalid or not Hermitian. + + .. note:: + + This instruction can be directly appended to a circuit using the + :func:`save_expectation_value` circuit method. + """ + # Convert O to SparsePauliOp representation + if isinstance(operator, Pauli): + operator = SparsePauliOp(operator) + elif not isinstance(operator, SparsePauliOp): + operator = SparsePauliOp.from_operator(Operator(operator)) + if not allclose(operator.coeffs.imag, 0): + raise ExtensionError("Input operator is not Hermitian.") + params = _expval_params(operator, variance=False) + super().__init__('save_expval', operator.num_qubits, label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params) + + +class SaveExpectationValueVariance(SaveAverageData): + """Save expectation value and variance of an operator.""" + def __init__(self, + operator, + label="expectation_value_variance", + unnormalized=False, + pershot=False, + conditional=False): + r"""Instruction to save the expectation value and variance of a Hermitian operator. + + The expectation value of a Hermitian operator :math:`H` for a + simulator in quantum state :math`\rho`is given by + :math:`\langle H\rangle = \mbox{Tr}[H.\rho]`. The variance is given by + :math:`\sigma^2 = \langle H^2 \rangle - \langle H \rangle>^2`. + + Args: + operator (Pauli or SparsePauliOp or Operator): a Hermitian operator. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated expectation value + over all shot [Default: False]. + pershot (bool): if True save a list of expectation values for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + + Raises: + ExtensionError: if the input operator is invalid or not Hermitian. + + .. note:: + + This instruction can be directly appended to a circuit using + the :func:`save_expectation_value` circuit method. + """ + # Convert O to SparsePauliOp representation + if isinstance(operator, Pauli): + operator = SparsePauliOp(operator) + elif not isinstance(operator, SparsePauliOp): + operator = SparsePauliOp.from_operator(Operator(operator)) + if not allclose(operator.coeffs.imag, 0): + raise ExtensionError("Input operator is not Hermitian.") + params = _expval_params(operator, variance=True) + super().__init__('save_expval_var', operator.num_qubits, label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional, + params=params) + + +def _expval_params(operator, variance=False): + + # Convert O to SparsePauliOp representation + if isinstance(operator, Pauli): + operator = SparsePauliOp(operator) + elif not isinstance(operator, SparsePauliOp): + operator = SparsePauliOp.from_operator(Operator(operator)) + if not isinstance(operator, SparsePauliOp): + raise ExtensionError("Invalid input operator") + + params = {} + + # Add Pauli basis components of O + for pauli, coeff in operator.label_iter(): + if pauli in params: + coeff1 = params[pauli][0] + params[pauli] = (coeff1 + coeff.real, 0) + else: + params[pauli] = (coeff.real, 0) + + # Add Pauli basis components of O^2 + if variance: + for pauli, coeff in operator.dot(operator).label_iter(): + if pauli in params: + coeff1, coeff2 = params[pauli] + params[pauli] = (coeff1, coeff2 + coeff.real) + else: + params[pauli] = (0, coeff.real) + + # Convert to list + return list(params.items()) + + +def save_expectation_value(self, + operator, + qubits, + label="expectation_value", + unnormalized=False, + pershot=False, + conditional=False): + r"""Save the expectation value of a Hermitian operator. + + Args: + operator (Pauli or SparsePauliOp or Operator): a Hermitian operator. + qubits (list): circuit qubits to apply instruction. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated expectation value + over all shot [Default: False]. + pershot (bool): if True save a list of expectation values for each + shot of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the average or pershot data + conditional on the current classical register + values [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + Raises: + ExtensionError: if the input operator is invalid or not Hermitian. + + .. note:: + + This method appends a :class:`SaveExpectationValue` instruction to + the quantum circuit. + """ + instr = SaveExpectationValue(operator, + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +def save_expectation_value_variance(self, + operator, + qubits, + label="expectation_value_variance", + unnormalized=False, + pershot=False, + conditional=False): + r"""Save the expectation value of a Hermitian operator. + + Args: + operator (Pauli or SparsePauliOp or Operator): a Hermitian operator. + qubits (list): circuit qubits to apply instruction. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + or conditional accumulated expectation value + and variance over all shot [Default: False]. + pershot (bool): if True save a list of expectation values and + variances for each shot of the simulation rather than + the average over all shots [Default: False]. + conditional (bool): if True save the data conditional on the current + classical register values [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + Raises: + ExtensionError: if the input operator is invalid or not Hermitian. + + .. note:: + + This method appends a :class:`SaveExpectationValueVariance` + instruction to the quantum circuit. + """ + instr = SaveExpectationValueVariance(operator, + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_expectation_value = save_expectation_value +QuantumCircuit.save_expectation_value_variance = save_expectation_value_variance diff --git a/qiskit/providers/aer/library/save_probabilities.py b/qiskit/providers/aer/library/save_probabilities.py new file mode 100644 index 0000000000..5fde254247 --- /dev/null +++ b/qiskit/providers/aer/library/save_probabilities.py @@ -0,0 +1,143 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save measurement outcome probabilites. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveAverageData, default_qubits + + +class SaveProbabilities(SaveAverageData): + """Save measurement outcome probabilities vector.""" + def __init__(self, + num_qubits, + label="probabilities", + unnormalized=False, + pershot=False, + conditional=False): + """Instruction to save measurement probabilities vector. + + Args: + num_qubits (int): the number of qubits for the snapshot type. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probabilities for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the probabilities data conditional + on the current classical register values + [Default: False]. + """ + super().__init__("save_probabilities", num_qubits, label, + conditional=conditional, + pershot=pershot, + unnormalized=unnormalized) + + +class SaveProbabilitiesDict(SaveAverageData): + """Save measurement outcome probabilities dict.""" + def __init__(self, + num_qubits, + label="probabilities_dict", + unnormalized=False, + pershot=False, + conditional=False): + """Instruction to save measurement probabilities dict. + + Args: + num_qubits (int): the number of qubits for the snapshot type. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probabilities for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the probabilities data conditional + on the current classical register values + [Default: False]. + """ + super().__init__("save_probabilities_dict", num_qubits, label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + + +def save_probabilities(self, + qubits=None, + label="probabilities", + unnormalized=False, + pershot=False, + conditional=False): + """Save measurement outcome probabilities vector. + + Args: + qubits (list or None): the qubits to apply snapshot to. If None all + qubits will be snapshot [Default: None]. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probabilities for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the probabilities data conditional + on the current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + """ + qubits = default_qubits(self, qubits=qubits) + instr = SaveProbabilities(len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +def save_probabilities_dict(self, + qubits=None, + label="probabilities", + unnormalized=False, + pershot=False, + conditional=False): + """Save measurement outcome probabilities vector. + + Args: + qubits (list or None): the qubits to apply snapshot to. If None all + qubits will be snapshot [Default: None]. + label (str): the key for retrieving saved data from results. + unnormalized (bool): If True return save the unnormalized accumulated + probabilities over all shots [Default: False]. + pershot (bool): if True save a list of probabilities for each shot + of the simulation rather than the average over + all shots [Default: False]. + conditional (bool): if True save the probabilities data conditional + on the current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + """ + qubits = default_qubits(self, qubits=qubits) + instr = SaveProbabilitiesDict(len(qubits), + label=label, + unnormalized=unnormalized, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_probabilities = save_probabilities +QuantumCircuit.save_probabilities_dict = save_probabilities_dict diff --git a/qiskit/providers/aer/library/save_stabilizer.py b/qiskit/providers/aer/library/save_stabilizer.py new file mode 100644 index 0000000000..6f1ebda2c5 --- /dev/null +++ b/qiskit/providers/aer/library/save_stabilizer.py @@ -0,0 +1,72 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save Clifford state. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveSingleData, default_qubits + + +class SaveStabilizer(SaveSingleData): + """Save Stabilizer instruction""" + def __init__(self, num_qubits, label="stabilizer", + pershot=False, conditional=False): + """Create new instruction to save the stabilizer simulator state. + + Args: + num_qubits (int): the number of qubits of the + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of Cliffords for each + shot of the simulation rather than a single + statevector [Default: False]. + conditional (bool): if True save data conditional on the current + classical register values [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + super().__init__('save_stabilizer', num_qubits, label, + pershot=pershot, + conditional=conditional) + + +def save_stabilizer(self, label="stabilizer", pershot=False, conditional=False): + """Save the current stabilizer simulator quantum state as a Clifford. + + Args: + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of Cliffords for each + shot of the simulation [Default: False]. + conditional (bool): if True save pershot data conditional on the + current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note:: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveStabilizer(len(qubits), + label=label, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_stabilizer = save_stabilizer diff --git a/qiskit/providers/aer/library/save_state.py b/qiskit/providers/aer/library/save_state.py new file mode 100644 index 0000000000..ce22b6208b --- /dev/null +++ b/qiskit/providers/aer/library/save_state.py @@ -0,0 +1,85 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save simulator state. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveSingleData, default_qubits + + +class SaveState(SaveSingleData): + """Save simulator state + + The format of the saved state depends on the simulation method used. + """ + def __init__(self, num_qubits, + label=None, + pershot=False, + conditional=False): + """Create new instruction to save the simualtor state. + + The format of the saved state depends on the simulation method used. + + Args: + num_qubits (int): the number of qubits of the + label (str or None): Optional, the key for retrieving saved data + from results. If None the key will be the + state type of the simulator. + pershot (bool): if True save a list of states for each + shot of the simulation rather than a single + state [Default: False]. + conditional (bool): if True save data conditional on the current + classical register values [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + if label is None: + label = '_method_' + super().__init__('save_state', num_qubits, label, + pershot=pershot, + conditional=conditional) + + +def save_state(self, label=None, pershot=False, conditional=False): + """Save the current simulator quantum state. + + Args: + label (str or None): Optional, the key for retrieving saved data + from results. If None the key will be the + state type of the simulator. + pershot (bool): if True save a list of statevectors for each + shot of the simulation [Default: False]. + conditional (bool): if True save pershot data conditional on the + current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveState(len(qubits), + label=label, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_state = save_state diff --git a/qiskit/providers/aer/library/save_statevector.py b/qiskit/providers/aer/library/save_statevector.py new file mode 100644 index 0000000000..5b43e0fceb --- /dev/null +++ b/qiskit/providers/aer/library/save_statevector.py @@ -0,0 +1,129 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save statevector. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveSingleData, default_qubits + + +class SaveStatevector(SaveSingleData): + """Save statevector""" + def __init__(self, num_qubits, + label="statevector", + pershot=False, + conditional=False): + """Create new instruction to save the simualtor statevector. + + Args: + num_qubits (int): the number of qubits of the + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of statevectors for each + shot of the simulation rather than a single + statevector [Default: False]. + conditional (bool): if True save data conditional on the current + classical register values [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + super().__init__('save_statevector', num_qubits, label, + pershot=pershot, + conditional=conditional) + + +class SaveStatevectorDict(SaveSingleData): + """Save statevector as ket-form dictionary.""" + def __init__(self, num_qubits, + label="statevector_dict", + pershot=False, + conditional=False): + """Create new instruction to save the simualtor statevector as a dict. + + Args: + num_qubits (int): the number of qubits of the + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of statevectors for each + shot of the simulation rather than a single + statevector [Default: False]. + conditional (bool): if True save data conditional on the current + classical register values [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + super().__init__('save_statevector_dict', num_qubits, label, + pershot=pershot, + conditional=conditional) + + +def save_statevector(self, label="statevector", pershot=False, conditional=False): + """Save the current simulator quantum state as a statevector. + + Args: + pershot (bool): if True save a list of statevectors for each + shot of the simulation [Default: False]. + label (str): the key for retrieving saved data from results. + conditional (bool): if True save pershot data conditional on the + current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note:: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveStatevector(len(qubits), + label=label, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +def save_statevector_dict(self, label="statevector", pershot=False, conditional=False): + """Save the current simulator quantum state as a statevector as a dict. + + Args: + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of statevectors for each + shot of the simulation [Default: False]. + conditional (bool): if True save pershot data conditional on the + current classical register values + [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note:: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveStatevectorDict(len(qubits), + label=label, + pershot=pershot, + conditional=conditional) + return self.append(instr, qubits) + + +QuantumCircuit.save_statevector = save_statevector +QuantumCircuit.save_statevector_dict = save_statevector_dict diff --git a/qiskit/providers/aer/library/save_unitary.py b/qiskit/providers/aer/library/save_unitary.py new file mode 100644 index 0000000000..f339b41725 --- /dev/null +++ b/qiskit/providers/aer/library/save_unitary.py @@ -0,0 +1,64 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2021. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" +Simulator instruction to save unitary matrix. +""" + +from qiskit.circuit import QuantumCircuit +from .save_data import SaveSingleData, default_qubits + + +class SaveUnitary(SaveSingleData): + """Save Unitary""" + def __init__(self, num_qubits, label="unitary", pershot=False): + """Create new instruction to save the unitary simulator state. + + Args: + num_qubits (int): the number of qubits of the + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of unitaries for each + shot of the simulation rather than a single + statevector [Default: False]. + + .. note:: + + This save instruction must always be performed on the full width of + qubits in a circuit, otherwise an exception will be raised during + simulation. + """ + super().__init__('save_unitary', num_qubits, label, + pershot=pershot) + + +def save_unitary(self, label="unitary", pershot=False): + """Save the current state of the unitary simulator. + + Args: + label (str): the key for retrieving saved data from results. + pershot (bool): if True save a list of unitaries for each + shot of the simulation [Default: False]. + + Returns: + QuantumCircuit: with attached instruction. + + .. note:: + + This instruction is always defined across all qubits in a circuit. + """ + qubits = default_qubits(self) + instr = SaveUnitary(len(qubits), + label=label, + pershot=pershot) + return self.append(instr, qubits) + + +QuantumCircuit.save_unitary = save_unitary diff --git a/qiskit/providers/aer/noise/__init__.py b/qiskit/providers/aer/noise/__init__.py index f79b3faaf2..841019e7ec 100644 --- a/qiskit/providers/aer/noise/__init__.py +++ b/qiskit/providers/aer/noise/__init__.py @@ -195,4 +195,3 @@ # Submodules from . import errors from . import device -from . import utils diff --git a/qiskit/providers/aer/noise/device/__init__.py b/qiskit/providers/aer/noise/device/__init__.py index 76a778bd7c..658baf69c0 100644 --- a/qiskit/providers/aer/noise/device/__init__.py +++ b/qiskit/providers/aer/noise/device/__init__.py @@ -14,7 +14,6 @@ Functions for building noise models from backend properties. """ -from .basic_device_model import basic_device_noise_model from .models import basic_device_readout_errors from .models import basic_device_gate_errors from .parameters import gate_param_values diff --git a/qiskit/providers/aer/noise/device/basic_device_model.py b/qiskit/providers/aer/noise/device/basic_device_model.py deleted file mode 100644 index c6d752430a..0000000000 --- a/qiskit/providers/aer/noise/device/basic_device_model.py +++ /dev/null @@ -1,127 +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=invalid-name,import-outside-toplevel -""" -Simplified noise models for devices backends. -""" - -import warnings - - -def basic_device_noise_model(properties, - gate_error=True, - readout_error=True, - thermal_relaxation=True, - temperature=0, - gate_lengths=None, - gate_length_units='ns', - standard_gates=True): - """ - Return a noise model derived from a devices backend properties. - - This function generates a noise model based on: - - * 1 and 2 qubit gate errors consisting of a - :func:`depolarizing_error` followed - by a :func:`thermal_relaxation_error`. - - * Single qubit :class:`ReadoutError` on all measurements. - - The Error error parameters are tuned for each individual qubit based on - the :math:`T_1`, :math:`T_2`, frequency and readout error parameters for - each qubit, and the gate error and gate time parameters for each gate - obtained from the device backend properties. - - **Additional Information** - - The noise model includes the following errors: - - * If ``readout_error=True`` include single qubit readout - errors on measurements. - - * If ``gate_error=True`` and ``thermal_relaxation=True`` include: - - * Single-qubit gate errors consisting of a :func:`depolarizing_error` - followed by a :func:`thermal_relaxation_error` for the qubit the - gate acts on. - - * Two-qubit gate errors consisting of a 2-qubit - :func:`depolarizing_error` followed by single qubit - :func:`thermal_relaxation_error` on each qubit participating in - the gate. - - * If ``gate_error=True`` is ``True`` and ``thermal_relaxation=False``: - - * An N-qubit :func:`depolarizing_error` on each N-qubit gate. - - * If ``gate_error=False`` and ``thermal_relaxation=True`` include - single-qubit :func:`thermal_relaxation_errors` on each qubits - participating in a multi-qubit gate. - - For best practice in simulating a backend make sure that the - circuit is compiled using the set of basis gates in the noise - module by setting ``basis_gates=noise_model.basis_gates`` - and using the device coupling map with - ``coupling_map=backend.configuration().coupling_map`` - - **Specifying custom gate times** - - The ``gate_lengths`` kwarg can be used to specify custom gate times - to add gate errors using the :math:`T_1` and :math:`T_2` values from - the backend properties. This should be passed as a list of tuples - ``gate_lengths=[(name, value), ...]`` - where ``name`` is the gate name string, and ``value`` is the gate time - in nanoseconds. - - If a custom gate is specified that already exists in - the backend properties, the ``gate_lengths`` value will override the - gate time value from the backend properties. - If non-default values are used gate_lengths should be a list - - Args: - properties (BackendProperties): backend properties. - gate_error (bool): Include depolarizing gate errors (Default: True). - readout_error (Bool): Include readout errors in model - (Default: True). - thermal_relaxation (Bool): Include thermal relaxation errors - (Default: True). - temperature (double): qubit temperature in milli-Kelvin (mK) for - thermal relaxation errors (Default: 0). - gate_lengths (list): Custom gate times for thermal relaxation errors. - Used to extend or override the gate times in - the backend properties (Default: None)) - gate_length_units (str): Time units for gate length values in gate_lengths. - Can be 'ns', 'ms', 'us', or 's' (Default: 'ns'). - standard_gates (bool): If true return errors as standard - qobj gates. If false return as unitary - qobj instructions (Default: True) - - Returns: - NoiseModel: An approximate noise model for the device backend. - """ - warnings.warn( - 'This function is been deprecated and moved to a method of the' - '`NoiseModel` class. For equivalent functionality use' - ' `NoiseModel.from_backend(properties, **kwargs).', - DeprecationWarning) - # This wrapper is for the deprecated function - # We need to import noise model here to avoid cyclic import errors - # pylint: disable=import-outside-toplevel - from qiskit.providers.aer.noise.noise_model import NoiseModel - return NoiseModel.from_backend(properties, - gate_error=gate_error, - readout_error=readout_error, - thermal_relaxation=thermal_relaxation, - temperature=temperature, - gate_lengths=gate_lengths, - gate_length_units=gate_length_units, - standard_gates=standard_gates) diff --git a/qiskit/providers/aer/noise/device/models.py b/qiskit/providers/aer/noise/device/models.py index 3a66c316ed..6e904ee4a3 100644 --- a/qiskit/providers/aer/noise/device/models.py +++ b/qiskit/providers/aer/noise/device/models.py @@ -249,11 +249,11 @@ def _excited_population(freq, temperature): if freq != inf and temperature != 0: # Compute the excited state population from qubit # frequency and temperature - # Boltzman constant kB = 6.62607015e-34 (eV/K) - # Planck constant h = 6.62607015e-34 (eV.s) + # Boltzman constant kB = 8.617333262-5 (eV/K) + # Planck constant h = 4.135667696e-15 (eV.s) # qubit temperature temperatue = T (mK) # qubit frequency frequency = f (GHz) - # excited state population = 1/(1+exp((2hf*1e9)/(kbT*1e-3))) + # excited state population = 1/(1+exp((2*h*f*1e9)/(kb*T*1e-3))) exp_param = exp((95.9849 * freq) / abs(temperature)) population = 1 / (1 + exp_param) if temperature < 0: diff --git a/qiskit/providers/aer/noise/errors/readout_error.py b/qiskit/providers/aer/noise/errors/readout_error.py index f492e2c26c..61d2445e79 100644 --- a/qiskit/providers/aer/noise/errors/readout_error.py +++ b/qiskit/providers/aer/noise/errors/readout_error.py @@ -42,11 +42,11 @@ def __init__(self, probabilities, atol=ATOL_DEFAULT): .. code-block:: python - probabilities[j] = [P(0|m), P(1|m), ..., P(2 ** N - 1|m)] + probabilities[m] = [P(0|m), P(1|m), ..., P(2 ** N - 1|m)] - where ``P(j|m)`` is the probability of recording a measurement outcome - of ``m`` as the value ``j``. Where ``j`` and ``m`` are integer - representations of bit-strings. + where ``P(n|m)`` is the probability of recording a noisy measurement + outcome as ``n`` given the true ideal measurement outcome was ``m``, + where ``n`` and ``m`` are integer representations of bit-strings. **Example: 1-qubit** diff --git a/qiskit/providers/aer/noise/noise_model.py b/qiskit/providers/aer/noise/noise_model.py index 419f771a93..defc7f3f39 100644 --- a/qiskit/providers/aer/noise/noise_model.py +++ b/qiskit/providers/aer/noise/noise_model.py @@ -15,15 +15,13 @@ import json import logging -from warnings import warn from qiskit.circuit import Instruction -from qiskit.providers import BaseBackend +from qiskit.providers import BaseBackend, Backend from qiskit.providers.models import BackendProperties -from qiskit.providers.aer.backends.aerbackend import AerJSONEncoder -from qiskit.providers.aer.backends.qasm_simulator import QasmSimulator - +from ..backends.aerbackend import AerJSONEncoder +from ..backends.qasm_simulator import QasmSimulator from .noiseerror import NoiseError from .errors.quantum_error import QuantumError from .errors.readout_error import ReadoutError @@ -89,17 +87,17 @@ class NoiseModel: # Get the default basis gates for the Qiskit Aer Qasm Simulator # this is used to decide what are instructions for a noise model # and what are labels for other named instructions - _QASMSIMULATOR_BASIS_GATES = QasmSimulator.DEFAULT_CONFIGURATION[ - 'basis_gates'] + # NOTE: we exclude kraus, roerror, and initialize instructions here + _QASMSIMULATOR_BASIS_GATES = QasmSimulator._DEFAULT_CONFIGURATION['basis_gates'] # Checks for standard 1-3 qubit instructions _1qubit_instructions = set([ - "x90", "u1", "u2", "u3", "U", "id", "x", "y", "z", "h", "s", "sdg", - "t", "tdg", "r", "rx", "ry", "rz", "p" - ]) - _2qubit_instructions = set(["cx", "cz", "swap", "rxx", "ryy", "rzz", - "rzx", "cu1", "cu2", "cu3", "cp"]) - _3qubit_instructions = set(["ccx", "cswap"]) + 'u1', 'u2', 'u3', 'u', 'p', 'r', 'rx', 'ry', 'rz', 'id', 'x', + 'y', 'z', 'h', 's', 'sdg', 'sx', 't', 'tdg']) + _2qubit_instructions = set([ + 'swap', 'cx', 'cy', 'cz', 'csx', 'cp', 'cu1', 'cu2', 'cu3', 'rxx', + 'ryy', 'rzz', 'rzx']) + _3qubit_instructions = set(['ccx', 'cswap']) def __init__(self, basis_gates=None): """Initialize an empty noise model. @@ -155,7 +153,6 @@ def __init__(self, basis_gates=None): # dict(str: ReadoutError) # where the dict keys are the gate qubits. self._local_readout_errors = {} - self._x90_gates = [] @property def basis_gates(self): @@ -270,17 +267,22 @@ def from_backend(cls, backend, Raises: NoiseError: If the input backend is not valid. """ - if isinstance(backend, BaseBackend): + if isinstance(backend, (BaseBackend, Backend)): properties = backend.properties() + basis_gates = backend.configuration().basis_gates if not properties: raise NoiseError('Qiskit backend {} does not have a ' 'BackendProperties'.format(backend)) elif isinstance(backend, BackendProperties): properties = backend + basis_gates = set() + for prop in properties.gates: + basis_gates.add(prop.gate) + basis_gates = list(basis_gates) else: raise NoiseError('{} is not a Qiskit backend or' ' BackendProperties'.format(backend)) - noise_model = NoiseModel() + noise_model = NoiseModel(basis_gates=basis_gates) # Add single-qubit readout errors if readout_error: @@ -301,8 +303,27 @@ def from_backend(cls, backend, noise_model.add_quantum_error(error, name, qubits, warnings=warnings) return noise_model - def __repr__(self): - """Display noise model""" + def is_ideal(self): + """Return True if the noise model has no noise terms.""" + # Get default errors + if self._default_quantum_errors: + return False + if self._default_readout_error: + return False + if self._local_quantum_errors: + return False + if self._local_readout_errors: + return False + if self._nonlocal_quantum_errors: + return False + return True + + def __str__(self): + """Noise model string representation""" + + # Check if noise model is ideal + if self.is_ideal(): + return "NoiseModel: Ideal" # Get default errors default_error_ops = [] @@ -331,27 +352,21 @@ def __repr__(self): self._str2qubits(nq_str))) output = "NoiseModel:" - if default_error_ops == [] and local_error_ops == [] and nonlocal_error_ops == []: - output += " Ideal" - else: - output += "\n Basis gates: {}".format(self.basis_gates) - if self._noise_instructions: - output += "\n Instructions with noise: {}".format( - list(self._noise_instructions)) - if self._noise_qubits: - output += "\n Qubits with noise: {}".format( - list(self._noise_qubits)) - if self._x90_gates: - output += "\n X-90 based single qubit gates: {}".format( - list(self._x90_gates)) - if default_error_ops != []: - output += "\n All-qubits errors: {}".format(default_error_ops) - if local_error_ops != []: - output += "\n Specific qubit errors: {}".format( - local_error_ops) - if nonlocal_error_ops != []: - output += "\n Non-local specific qubit errors: {}".format( - nonlocal_error_ops) + output += "\n Basis gates: {}".format(self.basis_gates) + if self._noise_instructions: + output += "\n Instructions with noise: {}".format( + list(self._noise_instructions)) + if self._noise_qubits: + output += "\n Qubits with noise: {}".format( + list(self._noise_qubits)) + if default_error_ops != []: + output += "\n All-qubits errors: {}".format(default_error_ops) + if local_error_ops != []: + output += "\n Specific qubit errors: {}".format( + local_error_ops) + if nonlocal_error_ops != []: + output += "\n Non-local specific qubit errors: {}".format( + nonlocal_error_ops) return output def __eq__(self, other): @@ -398,35 +413,14 @@ def add_basis_gates(self, instructions, warnings=True): # If the instruction is in the default basis gates for the # QasmSimulator we add it to the basis gates. if name in self._QASMSIMULATOR_BASIS_GATES: - if name not in ['measure', 'reset']: + if name not in ['measure', 'reset', 'initialize', + 'kraus', 'superop', 'roerror']: self._basis_gates.add(name) elif warnings: logger.warning( "Warning: Adding a gate \"%s\" to basis_gates which is " "not in QasmSimulator basis_gates.", name) - def set_x90_single_qubit_gates(self, instructions): - """ - Declares X90 based gates for noise model. - - Args: - instructions (list[str] or - list[Instruction]): the instructions error applies to. - - Raises: - NoiseError: if the input instructions are not valid. - """ - warn('This function is deprecated and will be removed in a future release. ' - 'To use an X90 based noise model use the Sqrt(X) "sx" gate and one of ' - ' the single-qubit phase gates "u1", "rx", "p" in the noise model and ' - ' basis gates to decompose into this gateset for noise simulations.', - DeprecationWarning) - for name, label in self._instruction_names_labels(instructions): - # Add X-90 based gate to noisy gates - self._noise_instructions.add(label) - self._basis_gates.add(name) - self._x90_gates = instructions - def add_all_qubit_quantum_error(self, error, instructions, warnings=True): """ Add a quantum error to the noise model that applies to all qubits. @@ -785,7 +779,7 @@ def to_dict(self, serializable=False): error_dict["gate_qubits"] = [self._str2qubits(qubits_str)] error_list.append(error_dict) - ret = {"errors": error_list, "x90_gates": self._x90_gates} + ret = {"errors": error_list} if serializable: ret = json.loads(json.dumps(ret, cls=AerJSONEncoder)) @@ -808,9 +802,6 @@ def from_dict(noise_dict): # Return noise model noise_model = NoiseModel() - # Set X90 gates - noise_model.set_x90_single_qubit_gates(noise_dict.get('x90_gates', [])) - # Get error terms errors = noise_dict.get('errors', []) diff --git a/qiskit/providers/aer/noise/utils/__init__.py b/qiskit/providers/aer/noise/utils/__init__.py deleted file mode 100644 index 06712a5c62..0000000000 --- a/qiskit/providers/aer/noise/utils/__init__.py +++ /dev/null @@ -1,191 +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. -""" -The functions here have been moved to `qiskit.providers.aer.utils`. -""" - -import warnings as warn - -# DEPRECATED: these functions have been moved -from ...utils import remap_noise_model as _remap_noise_model -from ...utils import NoiseTransformer as _NoiseTransformer -from ...utils import approximate_quantum_error as _approximate_quantum_error -from ...utils import approximate_noise_model as _approximate_noise_model -from ...utils import insert_noise as _insert_noise - - -def remap_noise_model(noise_model, - remapping, - discard_qubits=False, - warnings=True): - """Remap qubits in a noise model. - - This remaps the specified gate qubits for local quantum errors, the gate - and noise qubits for non-local quantum errors, and the gate qubits for - local ReadoutErrors. All-qubit quantum and readout errors are unaffected. - - Args: - noise_model (NoiseModel): a noise model to remap qubits. - remapping (list): list or remappings of old qubit to new qubit. - See Additional Information. - discard_qubits (bool): if True discard qubits not in remapping keys, - if False an identity mapping wil be assumed - for unnamed qubits (Default: False). - warnings (bool): display warnings if qubits being remapped are not - in the input noise model (Default: True). - - Returns: - NoiseModel: a new noise model with the same errors but remapped - gate and noise qubits for local and non-local errors. - - Raises: - NoiseError: if remapping has duplicate qubits in the remapped qubits. - - Additional Information: - * The remapping map be specified as either a list of pairs: - ``[(old, new), ...]``, or a list of old qubits where the new qubit is - inferred from the position: ``[old0, old1, ...]`` is treated as - ``[(old0, 0), (old1, 1), ...]``. - - * If ``discard_qubits`` is ``False``, any qubits in the noise model not - specified in the list of old qubits will be added to the remapping as - a trivial mapping ``(qubit, qubit)``. - """ - warn.warn( - 'This function is been moved to `qiskit.providers.aer.utils.remap_noise_model`.' - ' Importing it from `qiskit.providers.aer.noise.utils` will be' - ' removed in a future release.', DeprecationWarning) - return _remap_noise_model(noise_model, - remapping, - discard_qubits=discard_qubits, - warnings=warnings) - - -def insert_noise(circuits, noise_model, transpile=False): - """Return a noisy version of a QuantumCircuit. - - Args: - circuits (QuantumCircuit or list[QuantumCircuit]): Input noise-free circuits. - noise_model (NoiseModel): The noise model containing the errors to add - transpile (Boolean): Should the circuit be transpiled into the noise model basis gates - - Returns: - QuantumCircuit: The new circuit with the Kraus noise instructions inserted. - - Additional Information: - The noisy circuit return by this function will consist of the - original circuit with ``Kraus`` instructions inserted after all - instructions referenced in the ``noise_model``. The resulting circuit - cannot be ran on a quantum computer but can be executed on the - :class:`~qiskit.providers.aer.QasmSimulator`. - """ - warn.warn( - 'This function is been moved to `qiskit.providers.aer.utils.insert_noise`.' - ' Importing it from `qiskit.providers.aer.noise.utils` will be' - ' removed in a future release.', DeprecationWarning) - return _insert_noise(circuits, noise_model, transpile=transpile) - - -def approximate_quantum_error(error, - *, - operator_string=None, - operator_dict=None, - operator_list=None): - """ - Return an approximate QuantumError bases on the Hilbert-Schmidt metric. - - Currently this is only implemented for 1-qubit QuantumErrors. - - Args: - error (QuantumError): the error to be approximated. - operator_string (string or None): a name for a pre-made set of - building blocks for the output channel (Default: None). - operator_dict (dict or None): a dictionary whose values are the - building blocks for the output channel (Default: None). - operator_list (dict or None): list of building blocks for the - output channel (Default: None). - - Returns: - QuantumError: the approximate quantum error. - - Raises: - NoiseError: if number of qubits is not supported or approximation - failed. - RuntimeError: If there's no information about the noise type. - - Additional Information: - The operator input precedence is: ``list`` < ``dict`` < ``str``. - If a string is given, dict is overwritten; if a dict is given, list is - overwritten. Oossible values for string are ``'pauli'``, ``'reset'``, - ``'clifford'``. - For further information see :meth:`NoiseTransformer.named_operators`. - """ - warn.warn( - 'This function is been moved to `qiskit.providers.aer.utils.approximate_qauntum_error`.' - ' Importing it from `qiskit.providers.aer.noise.utils` will be removed' - ' in a future release.', - DeprecationWarning) - return _approximate_quantum_error(error, - operator_string=operator_string, - operator_dict=operator_dict, - operator_list=operator_list) - - -def approximate_noise_model(model, - *, - operator_string=None, - operator_dict=None, - operator_list=None): - """ - Return an approximate noise model. - - Args: - model (NoiseModel): the noise model to be approximated. - operator_string (string or None): a name for a pre-made set of - building blocks for the output channel (Default: None). - operator_dict (dict or None): a dictionary whose values are the - building blocks for the output channel (Default: None). - operator_list (dict or None): list of building blocks for the - output channel (Default: None). - - Returns: - NoiseModel: the approximate noise model. - - Raises: - NoiseError: if number of qubits is not supported or approximation - failed. - - Additional Information: - The operator input precedence is: ``list`` < ``dict`` < ``str``. - If a string is given, dict is overwritten; if a dict is given, list is - overwritten. Oossible values for string are ``'pauli'``, ``'reset'``, - ``'clifford'``. - For further information see :meth:`NoiseTransformer.named_operators`. - """ - warn.warn( - 'This function is been moved to `qiskit.providers.aer.utils.approximate_noise_model`.' - ' Importing it from `qiskit.providers.aer.noise.utils` will be removed in a' - ' future release.', DeprecationWarning) - return _approximate_noise_model(model, - operator_string=operator_string, - operator_dict=operator_dict, - operator_list=operator_list) - - -class NoiseTransformer(_NoiseTransformer): - """Transforms one quantum channel to another based on a specified criteria.""" - def __init__(self): - warn.warn( - 'This function is been moved to `qiskit.providers.aer.utils.NoiseTransformer`.' - ' Importing it from `qiskit.providers.aer.noise.utils` will be removed in a' - ' future release.', DeprecationWarning) - super().__init__() diff --git a/qiskit/providers/aer/pulse/__init__.py b/qiskit/providers/aer/pulse/__init__.py index 079bdce1bb..cb588e5966 100644 --- a/qiskit/providers/aer/pulse/__init__.py +++ b/qiskit/providers/aer/pulse/__init__.py @@ -47,7 +47,6 @@ # pylint: disable=import-error import distutils.sysconfig # noqa import numpy as np -from .qutip_extra_lite.cy import pyxbuilder as pbldr from .system_models.duffing_model_generators import duffing_system_model from .system_models.pulse_system_model import PulseSystemModel @@ -56,8 +55,3 @@ CFG_VARS = distutils.sysconfig.get_config_vars() if "CFLAGS" in CFG_VARS: CFG_VARS["CFLAGS"] = CFG_VARS["CFLAGS"].replace("-Wstrict-prototypes", "") - -# Setup pyximport -# pylint: disable=no-member -pbldr.install(setup_args={'include_dirs': [np.get_include()]}) -del pbldr diff --git a/qiskit/providers/aer/pulse/controllers/digest_pulse_qobj.py b/qiskit/providers/aer/pulse/controllers/digest_pulse_qobj.py index 35d6484db9..b0a3d8a557 100644 --- a/qiskit/providers/aer/pulse/controllers/digest_pulse_qobj.py +++ b/qiskit/providers/aer/pulse/controllers/digest_pulse_qobj.py @@ -18,7 +18,7 @@ from collections import OrderedDict import numpy as np -from qiskit.providers.aer.aererror import AerError +from ...aererror import AerError # pylint: disable=no-name-in-module from .pulse_utils import oplist_to_array @@ -64,7 +64,7 @@ def __init__(self): self.experiments = None -def digest_pulse_qobj(qobj, channels, dt, qubit_list, backend_options=None): +def digest_pulse_qobj(qobj, channels, dt, qubit_list): """ Given a PulseQobj (and other parameters), returns a DigestedPulseQobj containing relevant extracted information @@ -73,7 +73,6 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list, backend_options=None): channels (OrderedDict): channel dictionary dt (float): pulse sample width qubit_list (list): list of qubits to include - backend_options (dict): dict with options that can override all other parameters Returns: DigestedPulseQobj: digested pulse qobj @@ -83,10 +82,6 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list, backend_options=None): AerError: for unsupported features or invalid qobj TypeError: for arguments of invalid type """ - - if backend_options is None: - backend_options = {} - digested_qobj = DigestedPulseQobj() qobj_dict = qobj.to_dict() @@ -95,10 +90,6 @@ def digest_pulse_qobj(qobj, channels, dt, qubit_list, backend_options=None): # raises errors for unsupported features _unsupported_errors(qobj_dict) - # override anything in qobj_config that is present in backend_options - for key in backend_options.keys(): - qobj_config[key] = backend_options[key] - if 'memory_slots' not in qobj_config: raise ValueError('Number of memory_slots must be specific in Qobj config') @@ -349,7 +340,9 @@ def experiment_to_structs(experiment, ham_chans, pulse_inds, pulse_to_int, dt, q structs['channels'][chan_name][1].extend([inst['t0'] * dt, inst['phase'], cond]) - + # Delay instruction + elif inst['name'] == 'delay': + pass # nothing to be done in this case # A standard pulse else: start = inst['t0'] * dt diff --git a/qiskit/providers/aer/pulse/controllers/mc_controller.py b/qiskit/providers/aer/pulse/controllers/mc_controller.py index 658bb2c17c..03e93faf1d 100644 --- a/qiskit/providers/aer/pulse/controllers/mc_controller.py +++ b/qiskit/providers/aer/pulse/controllers/mc_controller.py @@ -11,6 +11,11 @@ # 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. + +# This file is part of QuTiP: Quantum Toolbox in Python. +# +# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. +# All rights reserved. # pylint: disable=no-name-in-module, import-error, invalid-name """ @@ -24,7 +29,7 @@ from qiskit.tools.parallel import parallel_map, CPU_COUNT from .pulse_sim_options import PulseSimOptions from .pulse_de_solver import setup_de_solver -from .pulse_utils import (cy_expect_psi_csr, occ_probabilities, write_shots_memory, spmv_csr) +from .pulse_utils import (occ_probabilities, write_shots_memory, spmv, cy_expect_psi) dznrm2 = get_blas_funcs("znrm2", dtype=np.float64) @@ -46,10 +51,10 @@ def run_monte_carlo_experiments(pulse_sim_desc, pulse_de_model, solver_options=N solver_options = PulseSimOptions() if solver_options is None else solver_options - if not pulse_sim_desc.initial_state.isket: + if not pulse_sim_desc.initial_state.data.ndim != 1: raise Exception("Initial state must be a state vector.") - y0 = pulse_sim_desc.initial_state.full().ravel() + y0 = pulse_sim_desc.initial_state.data.ravel() # set num_cpus to the value given in settings if none in Options if not solver_options.num_cpus: @@ -153,6 +158,7 @@ def monte_carlo_evolution(seed, if not ODE.successful(): raise Exception("Integration step failed!") norm2_psi = dznrm2(ODE.y) ** 2 + if norm2_psi <= rand_vals[0]: # collapse has occured: # find collapse time to within specified tolerance @@ -192,21 +198,13 @@ def monte_carlo_evolution(seed, collapse_times.append(ODE.t) # all constant collapse operators. for i in range(n_dp.shape[0]): - n_dp[i] = cy_expect_psi_csr(pulse_de_model.n_ops_data[i], - pulse_de_model.n_ops_ind[i], - pulse_de_model.n_ops_ptr[i], - ODE.y, True) - + n_dp[i] = cy_expect_psi(pulse_de_model.n_ops_data[i], ODE.y, True) # determine which operator does collapse and store it _p = np.cumsum(n_dp / np.sum(n_dp)) j = cinds[_p >= rand_vals[1]][0] collapse_operators.append(j) - state = spmv_csr(pulse_de_model.c_ops_data[j], - pulse_de_model.c_ops_ind[j], - pulse_de_model.c_ops_ptr[j], - ODE.y) - + state = spmv(pulse_de_model.c_ops_data[j], ODE.y) state /= dznrm2(state) ODE.y = state rand_vals = rng.rand(2) @@ -214,6 +212,7 @@ def monte_carlo_evolution(seed, # after while loop (Do measurement or conditional) # ------------------------------------------------ out_psi = ODE.y / dznrm2(ODE.y) + for aind in range(acq_idx, num_acq): if exp['acquire'][aind][0] == stop_time: current_acq = exp['acquire'][aind] diff --git a/qiskit/providers/aer/pulse/controllers/pulse_controller.py b/qiskit/providers/aer/pulse/controllers/pulse_controller.py index c3a514f9a6..8e964cc5bc 100644 --- a/qiskit/providers/aer/pulse/controllers/pulse_controller.py +++ b/qiskit/providers/aer/pulse/controllers/pulse_controller.py @@ -19,23 +19,21 @@ from warnings import warn import numpy as np +from qiskit.quantum_info.operators.operator import Operator from ..system_models.string_model_parser.string_model_parser import NoiseParser -from ..qutip_extra_lite import qobj_generators as qobj_gen +from ..system_models.string_model_parser import operator_generators as op_gen from .digest_pulse_qobj import digest_pulse_qobj -from ..qutip_extra_lite.qobj import Qobj from .pulse_sim_options import PulseSimOptions from .unitary_controller import run_unitary_experiments from .mc_controller import run_monte_carlo_experiments from .pulse_utils import get_ode_rhs_functor -def pulse_controller(qobj, system_model, backend_options): +def pulse_controller(qobj): """ Interprets PulseQobj input, runs simulations, and returns results Parameters: - qobj (qobj): pulse qobj containing a list of pulse schedules - system_model (PulseSystemModel): contains system model information - backend_options (dict): dict of options, which overrides other parameters + qobj (PulseQobj): pulse qobj containing a list of pulse schedules Returns: list: simulation results @@ -44,22 +42,17 @@ def pulse_controller(qobj, system_model, backend_options): ValueError: if input is of incorrect format Exception: for invalid ODE options """ - pulse_sim_desc = PulseSimDescription() pulse_de_model = PulseInternalDEModel() - if backend_options is None: - backend_options = {} - - noise_model = backend_options.get('noise_model', None) - - # post warnings for unsupported features - _unsupported_warnings(noise_model) + config = qobj.config # ############################### # ### Extract model parameters # ############################### + system_model = config.system_model + # Get qubit list and number qubit_list = system_model.subsystem_list if qubit_list is None: @@ -81,11 +74,11 @@ def pulse_controller(qobj, system_model, backend_options): dim_qub = ham_model._subsystem_dims dim_osc = {} # convert estates into a Qutip qobj - estates = [qobj_gen.state(state) for state in ham_model._estates.T[:]] + estates = [op_gen.state(state) for state in ham_model._estates.T[:]] # initial state set here - if 'initial_state' in backend_options: - pulse_sim_desc.initial_state = Qobj(backend_options['initial_state']) + if hasattr(config, 'initial_state'): + pulse_sim_desc.initial_state = op_gen.state(config.initial_state) else: pulse_sim_desc.initial_state = estates[0] @@ -96,6 +89,11 @@ def pulse_controller(qobj, system_model, backend_options): pulse_de_model.dt = system_model.dt # Parse noise + noise_model = getattr(config, 'noise_model', None) + + # post warnings for unsupported features + _unsupported_warnings(noise_model) + if noise_model: noise = NoiseParser(noise_dict=noise_model, dim_osc=dim_osc, dim_qub=dim_qub) noise.parse() @@ -110,8 +108,7 @@ def pulse_controller(qobj, system_model, backend_options): digested_qobj = digest_pulse_qobj(qobj, pulse_de_model.channels, system_model.dt, - qubit_list, - backend_options) + qubit_list) # extract simulation-description level qobj content pulse_sim_desc.shots = digested_qobj.shots @@ -133,12 +130,14 @@ def pulse_controller(qobj, system_model, backend_options): # if it wasn't specified in the PulseQobj, draw from system_model if qubit_lo_freq is None: - qubit_lo_freq = system_model._qubit_freq_est + default_freq = getattr(config, 'qubit_freq_est', [np.inf]) + if default_freq != [np.inf]: + qubit_lo_freq = default_freq - # if still None draw from the Hamiltonian + # if still None, or is the placeholder value draw from the Hamiltonian if qubit_lo_freq is None: qubit_lo_freq = system_model.hamiltonian.get_qubit_lo_from_drift() - warn('Warning: qubit_lo_freq was not specified in PulseQobj or in PulseSystemModel, ' + + warn('Warning: qubit_lo_freq was not specified in PulseQobj and there is no default, ' 'so it is beign automatically determined from the drift Hamiltonian.') pulse_de_model.freqs = system_model.calculate_channel_frequencies(qubit_lo_freq=qubit_lo_freq) @@ -147,14 +146,15 @@ def pulse_controller(qobj, system_model, backend_options): # ### Parse backend_options # # solver-specific information should be extracted in the solver # ############################### - pulse_sim_desc.seed = int(backend_options['seed']) if 'seed' in backend_options else None - pulse_sim_desc.q_level_meas = int(backend_options.get('q_level_meas', 1)) + + pulse_sim_desc.seed = int(config.seed) if hasattr(config, 'seed') else None + pulse_sim_desc.q_level_meas = int(getattr(config, 'q_level_meas', 1)) # solver options allowed_solver_options = ['atol', 'rtol', 'nsteps', 'max_step', 'num_cpus', 'norm_tol', 'norm_steps', 'method'] - solver_options = backend_options.get('solver_options', {}) + solver_options = getattr(config, 'solver_options', {}) for key in solver_options: if key not in allowed_solver_options: raise Exception('Invalid solver_option: {}'.format(key)) @@ -189,12 +189,12 @@ def pulse_controller(qobj, system_model, backend_options): if not pulse_sim_desc.measurement_ops[qubit_list.index(jj)]: q_level_meas = pulse_sim_desc.q_level_meas pulse_sim_desc.measurement_ops[qubit_list.index(jj)] = \ - qobj_gen.qubit_occ_oper_dressed(jj, - estates, - h_osc=dim_osc, - h_qub=dim_qub, - level=q_level_meas - ) + op_gen.qubit_occ_oper_dressed(jj, + estates, + h_osc=dim_osc, + h_qub=dim_qub, + level=q_level_meas + ) if not exp['can_sample']: pulse_sim_desc.can_sample = False @@ -203,7 +203,12 @@ def pulse_controller(qobj, system_model, backend_options): else run_monte_carlo_experiments) exp_results, exp_times = run_experiments(pulse_sim_desc, pulse_de_model, solver_options) - return format_exp_results(exp_results, exp_times, pulse_sim_desc) + output = { + 'results': format_exp_results(exp_results, exp_times, pulse_sim_desc), + 'success': True, + 'qobj_id': qobj.qobj_id + } + return output def format_exp_results(exp_results, exp_times, pulse_sim_desc): @@ -288,7 +293,6 @@ def format_exp_results(exp_results, exp_times, pulse_sim_desc): results['data']['memory'] = results['data']['memory'][0] all_results.append(results) - return all_results @@ -348,16 +352,10 @@ def __init__(self): self.num_h_terms = None self.c_num = None self.c_ops_data = None - self.c_ops_ind = None - self.c_ops_ptr = None self.n_ops_data = None - self.n_ops_ind = None - self.n_ops_ptr = None self.h_diag_elems = None self.h_ops_data = None - self.h_ops_ind = None - self.h_ops_ptr = None self._rhs_dict = None @@ -373,45 +371,33 @@ def _config_internal_data(self): H = [hpart[0] for hpart in self.system] self.num_h_terms = num_h_terms - # take care of collapse operators, if any - self.c_num = 0 - if self.noise: - self.c_num = len(self.noise) - self.num_h_terms += 1 - self.c_ops_data = [] - self.c_ops_ind = [] - self.c_ops_ptr = [] self.n_ops_data = [] - self.n_ops_ind = [] - self.n_ops_ptr = [] self.h_diag_elems = self.h_diag # if there are any collapse operators - H_noise = 0 - for kk in range(self.c_num): - c_op = self.noise[kk] - n_op = c_op.dag() * c_op - # collapse ops - self.c_ops_data.append(c_op.data.data) - self.c_ops_ind.append(c_op.data.indices) - self.c_ops_ptr.append(c_op.data.indptr) - # norm ops - self.n_ops_data.append(n_op.data.data) - self.n_ops_ind.append(n_op.data.indices) - self.n_ops_ptr.append(n_op.data.indptr) - # Norm ops added to time-independent part of - # Hamiltonian to decrease norm - H_noise -= 0.5j * n_op - - if H_noise: + self.c_num = 0 + + if self.noise: + self.c_num = len(self.noise) + self.num_h_terms += 1 + H_noise = Operator(np.zeros(self.noise[0].data.shape)) + for kk in range(self.c_num): + c_op = self.noise[kk] + n_op = c_op.adjoint() @ c_op + # collapse ops + self.c_ops_data.append(c_op.data) + # norm ops + self.n_ops_data.append(n_op.data) + # Norm ops added to time-independent part of + # Hamiltonian to decrease norm + H_noise = Operator(H_noise.data - 0.5j * n_op.data) + H = H + [H_noise] # construct data sets - self.h_ops_data = [-1.0j * hpart.data.data for hpart in H] - self.h_ops_ind = [hpart.data.indices for hpart in H] - self.h_ops_ptr = [hpart.data.indptr for hpart in H] + self.h_ops_data = [-1.0j * hpart.data for hpart in H] self._rhs_dict = {'freqs': list(self.freqs.values()), 'pulse_array': self.pulse_array, @@ -420,8 +406,6 @@ def _config_internal_data(self): 'vars_names': self.vars_names, 'num_h_terms': self.num_h_terms, 'h_ops_data': self.h_ops_data, - 'h_ops_ind': self.h_ops_ind, - 'h_ops_ptr': self.h_ops_ptr, 'h_diag_elems': self.h_diag_elems} def init_rhs(self, exp): diff --git a/qiskit/providers/aer/pulse/controllers/unitary_controller.py b/qiskit/providers/aer/pulse/controllers/unitary_controller.py index f76957158e..0397e12f25 100644 --- a/qiskit/providers/aer/pulse/controllers/unitary_controller.py +++ b/qiskit/providers/aer/pulse/controllers/unitary_controller.py @@ -24,7 +24,6 @@ from .pulse_sim_options import PulseSimOptions from .pulse_de_solver import setup_de_solver -# Imports from qutip_extra_lite from .pulse_utils import occ_probabilities, write_shots_memory dznrm2 = get_blas_funcs("znrm2", dtype=np.float64) @@ -83,10 +82,10 @@ def run_unitary_experiments(pulse_sim_desc, pulse_de_model, solver_options=None) solver_options = PulseSimOptions() if solver_options is None else solver_options - if not pulse_sim_desc.initial_state.isket: + if not pulse_sim_desc.initial_state.data.ndim != 1: raise Exception("Initial state must be a state vector.") - y0 = pulse_sim_desc.initial_state.full().ravel() + y0 = pulse_sim_desc.initial_state.data.ravel() # set num_cpus to the value given in settings if none in Options if not solver_options.num_cpus: diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/__init__.py b/qiskit/providers/aer/pulse/qutip_extra_lite/__init__.py deleted file mode 100644 index 1b3f375cbe..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -"""Temporary folder containing trimmed down qutip objects and functions -""" diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/CMakeLists.txt b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/CMakeLists.txt deleted file mode 100644 index a5529c2acf..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -include(Linter) -include(cython_utils) -# We need to remove the -static flag, because Python Extension system only supports -# dynamic linked libraries, but we want to build a shared libraries with the least -# dependencies we can, so some of these dependencies are linked statically into our -# shared library. -string(REPLACE " -static " "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - -set(CYTHON_INSTALL_DIR "qiskit/providers/aer/pulse/qutip_extra_lite/cy") -add_cython_module(spmath) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/__init__.py b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/complex_math.pxi b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/complex_math.pxi deleted file mode 100644 index 43f0fd1793..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/complex_math.pxi +++ /dev/null @@ -1,24 +0,0 @@ -cdef extern from "" namespace "std" nogil: - double abs(double complex x) - double complex acos(double complex x) - double complex acosh(double complex x) - double arg(double complex x) - double complex asin(double complex x) - double complex asinh(double complex x) - double complex atan(double complex x) - double complex atanh(double complex x) - double complex conj(double complex x) - double complex cos(double complex x) - double complex cosh(double complex x) - double complex exp(double complex x) - double imag(double complex x) - double complex log(double complex x) - double complex log10(double complex x) - double norm(double complex x) - double complex proj(double complex x) - double real(double complex x) - double complex sin(double complex x) - double complex sinh(double complex x) - double complex sqrt(double complex x) - double complex tan(double complex x) - double complex tanh(double complex x) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/parameters.pxi b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/parameters.pxi deleted file mode 100644 index 66705ba064..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/parameters.pxi +++ /dev/null @@ -1,14 +0,0 @@ -import numpy as np -cimport numpy as cnp - -DTYPE = np.float64 -ctypedef cnp.float64_t DTYPE_t - -ITYPE = np.int32 -ctypedef cnp.int32_t ITYPE_t - -CTYPE = np.complex128 -ctypedef cnp.complex128_t CTYPE_t - -CTYPE = np.int64 -ctypedef cnp.int64_t LTYPE_t diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/pyxbuilder.py b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/pyxbuilder.py deleted file mode 100644 index b7f9792bba..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/pyxbuilder.py +++ /dev/null @@ -1,76 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -"""Utility for making the build options for compiling the Hamiltonian""" -import sys -import os -import pyximport -from pyximport import install - -__all__ = ["install"] - -OLD_EXT = pyximport.pyximport.get_distutils_extension - - -def new_get_distutils_extension(modname, pyxfilename, language_level=None): - """Get the distutils extension""" - extension_mod, setup_args = OLD_EXT(modname, pyxfilename, language_level) - extension_mod.language = 'c++' - # If on Win and Python version >= 3.5 and not in MSYS2 (i.e. Visual studio compile) - if sys.platform == 'win32' and \ - (int(str(sys.version_info[0]) + str(sys.version_info[1])) >= 35) \ - and os.environ.get('MSYSTEM') is None: - - extension_mod.extra_compile_args = ['/w', '/O1'] - else: - extension_mod.extra_compile_args = ['-w', '-O1'] - if sys.platform == 'darwin': - extension_mod.extra_compile_args.append('-mmacosx-version-min=10.9') - extension_mod.extra_link_args = ['-mmacosx-version-min=10.9'] - return extension_mod, setup_args - - -pyximport.pyximport.get_distutils_extension = new_get_distutils_extension diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_routines.pxi b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_routines.pxi deleted file mode 100644 index 2c2cec8786..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_routines.pxi +++ /dev/null @@ -1,265 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -import numpy as np -from scipy.sparse import coo_matrix -from ..fastsparse import fast_csr_matrix -cimport numpy as np -cimport cython -from libcpp.algorithm cimport sort -from libcpp.vector cimport vector -from .sparse_structs cimport CSR_Matrix -np.import_array() -from libc.string cimport memset - -cdef extern from "numpy/arrayobject.h" nogil: - void PyArray_ENABLEFLAGS(np.ndarray arr, int flags) - void PyDataMem_FREE(void * ptr) - void PyDataMem_RENEW(void * ptr, size_t size) - void PyDataMem_NEW(size_t size) - - -#Struct used for CSR indices sorting -cdef struct _data_ind_pair: - double complex data - int ind - -ctypedef _data_ind_pair data_ind_pair -ctypedef int (*cfptr)(data_ind_pair, data_ind_pair) - - -cdef void raise_error_CSR(int E, CSR_Matrix * C = NULL): - if not C.numpy_lock and C != NULL: - free_CSR(C) - if E == -1: - raise MemoryError('Could not allocate memory.') - elif E == -2: - raise Exception('Error manipulating CSR_Matrix structure.') - elif E == -3: - raise Exception('CSR_Matrix is not initialized.') - elif E == -4: - raise Exception('NumPy already has lock on data.') - elif E == -5: - raise Exception('Cannot expand data structures past max_length.') - elif E == -6: - raise Exception('CSR_Matrix cannot be expanded.') - elif E == -7: - raise Exception('Data length cannot be larger than max_length') - else: - raise Exception('Error in Cython code.') - -cdef inline int int_min(int a, int b) nogil: - return b if b < a else a - -cdef inline int int_max(int a, int b) nogil: - return a if a > b else b - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void init_CSR(CSR_Matrix * mat, int nnz, int nrows, int ncols = 0, - int max_length = 0, int init_zeros = 1): - """ - Initialize CSR_Matrix struct. Matrix is assumed to be square with - shape nrows x nrows. Manually set mat.ncols otherwise - - Parameters - ---------- - mat : CSR_Matrix * - Pointer to struct. - nnz : int - Length of data and indices arrays. Also number of nonzero elements - nrows : int - Number of rows in matrix. Also gives length - of indptr array (nrows+1). - ncols : int (default = 0) - Number of cols in matrix. Default is ncols = nrows. - max_length : int (default = 0) - Maximum length of data and indices arrays. Used for resizing. - Default value of zero indicates no resizing. - """ - if max_length == 0: - max_length = nnz - if nnz > max_length: - raise_error_CSR(-7, mat) - if init_zeros: - mat.data = PyDataMem_NEW(nnz * sizeof(double complex)) - memset(&mat.data[0],0,nnz * sizeof(double complex)) - else: - mat.data = PyDataMem_NEW(nnz * sizeof(double complex)) - if mat.data == NULL: - raise_error_CSR(-1, mat) - if init_zeros: - mat.indices = PyDataMem_NEW(nnz * sizeof(int)) - mat.indptr = PyDataMem_NEW((nrows+1) * sizeof(int)) - memset(&mat.indices[0],0,nnz * sizeof(int)) - memset(&mat.indptr[0],0,(nrows+1) * sizeof(int)) - else: - mat.indices = PyDataMem_NEW(nnz * sizeof(int)) - mat.indptr = PyDataMem_NEW((nrows+1) * sizeof(int)) - mat.nnz = nnz - mat.nrows = nrows - if ncols == 0: - mat.ncols = nrows - else: - mat.ncols = ncols - mat.is_set = 1 - mat.max_length = max_length - mat.numpy_lock = 0 - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void copy_CSR(CSR_Matrix * out, CSR_Matrix * mat): - """ - Copy a CSR_Matrix. - """ - cdef size_t kk - if not mat.is_set: - raise_error_CSR(-3) - elif out.is_set: - raise_error_CSR(-2) - init_CSR(out, mat.nnz, mat.nrows, mat.nrows, mat.max_length) - # We cannot use memcpy here since there are issues with - # doing so on Win with the GCC compiler - for kk in range(mat.nnz): - out.data[kk] = mat.data[kk] - out.indices[kk] = mat.indices[kk] - for kk in range(mat.nrows+1): - out.indptr[kk] = mat.indptr[kk] - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void free_CSR(CSR_Matrix * mat): - """ - Manually free CSR_Matrix data structures if - data is not locked by NumPy. - """ - if not mat.numpy_lock and mat.is_set: - if mat.data != NULL: - PyDataMem_FREE(mat.data) - if mat.indices != NULL: - PyDataMem_FREE(mat.indices) - if mat.indptr != NULL: - PyDataMem_FREE(mat.indptr) - mat.is_set = 0 - else: - raise_error_CSR(-2) - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void shorten_CSR(CSR_Matrix * mat, int N): - """ - Shortends the length of CSR data and indices arrays. - """ - if (not mat.numpy_lock) and mat.is_set: - mat.data = PyDataMem_RENEW(mat.data, N * sizeof(double complex)) - mat.indices = PyDataMem_RENEW(mat.indices, N * sizeof(int)) - mat.nnz = N - else: - if mat.numpy_lock: - raise_error_CSR(-4, mat) - elif not mat.is_set: - raise_error_CSR(-3, mat) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef object CSR_to_scipy(CSR_Matrix * mat): - """ - Converts a CSR_Matrix struct to a SciPy csr_matrix class object. - The NumPy arrays are generated from the pointers, and the lifetime - of the pointer memory is tied to that of the NumPy array - (i.e. automatic garbage cleanup.) - - Parameters - ---------- - mat : CSR_Matrix * - Pointer to CSR_Matrix. - """ - cdef np.npy_intp dat_len, ptr_len - cdef np.ndarray[complex, ndim=1] _data - cdef np.ndarray[int, ndim=1] _ind, _ptr - if (not mat.numpy_lock) and mat.is_set: - dat_len = mat.nnz - ptr_len = mat.nrows+1 - _data = np.PyArray_SimpleNewFromData(1, &dat_len, np.NPY_COMPLEX128, mat.data) - PyArray_ENABLEFLAGS(_data, np.NPY_OWNDATA) - - _ind = np.PyArray_SimpleNewFromData(1, &dat_len, np.NPY_INT32, mat.indices) - PyArray_ENABLEFLAGS(_ind, np.NPY_OWNDATA) - - _ptr = np.PyArray_SimpleNewFromData(1, &ptr_len, np.NPY_INT32, mat.indptr) - PyArray_ENABLEFLAGS(_ptr, np.NPY_OWNDATA) - mat.numpy_lock = 1 - return fast_csr_matrix((_data, _ind, _ptr), shape=(mat.nrows,mat.ncols)) - else: - if mat.numpy_lock: - raise_error_CSR(-4) - elif not mat.is_set: - raise_error_CSR(-3) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef int ind_sort(data_ind_pair x, data_ind_pair y): - return x.ind < y.ind - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void sort_indices(CSR_Matrix * mat): - """ - Sorts the indices of a CSR_Matrix inplace. - """ - cdef size_t ii, jj - cdef vector[data_ind_pair] pairs - cdef cfptr cfptr_ = &ind_sort - cdef int row_start, row_end, length - - for ii in range(mat.nrows): - row_start = mat.indptr[ii] - row_end = mat.indptr[ii+1] - length = row_end - row_start - pairs.resize(length) - - for jj in range(length): - pairs[jj].data = mat.data[row_start+jj] - pairs[jj].ind = mat.indices[row_start+jj] - - sort(pairs.begin(),pairs.end(),cfptr_) - - for jj in range(length): - mat.data[row_start+jj] = pairs[jj].data - mat.indices[row_start+jj] = pairs[jj].ind \ No newline at end of file diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_structs.pxd b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_structs.pxd deleted file mode 100644 index 48e96cbca5..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_structs.pxd +++ /dev/null @@ -1,47 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -cdef struct _csr_mat: - double complex * data - int * indices - int * indptr - int nnz - int nrows - int ncols - int is_set - int max_length - int numpy_lock - -ctypedef _csr_mat CSR_Matrix \ No newline at end of file diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_utils.pyx b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_utils.pyx deleted file mode 100644 index 57bcf6109d..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/sparse_utils.pyx +++ /dev/null @@ -1,379 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -import numpy as np -from ..fastsparse import fast_csr_matrix -cimport numpy as cnp -from libc.math cimport abs, fabs, sqrt -from libcpp cimport bool -cimport cython -cnp.import_array() -from libc.string cimport memset - -cdef extern from "numpy/arrayobject.h" nogil: - void PyArray_ENABLEFLAGS(cnp.ndarray arr, int flags) - void PyDataMem_FREE(void * ptr) - void PyDataMem_RENEW(void * ptr, size_t size) - void PyDataMem_NEW(size_t size) - - -cdef extern from "" namespace "std" nogil: - double abs(double complex x) - double real(double complex x) - double imag(double complex x) - -cdef extern from "" namespace "std" nogil: - double cabs "abs" (double complex x) - -cdef inline int int_max(int x, int y): - return x ^ ((x ^ y) & -(x < y)) - -include "parameters.pxi" - -@cython.boundscheck(False) -@cython.wraparound(False) -def _sparse_bandwidth( - int[::1] idx, - int[::1] ptr, - int nrows): - """ - Calculates the max (mb), lower(lb), and upper(ub) bandwidths of a - csr_matrix. - """ - cdef int ldist - cdef int lb = -nrows - cdef int ub = -nrows - cdef int mb = 0 - cdef size_t ii, jj - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii + 1]): - ldist = ii - idx[jj] - lb = int_max(lb, ldist) - ub = int_max(ub, -ldist) - mb = int_max(mb, ub + lb + 1) - - return mb, lb, ub - - -@cython.boundscheck(False) -@cython.wraparound(False) -def _sparse_profile(int[::1] idx, - int[::1] ptr, - int nrows): - cdef int ii, jj, temp, ldist=0 - cdef LTYPE_t pro = 0 - for ii in range(nrows): - temp = 0 - for jj in range(ptr[ii], ptr[ii + 1]): - ldist = idx[jj] - ii - temp = int_max(temp, ldist) - pro += temp - return pro - - -@cython.boundscheck(False) -@cython.wraparound(False) -def _sparse_permute( - cnp.ndarray[cython.numeric, ndim=1] data, - int[::1] idx, - int[::1] ptr, - int nrows, - int ncols, - cnp.ndarray[ITYPE_t, ndim=1] rperm, - cnp.ndarray[ITYPE_t, ndim=1] cperm, - int flag): - """ - Permutes the rows and columns of a sparse CSR or CSC matrix according to - the permutation arrays rperm and cperm, respectively. - Here, the permutation arrays specify the new order of the rows and columns. - i.e. [0,1,2,3,4] -> [3,0,4,1,2]. - """ - cdef int ii, jj, kk, k0, nnz - cdef cnp.ndarray[cython.numeric] new_data = np.zeros_like(data) - cdef cnp.ndarray[ITYPE_t] new_idx = np.zeros_like(idx) - cdef cnp.ndarray[ITYPE_t] new_ptr = np.zeros_like(ptr) - cdef cnp.ndarray[ITYPE_t] perm_r - cdef cnp.ndarray[ITYPE_t] perm_c - cdef cnp.ndarray[ITYPE_t] inds - - if flag == 0: # CSR matrix - if rperm.shape[0] != 0: - inds = np.argsort(rperm).astype(ITYPE) - perm_r = np.arange(rperm.shape[0], dtype=ITYPE)[inds] - - for jj in range(nrows): - ii = perm_r[jj] - new_ptr[ii + 1] = ptr[jj + 1] - ptr[jj] - - for jj in range(nrows): - new_ptr[jj + 1] = new_ptr[jj+1] + new_ptr[jj] - - for jj in range(nrows): - k0 = new_ptr[perm_r[jj]] - for kk in range(ptr[jj], ptr[jj + 1]): - new_idx[k0] = idx[kk] - new_data[k0] = data[kk] - k0 = k0 + 1 - - if cperm.shape[0] != 0: - inds = np.argsort(cperm).astype(ITYPE) - perm_c = np.arange(cperm.shape[0], dtype=ITYPE)[inds] - nnz = new_ptr[new_ptr.shape[0] - 1] - for jj in range(nnz): - new_idx[jj] = perm_c[new_idx[jj]] - - elif flag == 1: # CSC matrix - if cperm.shape[0] != 0: - inds = np.argsort(cperm).astype(ITYPE) - perm_c = np.arange(cperm.shape[0], dtype=ITYPE)[inds] - - for jj in range(ncols): - ii = perm_c[jj] - new_ptr[ii + 1] = ptr[jj + 1] - ptr[jj] - - for jj in range(ncols): - new_ptr[jj + 1] = new_ptr[jj + 1] + new_ptr[jj] - - for jj in range(ncols): - k0 = new_ptr[perm_c[jj]] - for kk in range(ptr[jj], ptr[jj + 1]): - new_idx[k0] = idx[kk] - new_data[k0] = data[kk] - k0 = k0 + 1 - - if rperm.shape[0] != 0: - inds = np.argsort(rperm).astype(ITYPE) - perm_r = np.arange(rperm.shape[0], dtype=ITYPE)[inds] - nnz = new_ptr[new_ptr.shape[0] - 1] - for jj in range(nnz): - new_idx[jj] = perm_r[new_idx[jj]] - - return new_data, new_idx, new_ptr - - -@cython.boundscheck(False) -@cython.wraparound(False) -def _sparse_reverse_permute( - cnp.ndarray[cython.numeric, ndim=1] data, - int[::1] idx, - int[::1] ptr, - int nrows, - int ncols, - cnp.ndarray[ITYPE_t, ndim=1] rperm, - cnp.ndarray[ITYPE_t, ndim=1] cperm, - int flag): - """ - Reverse permutes the rows and columns of a sparse CSR or CSC matrix - according to the original permutation arrays rperm and cperm, respectively. - """ - cdef int ii, jj, kk, k0, nnz - cdef cnp.ndarray[cython.numeric, ndim=1] new_data = np.zeros_like(data) - cdef cnp.ndarray[ITYPE_t, ndim=1] new_idx = np.zeros_like(idx) - cdef cnp.ndarray[ITYPE_t, ndim=1] new_ptr = np.zeros_like(ptr) - - if flag == 0: # CSR matrix - if rperm.shape[0] != 0: - for jj in range(nrows): - ii = rperm[jj] - new_ptr[ii + 1] = ptr[jj + 1] - ptr[jj] - - for jj in range(nrows): - new_ptr[jj + 1] = new_ptr[jj + 1] + new_ptr[jj] - - for jj in range(nrows): - k0 = new_ptr[rperm[jj]] - for kk in range(ptr[jj], ptr[jj + 1]): - new_idx[k0] = idx[kk] - new_data[k0] = data[kk] - k0 = k0 + 1 - - if cperm.shape[0] > 0: - nnz = new_ptr[new_ptr.shape[0] - 1] - for jj in range(nnz): - new_idx[jj] = cperm[new_idx[jj]] - - if flag == 1: # CSC matrix - if cperm.shape[0] != 0: - for jj in range(ncols): - ii = cperm[jj] - new_ptr[ii + 1] = ptr[jj + 1] - ptr[jj] - - for jj in range(ncols): - new_ptr[jj + 1] = new_ptr[jj + 1] + new_ptr[jj] - - for jj in range(ncols): - k0 = new_ptr[cperm[jj]] - for kk in range(ptr[jj], ptr[jj + 1]): - new_idx[k0] = idx[kk] - new_data[k0] = data[kk] - k0 = k0 + 1 - - if cperm.shape[0] != 0: - nnz = new_ptr[new_ptr.shape[0] - 1] - for jj in range(nnz): - new_idx[jj] = rperm[new_idx[jj]] - - return new_data, new_idx, new_ptr - - -@cython.boundscheck(False) -@cython.wraparound(False) -def _isdiag(int[::1] idx, - int[::1] ptr, - int nrows): - - cdef int row, num_elems - for row in range(nrows): - num_elems = ptr[row+1] - ptr[row] - if num_elems > 1: - return 0 - elif num_elems == 1: - if idx[ptr[row]] != row: - return 0 - return 1 - - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef cnp.ndarray[complex, ndim=1, mode='c'] _csr_get_diag(complex[::1] data, - int[::1] idx, int[::1] ptr, int k=0): - - cdef size_t row, jj - cdef int num_rows = ptr.shape[0]-1 - cdef int abs_k = abs(k) - cdef int start, stop - cdef cnp.ndarray[complex, ndim=1, mode='c'] out = np.zeros(num_rows-abs_k, dtype=complex) - - if k >= 0: - start = 0 - stop = num_rows-abs_k - else: #k < 0 - start = abs_k - stop = num_rows - - for row in range(start, stop): - for jj in range(ptr[row], ptr[row+1]): - if idx[jj]-k == row: - out[row-start] = data[jj] - break - return out - - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def unit_row_norm(complex[::1] data, int[::1] ptr, int nrows): - cdef size_t row, ii - cdef double total - for row in range(nrows): - total = 0 - for ii in range(ptr[row], ptr[row+1]): - total += real(data[ii]) * real(data[ii]) + imag(data[ii]) * imag(data[ii]) - total = sqrt(total) - for ii in range(ptr[row], ptr[row+1]): - data[ii] /= total - - - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef double zcsr_one_norm(complex[::1] data, int[::1] ind, int[::1] ptr, - int nrows, int ncols): - - cdef int k - cdef size_t ii, jj - cdef double * col_sum = PyDataMem_NEW(ncols * sizeof(double)) - cdef double max_col = 0 - - memset(&col_sum[0],0,ncols * sizeof(double)) - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] - col_sum[k] += cabs(data[jj]) - for ii in range(ncols): - if col_sum[ii] > max_col: - max_col = col_sum[ii] - PyDataMem_FREE(col_sum) - return max_col - - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef double zcsr_inf_norm(complex[::1] data, int[::1] ind, int[::1] ptr, - int nrows, int ncols): - - cdef int k - cdef size_t ii, jj - cdef double * row_sum = PyDataMem_NEW(nrows * sizeof(double)) - cdef double max_row = 0 - - memset(&row_sum[0],0,nrows * sizeof(double)) - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - row_sum[ii] += cabs(data[jj]) - for ii in range(nrows): - if row_sum[ii] > max_row: - max_row = row_sum[ii] - PyDataMem_FREE(row_sum) - return max_row - - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef bool cy_tidyup(complex[::1] data, double atol, unsigned int nnz): - """ - Performs an in-place tidyup of CSR matrix data - """ - cdef size_t kk - cdef double re, im - cdef bool re_flag, im_flag, out_flag = 0 - for kk in range(nnz): - re_flag = 0 - im_flag = 0 - re = real(data[kk]) - im = imag(data[kk]) - if fabs(re) < atol: - re = 0 - re_flag = 1 - if fabs(im) < atol: - im = 0 - im_flag = 1 - - if re_flag or im_flag: - data[kk] = re + 1j*im - - if re_flag and im_flag: - out_flag = 1 - return out_flag diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pxd b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pxd deleted file mode 100644 index ea64bed657..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pxd +++ /dev/null @@ -1,39 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -from .sparse_structs cimport CSR_Matrix - -cdef void fdense2D_to_CSR(complex[::1, :] mat, CSR_Matrix * out, - unsigned int nrows, unsigned int ncols) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pyx b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pyx deleted file mode 100644 index 147df97c29..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spconvert.pyx +++ /dev/null @@ -1,303 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -import numpy as np -from ..fastsparse import fast_csr_matrix -cimport numpy as cnp -cimport cython -from libc.stdlib cimport div, malloc, free - -cdef extern from "stdlib.h": - ctypedef struct div_t: - int quot - int rem - -include "sparse_routines.pxi" - -@cython.boundscheck(False) -@cython.wraparound(False) -def arr_coo2fast(complex[::1] data, int[::1] rows, int[::1] cols, int nrows, int ncols): - """ - Converts a set of ndarrays (data, rows, cols) that specify a COO sparse matrix - to CSR format. - """ - cdef int nnz = data.shape[0] - cdef COO_Matrix mat - mat.data = &data[0] - mat.rows = &rows[0] - mat.cols = &cols[0] - mat.nrows = nrows - mat.ncols = ncols - mat.nnz = nnz - mat.is_set = 1 - mat.max_length = nnz - - cdef CSR_Matrix out - COO_to_CSR(&out, &mat) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -def dense2D_to_fastcsr_cmode(complex[:, ::1] mat, int nrows, int ncols): - """ - Converts a dense c-mode complex ndarray to a sparse CSR matrix. - - Parameters - ---------- - mat : ndarray - Input complex ndarray - nrows : int - Number of rows in matrix. - ncols : int - Number of cols in matrix. - - Returns - ------- - out : fast_csr_matrix - Output matrix in CSR format. - """ - cdef int nnz = 0 - cdef size_t ii, jj - cdef np.ndarray[complex, ndim=1, mode='c'] data = np.zeros(nrows*ncols, dtype=complex) - cdef np.ndarray[int, ndim=1, mode='c'] ind = np.zeros(nrows*ncols, dtype=np.int32) - cdef np.ndarray[int, ndim=1, mode='c'] ptr = np.zeros(nrows+1, dtype=np.int32) - - for ii in range(nrows): - for jj in range(ncols): - if mat[ii,jj] != 0: - ind[nnz] = jj - data[nnz] = mat[ii,jj] - nnz += 1 - ptr[ii+1] = nnz - - if nnz < (nrows*ncols): - return fast_csr_matrix((data[:nnz], ind[:nnz], ptr), shape=(nrows,ncols)) - else: - return fast_csr_matrix((data, ind, ptr), shape=(nrows,ncols)) - - -@cython.boundscheck(False) -@cython.wraparound(False) -def dense1D_to_fastcsr_ket(complex[::1] vec): - """ - Converts a dense c-mode complex ndarray to a sparse CSR matrix. - - Parameters - ---------- - mat : ndarray - Input complex ndarray - - Returns - ------- - out : fast_csr_matrix - Output matrix in CSR format. - """ - cdef int nnz = 0 - cdef size_t ii, nrows = vec.shape[0] - cdef np.ndarray[complex, ndim=1, mode='c'] data = np.zeros(nrows, dtype=complex) - cdef np.ndarray[int, ndim=1, mode='c'] ind = np.zeros(nrows, dtype=np.int32) - cdef np.ndarray[int, ndim=1, mode='c'] ptr = np.zeros(nrows+1, dtype=np.int32) - - for ii in range(nrows): - if vec[ii] != 0: - data[nnz] = vec[ii] - nnz += 1 - ptr[ii+1] = nnz - - if nnz < (nrows): - return fast_csr_matrix((data[:nnz], ind[:nnz], ptr), shape=(nrows,1)) - else: - return fast_csr_matrix((data, ind, ptr), shape=(nrows,1)) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void fdense2D_to_CSR(complex[::1, :] mat, CSR_Matrix * out, - unsigned int nrows, unsigned int ncols): - """ - Converts a dense complex ndarray to a CSR matrix struct. - - Parameters - ---------- - mat : ndarray - Input complex ndarray - nrows : int - Number of rows in matrix. - ncols : int - Number of cols in matrix. - - Returns - ------- - out : CSR_Matrix - Output matrix as CSR struct. - """ - cdef int nnz = 0 - cdef size_t ii, jj - init_CSR(out, nrows*ncols, nrows, ncols, nrows*ncols) - - for ii in range(nrows): - for jj in range(ncols): - if mat[ii,jj] != 0: - out.indices[nnz] = jj - out.data[nnz] = mat[ii,jj] - nnz += 1 - out.indptr[ii+1] = nnz - - if nnz < (nrows*ncols): - shorten_CSR(out, nnz) - - -@cython.boundscheck(False) -@cython.wraparound(False) -def dense2D_to_fastcsr_fmode(complex[::1, :] mat, int nrows, int ncols): - """ - Converts a dense fortran-mode complex ndarray to a sparse CSR matrix. - - Parameters - ---------- - mat : ndarray - Input complex ndarray - nrows : int - Number of rows in matrix. - ncols : int - Number of cols in matrix. - - Returns - ------- - out : fast_csr_matrix - Output matrix in CSR format. - """ - cdef int nnz = 0 - cdef size_t ii, jj - cdef np.ndarray[complex, ndim=1, mode='c'] data = np.zeros(nrows*ncols, dtype=complex) - cdef np.ndarray[int, ndim=1, mode='c'] ind = np.zeros(nrows*ncols, dtype=np.int32) - cdef np.ndarray[int, ndim=1, mode='c'] ptr = np.zeros(nrows+1, dtype=np.int32) - - for ii in range(nrows): - for jj in range(ncols): - if mat[ii,jj] != 0: - ind[nnz] = jj - data[nnz] = mat[ii,jj] - nnz += 1 - ptr[ii+1] = nnz - - if nnz < (nrows*ncols): - return fast_csr_matrix((data[:nnz], ind[:nnz], ptr), shape=(nrows,ncols)) - else: - return fast_csr_matrix((data, ind, ptr), shape=(nrows,ncols)) - - - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_reshape(object A not None, int new_rows, int new_cols): - """ - Reshapes a complex CSR matrix. - - Parameters - ---------- - A : fast_csr_matrix - Input CSR matrix. - new_rows : int - Number of rows in reshaped matrix. - new_cols : int - Number of cols in reshaped matrix. - - Returns - ------- - out : fast_csr_matrix - Reshaped CSR matrix. - - Notes - ----- - This routine does not need to make a temp. copy of the matrix. - """ - cdef CSR_Matrix inmat = CSR_from_scipy(A) - cdef COO_Matrix mat - CSR_to_COO(&mat, &inmat) - cdef CSR_Matrix out - cdef div_t new_inds - cdef size_t kk - - if (mat.nrows * mat.ncols) != (new_rows * new_cols): - raise Exception('Total size of array must be unchanged.') - - for kk in range(mat.nnz): - new_inds = div(mat.ncols*mat.rows[kk]+mat.cols[kk], new_cols) - mat.rows[kk] = new_inds.quot - mat.cols[kk] = new_inds.rem - - mat.nrows = new_rows - mat.ncols = new_cols - - COO_to_CSR_inplace(&out, &mat) - sort_indices(&out) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -@cython.cdivision(True) -def cy_index_permute(int [::1] idx_arr, - int [::1] dims, - int [::1] order): - - cdef int ndims = dims.shape[0] - cdef int ii, n, dim, idx, orderr - - #the fastest way to allocate memory for a temporary array - cdef int * multi_idx = malloc(sizeof(int) * ndims) - - try: - for ii from 0 <= ii < idx_arr.shape[0]: - idx = idx_arr[ii] - - #First, decompose long index into multi-index - for n from ndims > n >= 0: - dim = dims[n] - multi_idx[n] = idx % dim - idx = idx // dim - - #Finally, assemble new long index from reordered multi-index - dim = 1 - idx = 0 - for n from ndims > n >= 0: - orderr = order[n] - idx += multi_idx[orderr] * dim - dim *= dims[orderr] - - idx_arr[ii] = idx - finally: - free(multi_idx) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pxd b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pxd deleted file mode 100644 index 8d31956057..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pxd +++ /dev/null @@ -1,49 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### - -cimport numpy as cnp -cimport cython -from libcpp cimport bool - -include "parameters.pxi" - -cpdef cnp.ndarray[CTYPE_t, ndim=1, mode="c"] spmv_csr(complex[::1] data, - int[::1] ind, int[::1] ptr, complex[::1] vec) - -cpdef cy_expect_psi_csr(complex[::1] data, - int[::1] ind, - int[::1] ptr, - complex[::1] vec, - bool isherm) \ No newline at end of file diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pyx b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pyx deleted file mode 100644 index f3c6d60f81..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmatfuncs.pyx +++ /dev/null @@ -1,100 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -import numpy as np -cimport numpy as cnp -cimport cython -cimport libc.math -from libcpp cimport bool - -cdef extern from "src/zspmv.hpp" nogil: - void zspmvpy(double complex *data, int *ind, int *ptr, double complex *vec, - double complex a, double complex *out, int nrows) - -include "complex_math.pxi" - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef cnp.ndarray[complex, ndim=1, mode="c"] spmv_csr(complex[::1] data, - int[::1] ind, int[::1] ptr, complex[::1] vec): - """ - Sparse matrix, dense vector multiplication. - Here the vector is assumed to have one-dimension. - Matrix must be in CSR format and have complex entries. - - Parameters - ---------- - data : array - Data for sparse matrix. - idx : array - Indices for sparse matrix data. - ptr : array - Pointers for sparse matrix data. - vec : array - Dense vector for multiplication. Must be one-dimensional. - - Returns - ------- - out : array - Returns dense array. - - """ - cdef unsigned int num_rows = ptr.shape[0] - 1 - cdef cnp.ndarray[complex, ndim=1, mode="c"] out = np.zeros((num_rows), dtype=np.complex) - zspmvpy(&data[0], &ind[0], &ptr[0], &vec[0], 1.0, &out[0], num_rows) - return out - -@cython.boundscheck(False) -@cython.wraparound(False) -cpdef cy_expect_psi_csr(complex[::1] data, - int[::1] ind, - int[::1] ptr, - complex[::1] vec, - bool isherm): - - cdef size_t row, jj - cdef int nrows = vec.shape[0] - cdef complex expt = 0, temp, cval - - for row in range(nrows): - cval = conj(vec[row]) - temp = 0 - for jj in range(ptr[row], ptr[row+1]): - temp += data[jj]*vec[ind[jj]] - expt += cval*temp - - if isherm : - return real(expt) - else: - return expt \ No newline at end of file diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmath.pyx b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmath.pyx deleted file mode 100644 index 30938cea30..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/spmath.pyx +++ /dev/null @@ -1,423 +0,0 @@ -#!python -#cython: language_level=3 -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -import numpy as np -cimport numpy as cnp -cimport cython -from libcpp cimport bool -from libc.string cimport memset - -cdef extern from "" namespace "std" nogil: - double complex conj(double complex x) - double real(double complex) - double imag(double complex) - double abs(double complex) - -include "sparse_routines.pxi" - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_mult(object A, object B, int sorted = 1): - - cdef complex [::1] dataA = A.data - cdef int[::1] indsA = A.indices - cdef int[::1] indptrA = A.indptr - cdef int Annz = A.nnz - - cdef complex [::1] dataB = B.data - cdef int[::1] indsB = B.indices - cdef int[::1] indptrB = B.indptr - cdef int Bnnz = B.nnz - - cdef int nrows = A.shape[0] - cdef int ncols = B.shape[1] - - #Both matrices are zero mats - if Annz == 0 or Bnnz == 0: - return fast_csr_matrix(shape=(nrows,ncols)) - - cdef int nnz - cdef CSR_Matrix out - - nnz = _zcsr_mult_pass1(&dataA[0], &indsA[0], &indptrA[0], - &dataB[0], &indsB[0], &indptrB[0], - nrows, ncols) - - if nnz == 0: - return fast_csr_matrix(shape=(nrows,ncols)) - - init_CSR(&out, nnz, nrows, ncols) - _zcsr_mult_pass2(&dataA[0], &indsA[0], &indptrA[0], - &dataB[0], &indsB[0], &indptrB[0], - &out, - nrows, ncols) - - #Shorten data and indices if needed - if out.nnz > out.indptr[out.nrows]: - shorten_CSR(&out, out.indptr[out.nrows]) - - if sorted: - sort_indices(&out) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef int _zcsr_mult_pass1(double complex * Adata, int * Aind, int * Aptr, - double complex * Bdata, int * Bind, int * Bptr, - int nrows, int ncols) nogil: - - cdef int j, k, nnz = 0 - cdef size_t ii,jj,kk - #Setup mask array - cdef int * mask = PyDataMem_NEW(ncols*sizeof(int)) - for ii in range(ncols): - mask[ii] = -1 - #Pass 1 - for ii in range(nrows): - for jj in range(Aptr[ii], Aptr[ii+1]): - j = Aind[jj] - for kk in range(Bptr[j], Bptr[j+1]): - k = Bind[kk] - if mask[k] != ii: - mask[k] = ii - nnz += 1 - PyDataMem_FREE(mask) - return nnz - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void _zcsr_mult_pass2(double complex * Adata, int * Aind, int * Aptr, - double complex * Bdata, int * Bind, int * Bptr, - CSR_Matrix * C, - int nrows, int ncols) nogil: - - cdef int head, length, temp, j, k, nnz = 0 - cdef size_t ii,jj,kk - cdef double complex val - cdef double complex * sums = PyDataMem_NEW(ncols * sizeof(double complex)) - cdef int * nxt = PyDataMem_NEW(ncols*sizeof(int)) - - memset(&sums[0],0,ncols * sizeof(double complex)) - for ii in range(ncols): - nxt[ii] = -1 - - C.indptr[0] = 0 - for ii in range(nrows): - head = -2 - length = 0 - for jj in range(Aptr[ii], Aptr[ii+1]): - j = Aind[jj] - val = Adata[jj] - for kk in range(Bptr[j], Bptr[j+1]): - k = Bind[kk] - sums[k] += val*Bdata[kk] - if nxt[k] == -1: - nxt[k] = head - head = k - length += 1 - - for jj in range(length): - if sums[head] != 0: - C.indices[nnz] = head - C.data[nnz] = sums[head] - nnz += 1 - temp = head - head = nxt[head] - nxt[temp] = -1 - sums[temp] = 0 - - C.indptr[ii+1] = nnz - - #Free temp arrays - PyDataMem_FREE(sums) - PyDataMem_FREE(nxt) - - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_kron(object A, object B): - """ - Computes the kronecker product between two complex - sparse matrices in CSR format. - """ - cdef complex[::1] dataA = A.data - cdef int[::1] indsA = A.indices - cdef int[::1] indptrA = A.indptr - cdef int rowsA = A.shape[0] - cdef int colsA = A.shape[1] - - cdef complex[::1] dataB = B.data - cdef int[::1] indsB = B.indices - cdef int[::1] indptrB = B.indptr - cdef int rowsB = B.shape[0] - cdef int colsB = B.shape[1] - - cdef int out_nnz = _safe_multiply(dataA.shape[0], dataB.shape[0]) - cdef int rows_out = rowsA * rowsB - cdef int cols_out = colsA * colsB - - cdef CSR_Matrix out - init_CSR(&out, out_nnz, rows_out, cols_out) - - _zcsr_kron_core(&dataA[0], &indsA[0], &indptrA[0], - &dataB[0], &indsB[0], &indptrB[0], - &out, - rowsA, rowsB, colsB) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void _zcsr_kron_core(double complex * dataA, int * indsA, int * indptrA, - double complex * dataB, int * indsB, int * indptrB, - CSR_Matrix * out, - int rowsA, int rowsB, int colsB) nogil: - cdef size_t ii, jj, ptrA, ptr - cdef int row = 0 - cdef int ptr_start, ptr_end - cdef int row_startA, row_endA, row_startB, row_endB, distA, distB, ptrB - - for ii in range(rowsA): - row_startA = indptrA[ii] - row_endA = indptrA[ii+1] - distA = row_endA - row_startA - - for jj in range(rowsB): - row_startB = indptrB[jj] - row_endB = indptrB[jj+1] - distB = row_endB - row_startB - - ptr_start = out.indptr[row] - ptr_end = ptr_start + distB - - out.indptr[row+1] = out.indptr[row] + distA * distB - row += 1 - - for ptrA in range(row_startA, row_endA): - ptrB = row_startB - for ptr in range(ptr_start, ptr_end): - out.indices[ptr] = indsA[ptrA] * colsB + indsB[ptrB] - out.data[ptr] = dataA[ptrA] * dataB[ptrB] - ptrB += 1 - - ptr_start += distB - ptr_end += distB - - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_transpose(object A): - """ - Transpose of a sparse matrix in CSR format. - """ - cdef complex[::1] data = A.data - cdef int[::1] ind = A.indices - cdef int[::1] ptr = A.indptr - cdef int nrows = A.shape[0] - cdef int ncols = A.shape[1] - - cdef CSR_Matrix out - init_CSR(&out, data.shape[0], ncols, nrows) - - _zcsr_trans_core(&data[0], &ind[0], &ptr[0], - &out, nrows, ncols) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void _zcsr_trans_core(double complex * data, int * ind, int * ptr, - CSR_Matrix * out, - int nrows, int ncols) nogil: - - cdef int k, nxt - cdef size_t ii, jj - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] + 1 - out.indptr[k] += 1 - - for ii in range(ncols): - out.indptr[ii+1] += out.indptr[ii] - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] - nxt = out.indptr[k] - out.data[nxt] = data[jj] - out.indices[nxt] = ii - out.indptr[k] = nxt + 1 - - for ii in range(ncols,0,-1): - out.indptr[ii] = out.indptr[ii-1] - - out.indptr[0] = 0 - - - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_adjoint(object A): - """ - Adjoint of a sparse matrix in CSR format. - """ - cdef complex[::1] data = A.data - cdef int[::1] ind = A.indices - cdef int[::1] ptr = A.indptr - cdef int nrows = A.shape[0] - cdef int ncols = A.shape[1] - - cdef CSR_Matrix out - init_CSR(&out, data.shape[0], ncols, nrows) - - _zcsr_adjoint_core(&data[0], &ind[0], &ptr[0], - &out, nrows, ncols) - return CSR_to_scipy(&out) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef void _zcsr_adjoint_core(double complex * data, int * ind, int * ptr, - CSR_Matrix * out, - int nrows, int ncols) nogil: - - cdef int k, nxt - cdef size_t ii, jj - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] + 1 - out.indptr[k] += 1 - - for ii in range(ncols): - out.indptr[ii+1] += out.indptr[ii] - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] - nxt = out.indptr[k] - out.data[nxt] = conj(data[jj]) - out.indices[nxt] = ii - out.indptr[k] = nxt + 1 - - for ii in range(ncols,0,-1): - out.indptr[ii] = out.indptr[ii-1] - - out.indptr[0] = 0 - - -@cython.boundscheck(False) -@cython.wraparound(False) -def zcsr_isherm(object A not None, double tol = 1e-12): - """ - Determines if a given input sparse CSR matrix is Hermitian - to within a specified floating-point tolerance. - - Parameters - ---------- - A : csr_matrix - Input sparse matrix. - tol : float (default is atol from settings) - Desired tolerance value. - - Returns - ------- - isherm : int - One if matrix is Hermitian, zero otherwise. - - Notes - ----- - This implimentation is esentially an adjoint calulation - where the data and indices are not stored, but checked - elementwise to see if they match those of the input matrix. - Thus we do not need to build the actual adjoint. Here we - only need a temp array of output indptr. - """ - cdef complex[::1] data = A.data - cdef int[::1] ind = A.indices - cdef int[::1] ptr = A.indptr - cdef int nrows = A.shape[0] - cdef int ncols = A.shape[1] - - cdef int k, nxt, isherm = 1 - cdef size_t ii, jj - cdef complex tmp, tmp2 - - if nrows != ncols: - return 0 - - cdef int * out_ptr = PyDataMem_NEW( (ncols+1) * sizeof(int)) - - memset(&out_ptr[0],0,(ncols+1) * sizeof(int)) - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] + 1 - out_ptr[k] += 1 - - for ii in range(nrows): - out_ptr[ii+1] += out_ptr[ii] - - for ii in range(nrows): - for jj in range(ptr[ii], ptr[ii+1]): - k = ind[jj] - nxt = out_ptr[k] - out_ptr[k] += 1 - #structure test - if ind[nxt] != ii: - isherm = 0 - break - tmp = conj(data[jj]) - tmp2 = data[nxt] - #data test - if abs(tmp-tmp2) > tol: - isherm = 0 - break - else: - continue - break - - PyDataMem_FREE(out_ptr) - return isherm - -@cython.overflowcheck(True) -cdef _safe_multiply(int A, int B): - """ - Computes A*B and checks for overflow. - """ - cdef int C = A*B - return C \ No newline at end of file diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/utilities.py b/qiskit/providers/aer/pulse/qutip_extra_lite/cy/utilities.py deleted file mode 100644 index 1c9cd5ffdf..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/cy/utilities.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name, no-name-in-module, import-error -"""Cython utilities""" -import os - - -def _cython_build_cleanup(tdname, build_dir=None): - """Cleanup cython build files - """ - if build_dir is None: - build_dir = os.path.join(os.path.expanduser('~'), '.pyxbld') - - # Remove tdname.pyx - pyx_file = tdname + ".pyx" - try: - os.remove(pyx_file) - except OSError: - pass - - # Remove temp build files - for dirpath, _, files in os.walk(build_dir): - for file in files: - if file.startswith(tdname): - try: - os.remove(os.path.join(dirpath, file)) - except OSError: - pass diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/dimensions.py b/qiskit/providers/aer/pulse/qutip_extra_lite/dimensions.py deleted file mode 100644 index 4a6d70c14a..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/dimensions.py +++ /dev/null @@ -1,134 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name -""" -Internal use module for manipulating dims specifications. -""" - -__all__ = [] -# Everything should be explicitly imported, not made available -# by default. - -import numpy as np - - -def flatten(the_list): - """Flattens a list of lists to the first level. - - Given a list containing a mix of scalars and lists, - flattens down to a list of the scalars within the original - list. - - Args: - the_list (list): Input list - - Returns: - list: Flattened list. - - """ - if not isinstance(the_list, list): - return [the_list] - else: - return sum(map(flatten, the_list), []) - - -def is_scalar(dims): - """ - Returns True if a dims specification is effectively - a scalar (has dimension 1). - """ - return np.prod(flatten(dims)) == 1 - - -def is_vector(dims): - """Is a vector""" - return ( - isinstance(dims, list) and - isinstance(dims[0], (int, np.integer)) - ) - - -def is_vectorized_oper(dims): - """Is a vectorized operator.""" - return ( - isinstance(dims, list) and - isinstance(dims[0], list) - ) - - -# pylint: disable=too-many-return-statements -def type_from_dims(dims, enforce_square=True): - """Get the type of operator from dims structure""" - bra_like, ket_like = map(is_scalar, dims) - - if bra_like: - if is_vector(dims[1]): - return 'bra' - elif is_vectorized_oper(dims[1]): - return 'operator-bra' - - if ket_like: - if is_vector(dims[0]): - return 'ket' - elif is_vectorized_oper(dims[0]): - return 'operator-ket' - - elif is_vector(dims[0]) and (dims[0] == dims[1] or not enforce_square): - return 'oper' - - elif ( - is_vectorized_oper(dims[0]) and - ( - ( - dims[0] == dims[1] and - dims[0][0] == dims[1][0] - ) or not enforce_square - ) - ): - return 'super' - - return 'other' diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/fastsparse.py b/qiskit/providers/aer/pulse/qutip_extra_lite/fastsparse.py deleted file mode 100644 index e822c1ce7e..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/fastsparse.py +++ /dev/null @@ -1,440 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, The QuTiP Project. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name - -""" -Module for fast versions of SciPy sparse CSR matrices -""" - -import operator -from warnings import warn -import numpy as np -from scipy.sparse import (_sparsetools, isspmatrix, csr_matrix, dia_matrix) -from scipy.sparse.sputils import (upcast, isdense, isscalarlike, get_index_dtype) -from scipy.sparse.base import SparseEfficiencyWarning - - -class fast_csr_matrix(csr_matrix): - """ - A subclass of scipy.sparse.csr_matrix that skips the data format - checks that are run everytime a new csr_matrix is created. - """ - # pylint: disable=super-init-not-called - def __init__(self, args=None, shape=None, dtype=None, copy=False): - if args is None: # Build zero matrix - if shape is None: - raise Exception('Shape must be given when building zero matrix.') - self.data = np.array([], dtype=complex) - self.indices = np.array([], dtype=np.int32) - self.indptr = np.zeros(shape[0] + 1, dtype=np.int32) - self._shape = tuple(int(s) for s in shape) - - else: - if args[0].shape[0] and args[0].dtype != complex: - raise TypeError('fast_csr_matrix allows only complex data.') - if args[1].shape[0] and args[1].dtype != np.int32: - raise TypeError('fast_csr_matrix allows only int32 indices.') - if args[2].shape[0] and args[1].dtype != np.int32: - raise TypeError('fast_csr_matrix allows only int32 indptr.') - self.data = np.array(args[0], dtype=complex, copy=copy) - self.indices = np.array(args[1], dtype=np.int32, copy=copy) - self.indptr = np.array(args[2], dtype=np.int32, copy=copy) - if shape is None: - self._shape = tuple([len(self.indptr) - 1] * 2) - else: - self._shape = tuple(int(s) for s in shape) - self.dtype = complex - self.maxprint = 50 - self.format = 'csr' - - def _binopt(self, other, op): - """ - Do the binary operation fn to two sparse matrices using - fast_csr_matrix only when other is also a fast_csr_matrix. - """ - # e.g. csr_plus_csr, csr_minus_csr, etc. - if not isinstance(other, fast_csr_matrix): - other = csr_matrix(other) - # e.g. csr_plus_csr, csr_minus_csr, etc. - fn = getattr(_sparsetools, self.format + op + self.format) - - maxnnz = self.nnz + other.nnz - idx_dtype = get_index_dtype((self.indptr, self.indices, - other.indptr, other.indices), - maxval=maxnnz) - indptr = np.empty(self.indptr.shape, dtype=idx_dtype) - indices = np.empty(maxnnz, dtype=idx_dtype) - - bool_ops = ['_ne_', '_lt_', '_gt_', '_le_', '_ge_'] - if op in bool_ops: - data = np.empty(maxnnz, dtype=np.bool_) - else: - data = np.empty(maxnnz, dtype=upcast(self.dtype, other.dtype)) - - fn(self.shape[0], self.shape[1], - np.asarray(self.indptr, dtype=idx_dtype), - np.asarray(self.indices, dtype=idx_dtype), - self.data, - np.asarray(other.indptr, dtype=idx_dtype), - np.asarray(other.indices, dtype=idx_dtype), - other.data, - indptr, indices, data) - - actual_nnz = indptr[-1] - indices = indices[:actual_nnz] - data = data[:actual_nnz] - if actual_nnz < maxnnz // 2: - # too much waste, trim arrays - indices = indices.copy() - data = data.copy() - if isinstance(other, fast_csr_matrix) and (op not in bool_ops): - A = fast_csr_matrix((data, indices, indptr), dtype=data.dtype, shape=self.shape) - else: - A = csr_matrix((data, indices, indptr), dtype=data.dtype, shape=self.shape) - return A - - # pylint: disable=too-many-return-statements - def multiply(self, other): - """Point-wise multiplication by another matrix, vector, or - scalar. - """ - # Scalar multiplication. - if isscalarlike(other): - return self._mul_scalar(other) - # Sparse matrix or vector. - if isspmatrix(other): - if self.shape == other.shape: - if not isinstance(other, fast_csr_matrix): - other = csr_matrix(other) - return self._binopt(other, '_elmul_') - # Single element. - elif other.shape == (1, 1): - return self._mul_scalar(other.toarray()[0, 0]) - elif self.shape == (1, 1): - return other._mul_scalar(self.toarray()[0, 0]) - # A row times a column. - elif self.shape[1] == other.shape[0] and self.shape[1] == 1: - return self._mul_sparse_matrix(other.tocsc()) - elif self.shape[0] == other.shape[1] and self.shape[0] == 1: - return other._mul_sparse_matrix(self.tocsc()) - # Row vector times matrix. other is a row. - elif other.shape[0] == 1 and self.shape[1] == other.shape[1]: - other = dia_matrix((other.toarray().ravel(), [0]), - shape=(other.shape[1], other.shape[1])) - return self._mul_sparse_matrix(other) - # self is a row. - elif self.shape[0] == 1 and self.shape[1] == other.shape[1]: - copy = dia_matrix((self.toarray().ravel(), [0]), - shape=(self.shape[1], self.shape[1])) - return other._mul_sparse_matrix(copy) - # Column vector times matrix. other is a column. - elif other.shape[1] == 1 and self.shape[0] == other.shape[0]: - other = dia_matrix((other.toarray().ravel(), [0]), - shape=(other.shape[0], other.shape[0])) - return other._mul_sparse_matrix(self) - # self is a column. - elif self.shape[1] == 1 and self.shape[0] == other.shape[0]: - copy = dia_matrix((self.toarray().ravel(), [0]), - shape=(self.shape[0], self.shape[0])) - return copy._mul_sparse_matrix(other) - else: - raise ValueError("inconsistent shapes") - # Dense matrix. - if isdense(other): - if self.shape == other.shape: - ret = self.tocoo() - ret.data = np.multiply(ret.data, other[ret.row, ret.col] - ).view(np.ndarray).ravel() - return ret - # Single element. - elif other.size == 1: - return self._mul_scalar(other.flat[0]) - # Anything else. - return np.multiply(self.todense(), other) - - def _mul_sparse_matrix(self, other): - """ - Do the sparse matrix mult returning fast_csr_matrix only - when other is also fast_csr_matrix. - """ - M, _ = self.shape - _, N = other.shape - - major_axis = self._swap((M, N))[0] - if isinstance(other, fast_csr_matrix): - A = zcsr_mult(self, other, sorted=1) - return A - - other = csr_matrix(other) # convert to this format - idx_dtype = get_index_dtype((self.indptr, self.indices, - other.indptr, other.indices), - maxval=M * N) - indptr = np.empty(major_axis + 1, dtype=idx_dtype) - - fn = getattr(_sparsetools, self.format + '_matmat_pass1') - fn(M, N, - np.asarray(self.indptr, dtype=idx_dtype), - np.asarray(self.indices, dtype=idx_dtype), - np.asarray(other.indptr, dtype=idx_dtype), - np.asarray(other.indices, dtype=idx_dtype), - indptr) - - nnz = indptr[-1] - idx_dtype = get_index_dtype((self.indptr, self.indices, - other.indptr, other.indices), - maxval=nnz) - indptr = np.asarray(indptr, dtype=idx_dtype) - indices = np.empty(nnz, dtype=idx_dtype) - data = np.empty(nnz, dtype=upcast(self.dtype, other.dtype)) - - fn = getattr(_sparsetools, self.format + '_matmat_pass2') - fn(M, N, np.asarray(self.indptr, dtype=idx_dtype), - np.asarray(self.indices, dtype=idx_dtype), - self.data, - np.asarray(other.indptr, dtype=idx_dtype), - np.asarray(other.indices, dtype=idx_dtype), - other.data, - indptr, indices, data) - A = csr_matrix((data, indices, indptr), shape=(M, N)) - return A - - def _scalar_binopt(self, other, op): - """Scalar version of self._binopt, for cases in which no new nonzeros - are added. Produces a new spmatrix in canonical form. - """ - self.sum_duplicates() - res = self._with_data(op(self.data, other), copy=True) - res.eliminate_zeros() - return res - - # pylint: disable=too-many-return-statements - def __eq__(self, other): - # Scalar other. - if isscalarlike(other): - if np.isnan(other): - return csr_matrix(self.shape, dtype=np.bool_) - - if other == 0: - warn("Comparing a sparse matrix with 0 using == is inefficient" - ", try using != instead.", SparseEfficiencyWarning) - all_true = _all_true(self.shape) - inv = self._scalar_binopt(other, operator.ne) - return all_true - inv - else: - return self._scalar_binopt(other, operator.eq) - # Dense other. - elif isdense(other): - return self.todense() == other - # Sparse other. - elif isspmatrix(other): - warn("Comparing sparse matrices using == is inefficient, try using" - " != instead.", SparseEfficiencyWarning) - # TODO sparse broadcasting - if self.shape != other.shape: - return False - elif self.format != other.format: - other = other.asformat(self.format) - res = self._binopt(other, '_ne_') - all_true = _all_true(self.shape) - return all_true - res - else: - return False - - # pylint: disable=too-many-return-statements - def __ne__(self, other): - # Scalar other. - if isscalarlike(other): - if np.isnan(other): - warn("Comparing a sparse matrix with nan using != is inefficient", - SparseEfficiencyWarning) - all_true = _all_true(self.shape) - return all_true - elif other != 0: - warn("Comparing a sparse matrix with a nonzero scalar using !=" - " is inefficient, try using == instead.", SparseEfficiencyWarning) - all_true = _all_true(self.shape) - inv = self._scalar_binopt(other, operator.eq) - return all_true - inv - else: - return self._scalar_binopt(other, operator.ne) - # Dense other. - elif isdense(other): - return self.todense() != other - # Sparse other. - elif isspmatrix(other): - # TODO sparse broadcasting - if self.shape != other.shape: - return True - elif self.format != other.format: - other = other.asformat(self.format) - return self._binopt(other, '_ne_') - else: - return True - - def _inequality(self, other, op, op_name, bad_scalar_msg): - # Scalar other. - if isscalarlike(other): - if other == 0 and op_name in ('_le_', '_ge_'): - raise NotImplementedError(" >= and <= don't work with 0.") - - if op(0, other): - warn(bad_scalar_msg, SparseEfficiencyWarning) - other_arr = np.empty(self.shape, dtype=np.result_type(other)) - other_arr.fill(other) - other_arr = csr_matrix(other_arr) - return self._binopt(other_arr, op_name) - else: - return self._scalar_binopt(other, op) - # Dense other. - elif isdense(other): - return op(self.todense(), other) - # Sparse other. - elif isspmatrix(other): - # TODO sparse broadcasting - if self.shape != other.shape: - raise ValueError("inconsistent shapes") - if self.format != other.format: - other = other.asformat(self.format) - if op_name not in ('_ge_', '_le_'): - return self._binopt(other, op_name) - - warn("Comparing sparse matrices using >= and <= is inefficient, " - "using <, >, or !=, instead.", SparseEfficiencyWarning) - all_true = _all_true(self.shape) - res = self._binopt(other, '_gt_' if op_name == '_le_' else '_lt_') - return all_true - res - else: - raise ValueError("Operands could not be compared.") - - def _with_data(self, data, copy=True): - """Returns a matrix with the same sparsity structure as self, - but with different data. By default the structure arrays - (i.e. .indptr and .indices) are copied. - """ - # We need this just in case something like abs(data) gets called - # does nothing if data.dtype is complex. - data = np.asarray(data, dtype=complex) - if copy: - return fast_csr_matrix((data, self.indices.copy(), self.indptr.copy()), - shape=self.shape, dtype=data.dtype) - else: - return fast_csr_matrix((data, self.indices, self.indptr), - shape=self.shape, dtype=data.dtype) - - # pylint: disable=arguments-differ - def transpose(self): - """ - Returns the transpose of the matrix, keeping - it in fast_csr format. - """ - return zcsr_transpose(self) - - def trans(self): - """ - Same as transpose - """ - return zcsr_transpose(self) - - def getH(self): - """ - Returns the conjugate-transpose of the matrix, keeping - it in fast_csr format. - """ - return zcsr_adjoint(self) - - def adjoint(self): - """ - Same as getH - """ - return zcsr_adjoint(self) - - -def csr2fast(A, copy=False): - """Converts a SciPy CSR matrix - to the internal fast version. - - Args: - A (csr_matrx): Input csr_matrix. - copy (bool): Make a copy of the data arrays. - - Returns: - fast_csr: The equivilent fast CSR matrix. - """ - if (not isinstance(A, fast_csr_matrix)) or copy: - # Do not need to do any type checking here - # since fast_csr_matrix does that. - return fast_csr_matrix((A.data, A.indices, A.indptr), - shape=A.shape, copy=copy) - else: - return A - - -def fast_identity(N): - """Generates a sparse identity matrix in - fast_csr format. - """ - data = np.ones(N, dtype=complex) - ind = np.arange(N, dtype=np.int32) - ptr = np.arange(N + 1, dtype=np.int32) - ptr[-1] = N - return fast_csr_matrix((data, ind, ptr), shape=(N, N)) - - -# Convenience functions -# -------------------- -def _all_true(shape): - A = csr_matrix((np.ones(np.prod(shape), dtype=np.bool_), - np.tile(np.arange(shape[1], dtype=np.int32), shape[0]), - np.arange(0, np.prod(shape) + 1, shape[1], dtype=np.int32)), - shape=shape) - return A - - -# Need to do some trailing imports here -# ------------------------------------- -# pylint: disable=no-name-in-module, wrong-import-position, import-error -from .cy.spmath import (zcsr_transpose, zcsr_adjoint, zcsr_mult) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/operators.py b/qiskit/providers/aer/pulse/qutip_extra_lite/operators.py deleted file mode 100644 index 7793882422..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/operators.py +++ /dev/null @@ -1,457 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name - -""" -This module contains functions for generating Qobj representation of a variety -of commonly occuring quantum operators. -""" - -import numpy as np -from .fastsparse import fast_csr_matrix, fast_identity -from .qobj import Qobj - - -# Spin operators -def jmat(j, *args): - """Higher-order spin operators: - - Args: - j (float): Spin of operator - - args (str): Which operator to return 'x','y','z','+','-'. - If no args given, then output is ['x','y','z'] - - Returns: - Qobj: Requested spin operator(s). - - Raises: - TypeError: Invalid input. - """ - if (np.fix(2 * j) != 2 * j) or (j < 0): - raise TypeError('j must be a non-negative integer or half-integer') - - if not args: - return jmat(j, 'x'), jmat(j, 'y'), jmat(j, 'z') - - if args[0] == '+': - A = _jplus(j) - elif args[0] == '-': - A = _jplus(j).getH() - elif args[0] == 'x': - A = 0.5 * (_jplus(j) + _jplus(j).getH()) - elif args[0] == 'y': - A = -0.5 * 1j * (_jplus(j) - _jplus(j).getH()) - elif args[0] == 'z': - A = _jz(j) - else: - raise TypeError('Invalid type') - - return Qobj(A) - - -def _jplus(j): - """ - Internal functions for generating the data representing the J-plus - operator. - """ - m = np.arange(j, -j - 1, -1, dtype=complex) - data = (np.sqrt(j * (j + 1.0) - (m + 1.0) * m))[1:] - N = m.shape[0] - ind = np.arange(1, N, dtype=np.int32) - ptr = np.array(list(range(N - 1)) + [N - 1] * 2, dtype=np.int32) - ptr[-1] = N - 1 - return fast_csr_matrix((data, ind, ptr), shape=(N, N)) - - -def _jz(j): - """ - Internal functions for generating the data representing the J-z operator. - """ - N = int(2 * j + 1) - data = np.array([j - k for k in range(N) if (j - k) != 0], dtype=complex) - # Even shaped matrix - if N % 2 == 0: - ind = np.arange(N, dtype=np.int32) - ptr = np.arange(N + 1, dtype=np.int32) - ptr[-1] = N - # Odd shaped matrix - else: - j = int(j) - ind = np.array(list(range(j)) + list(range(j + 1, N)), dtype=np.int32) - ptr = np.array(list(range(j + 1)) + list(range(j, N)), dtype=np.int32) - ptr[-1] = N - 1 - return fast_csr_matrix((data, ind, ptr), shape=(N, N)) - - -# -# Spin j operators: -# -def spin_Jx(j): - """Spin-j x operator - - Parameters - ---------- - j : float - Spin of operator - - Returns - ------- - op : Qobj - ``qobj`` representation of the operator. - - """ - return jmat(j, 'x') - - -def spin_Jy(j): - """Spin-j y operator - - Args: - j (float): Spin of operator - - Returns: - Qobj: representation of the operator. - - """ - return jmat(j, 'y') - - -def spin_Jz(j): - """Spin-j z operator - - Args: - j (float): Spin of operator - - Returns: - Qobj: representation of the operator. - - """ - return jmat(j, 'z') - - -def spin_Jm(j): - """Spin-j annihilation operator - - Parameters: - j (float): Spin of operator - - Returns: - Qobj: representation of the operator. - - """ - return jmat(j, '-') - - -def spin_Jp(j): - """Spin-j creation operator - - Args: - j (float): Spin of operator - - Returns: - Qobj: representation of the operator. - - """ - return jmat(j, '+') - - -def spin_J_set(j): - """Set of spin-j operators (x, y, z) - - Args: - j (float): Spin of operators - - Returns: - list: list of ``qobj`` representating of the spin operator. - - """ - return jmat(j) - - -# -# Pauli spin 1/2 operators: -# -def sigmap(): - """Creation operator for Pauli spins. - - Examples - -------- - >>> sigmap() - Quantum object: dims = [[2], [2]], \ -shape = [2, 2], type = oper, isHerm = False - Qobj data = - [[ 0. 1.] - [ 0. 0.]] - - """ - return jmat(1 / 2., '+') - - -def sigmam(): - """Annihilation operator for Pauli spins. - - Examples - -------- - >>> sigmam() - Quantum object: dims = [[2], [2]], \ -shape = [2, 2], type = oper, isHerm = False - Qobj data = - [[ 0. 0.] - [ 1. 0.]] - - """ - return jmat(1 / 2., '-') - - -def sigmax(): - """Pauli spin 1/2 sigma-x operator - - Examples - -------- - >>> sigmax() - Quantum object: dims = [[2], [2]], \ -shape = [2, 2], type = oper, isHerm = False - Qobj data = - [[ 0. 1.] - [ 1. 0.]] - - """ - return 2.0 * jmat(1.0 / 2, 'x') - - -def sigmay(): - """Pauli spin 1/2 sigma-y operator. - - Examples - -------- - >>> sigmay() - Quantum object: dims = [[2], [2]], \ -shape = [2, 2], type = oper, isHerm = True - Qobj data = - [[ 0.+0.j 0.-1.j] - [ 0.+1.j 0.+0.j]] - - """ - return 2.0 * jmat(1.0 / 2, 'y') - - -def sigmaz(): - """Pauli spin 1/2 sigma-z operator. - - Examples - -------- - >>> sigmaz() - Quantum object: dims = [[2], [2]], \ -shape = [2, 2], type = oper, isHerm = True - Qobj data = - [[ 1. 0.] - [ 0. -1.]] - - """ - return 2.0 * jmat(1.0 / 2, 'z') - - -# -# DESTROY returns annihilation operator for N dimensional Hilbert space -# out = destroy(N), N is integer value & N>0 -# -def destroy(N, offset=0): - """Destruction (lowering) operator. - - Args: - N (int): Dimension of Hilbert space. - - offset (int): (default 0) The lowest number state that is included - in the finite number state representation of the operator. - - Returns: - Qobj: Qobj for lowering operator. - - Raises: - ValueError: Invalid input. - - """ - if not isinstance(N, (int, np.integer)): # raise error if N not integer - raise ValueError("Hilbert space dimension must be integer value") - data = np.sqrt(np.arange(offset + 1, N + offset, dtype=complex)) - ind = np.arange(1, N, dtype=np.int32) - ptr = np.arange(N + 1, dtype=np.int32) - ptr[-1] = N - 1 - return Qobj(fast_csr_matrix((data, ind, ptr), shape=(N, N)), isherm=False) - - -# -# create returns creation operator for N dimensional Hilbert space -# out = create(N), N is integer value & N>0 -# -def create(N, offset=0): - """Creation (raising) operator. - - Args: - N (int): Dimension of Hilbert space. - - offset (int): (default 0) The lowest number state that is included - in the finite number state representation of the operator. - - Returns: - Qobj: Qobj for raising operator. - - Raises: - ValueError: Invalid inputs. - - """ - if not isinstance(N, (int, np.integer)): # raise error if N not integer - raise ValueError("Hilbert space dimension must be integer value") - qo = destroy(N, offset=offset) # create operator using destroy function - return qo.dag() - - -# -# QEYE returns identity operator for an N dimensional space -# a = qeye(N), N is integer & N>0 -# -def qeye(N): - """ - Identity operator - - Args: - N (int): Dimension of Hilbert space. If provided as a list of ints, - then the dimension is the product over this list, but the - ``dims`` property of the new Qobj are set to this list. - - Returns: - Qobj: Identity operator Qobj. - - Raises: - ValueError: Invalid input. - """ - N = int(N) - if N < 0: - raise ValueError("N must be integer N>=0") - return Qobj(fast_identity(N), isherm=True, isunitary=True) - - -def identity(N): - """Identity operator. Alternative name to :func:`qeye`. - - Parameters - ---------- - N : int or list of ints - Dimension of Hilbert space. If provided as a list of ints, - then the dimension is the product over this list, but the - ``dims`` property of the new Qobj are set to this list. - - Returns - ------- - oper : qobj - Identity operator Qobj. - """ - return qeye(N) - - -def position(N, offset=0): - """ - Position operator x=1/sqrt(2)*(a+a.dag()) - - Args: - N (int): Number of Fock states in Hilbert space. - - offset (int): (default 0) The lowest number state that is included - in the finite number state representation of the operator. - Returns: - Qobj: Position operator as Qobj. - """ - a = destroy(N, offset=offset) - return 1.0 / np.sqrt(2.0) * (a + a.dag()) - - -def momentum(N, offset=0): - """ - Momentum operator p=-1j/sqrt(2)*(a-a.dag()) - - Args: - N (int): Number of Fock states in Hilbert space. - - offset (int): (default 0) The lowest number state that is - included in the finite number state - representation of the operator. - Returns: - Qobj: Momentum operator as Qobj. - """ - a = destroy(N, offset=offset) - return -1j / np.sqrt(2.0) * (a - a.dag()) - - -# number operator, important! -def num(N, offset=0): - """Quantum object for number operator. - - Args: - N (int): The dimension of the Hilbert space. - - offset(int): (default 0) The lowest number state that is included - in the finite number state representation of the operator. - - Returns: - Qobj: Qobj for number operator. - - """ - if offset == 0: - data = np.arange(1, N, dtype=complex) - ind = np.arange(1, N, dtype=np.int32) - ptr = np.array([0] + list(range(0, N)), dtype=np.int32) - ptr[-1] = N - 1 - else: - data = np.arange(offset, offset + N, dtype=complex) - ind = np.arange(N, dtype=np.int32) - ptr = np.arange(N + 1, dtype=np.int32) - ptr[-1] = N - - return Qobj(fast_csr_matrix((data, ind, ptr), - shape=(N, N)), isherm=True) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/qobj.py b/qiskit/providers/aer/pulse/qutip_extra_lite/qobj.py deleted file mode 100644 index b330852f0c..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/qobj.py +++ /dev/null @@ -1,768 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name, redefined-outer-name, no-name-in-module -# pylint: disable=import-error, unused-import - -"""The Quantum Object (Qobj) class, for representing quantum states and -operators, and related functions. -""" - -__all__ = ['Qobj'] - -import warnings -import builtins - -# import math functions from numpy.math: required for td string evaluation -import numpy as np -import scipy.sparse as sp -import scipy.linalg as la -from qiskit.providers.aer.version import __version__ - -from .dimensions import type_from_dims - -# used in existing functions that break if removed -from .cy.spmath import (zcsr_adjoint, zcsr_isherm) -from .fastsparse import fast_csr_matrix, fast_identity - -# general absolute tolerance -atol = 1e-12 - - -class Qobj(): - """A class for representing quantum objects, such as quantum operators - and states. - - The Qobj class is the QuTiP representation of quantum operators and state - vectors. This class also implements math operations +,-,* between Qobj - instances (and / by a C-number), as well as a collection of common - operator/state operations. The Qobj constructor optionally takes a - dimension ``list`` and/or shape ``list`` as arguments. - - Attributes - ---------- - data : array_like - Sparse matrix characterizing the quantum object. - dims : list - List of dimensions keeping track of the tensor structure. - shape : list - Shape of the underlying `data` array. - type : str - Type of quantum object: 'bra', 'ket', 'oper', 'operator-ket', - 'operator-bra', or 'super'. - superrep : str - Representation used if `type` is 'super'. One of 'super' - (Liouville form) or 'choi' (Choi matrix with tr = dimension). - isherm : bool - Indicates if quantum object represents Hermitian operator. - isunitary : bool - Indictaes if quantum object represents unitary operator. - iscp : bool - Indicates if the quantum object represents a map, and if that map is - completely positive (CP). - ishp : bool - Indicates if the quantum object represents a map, and if that map is - hermicity preserving (HP). - istp : bool - Indicates if the quantum object represents a map, and if that map is - trace preserving (TP). - iscptp : bool - Indicates if the quantum object represents a map that is completely - positive and trace preserving (CPTP). - isket : bool - Indicates if the quantum object represents a ket. - isbra : bool - Indicates if the quantum object represents a bra. - isoper : bool - Indicates if the quantum object represents an operator. - issuper : bool - Indicates if the quantum object represents a superoperator. - isoperket : bool - Indicates if the quantum object represents an operator in column vector - form. - isoperbra : bool - Indicates if the quantum object represents an operator in row vector - form. - - Methods - ------- - copy() - Create copy of Qobj - conj() - Conjugate of quantum object. - cosm() - Cosine of quantum object. - dag() - Adjoint (dagger) of quantum object. - dnorm() - Diamond norm of quantum operator. - dual_chan() - Dual channel of quantum object representing a CP map. - eigenenergies(sparse=False, sort='low', eigvals=0, tol=0, maxiter=100000) - Returns eigenenergies (eigenvalues) of a quantum object. - eigenstates(sparse=False, sort='low', eigvals=0, tol=0, maxiter=100000) - Returns eigenenergies and eigenstates of quantum object. - expm() - Matrix exponential of quantum object. - full(order='C') - Returns dense array of quantum object `data` attribute. - groundstate(sparse=False, tol=0, maxiter=100000) - Returns eigenvalue and eigenket for the groundstate of a quantum - object. - matrix_element(bra, ket) - Returns the matrix element of operator between `bra` and `ket` vectors. - norm(norm='tr', sparse=False, tol=0, maxiter=100000) - Returns norm of a ket or an operator. - proj() - Computes the projector for a ket or bra vector. - sinm() - Sine of quantum object. - sqrtm() - Matrix square root of quantum object. - tidyup(atol=1e-12) - Removes small elements from quantum object. - tr() - Trace of quantum object. - trans() - Transpose of quantum object. - transform(inpt, inverse=False) - Performs a basis transformation defined by `inpt` matrix. - trunc_neg(method='clip') - Removes negative eigenvalues and returns a new Qobj that is - a valid density operator. - unit(norm='tr', sparse=False, tol=0, maxiter=100000) - Returns normalized quantum object. - - """ - __array_priority__ = 100 # sets Qobj priority above numpy arrays - - # pylint: disable=dangerous-default-value, redefined-builtin - def __init__(self, inpt=None, dims=[[], []], shape=[], - type=None, isherm=None, copy=True, - fast=False, superrep=None, isunitary=None): - """ - Qobj constructor. - - Args: - inpt (ndarray): Input array or matrix data. - dims (list): List of Qobj dims. - shape (list): shape of underlying data. - type (str): Is object a ket, bra, oper, super. - isherm (bool): Is object Hermitian. - copy (bool): Copy input data. - fast (str or bool): Fast object instantiation. - superrep (str): Type of super representaiton. - isunitary (bool): Is object unitary. - - Raises: - Exception: Something bad happened. - """ - - self._isherm = isherm - self._type = type - self.superrep = superrep - self._isunitary = isunitary - - if fast == 'mc': - # fast Qobj construction for use in mcsolve with ket output - self._data = inpt - self.dims = dims - self._isherm = False - return - - if fast == 'mc-dm': - # fast Qobj construction for use in mcsolve with dm output - self._data = inpt - self.dims = dims - self._isherm = True - return - - if isinstance(inpt, Qobj): - # if input is already Qobj then return identical copy - - self._data = fast_csr_matrix((inpt.data.data, inpt.data.indices, - inpt.data.indptr), - shape=inpt.shape, copy=copy) - - if not np.any(dims): - # Dimensions of quantum object used for keeping track of tensor - # components - self.dims = inpt.dims - else: - self.dims = dims - - self.superrep = inpt.superrep - self._isunitary = inpt._isunitary - - elif inpt is None: - # initialize an empty Qobj with correct dimensions and shape - - if any(dims): - N, M = np.prod(dims[0]), np.prod(dims[1]) - self.dims = dims - - elif shape: - N, M = shape - self.dims = [[N], [M]] - - else: - N, M = 1, 1 - self.dims = [[N], [M]] - - self._data = fast_csr_matrix(shape=(N, M)) - - elif isinstance(inpt, (list, tuple)): - # case where input is a list - data = np.array(inpt) - if len(data.shape) == 1: - # if list has only one dimension (i.e [5,4]) - data = data.transpose() - - _tmp = sp.csr_matrix(data, dtype=complex) - self._data = fast_csr_matrix((_tmp.data, _tmp.indices, _tmp.indptr), - shape=_tmp.shape) - if not np.any(dims): - self.dims = [[int(data.shape[0])], [int(data.shape[1])]] - else: - self.dims = dims - - elif isinstance(inpt, np.ndarray) or sp.issparse(inpt): - # case where input is array or sparse - if inpt.ndim == 1: - inpt = inpt[:, np.newaxis] - - do_copy = copy - if not isinstance(inpt, fast_csr_matrix): - _tmp = sp.csr_matrix(inpt, dtype=complex, copy=do_copy) - _tmp.sort_indices() # Make sure indices are sorted. - do_copy = 0 - else: - _tmp = inpt - self._data = fast_csr_matrix((_tmp.data, _tmp.indices, _tmp.indptr), - shape=_tmp.shape, copy=do_copy) - - if not np.any(dims): - self.dims = [[int(inpt.shape[0])], [int(inpt.shape[1])]] - else: - self.dims = dims - - elif isinstance(inpt, (int, float, complex, - np.integer, np.floating, np.complexfloating)): - # if input is int, float, or complex then convert to array - _tmp = sp.csr_matrix([[inpt]], dtype=complex) - self._data = fast_csr_matrix((_tmp.data, _tmp.indices, _tmp.indptr), - shape=_tmp.shape) - if not np.any(dims): - self.dims = [[1], [1]] - else: - self.dims = dims - - else: - warnings.warn("Initializing Qobj from unsupported type: %s" % - builtins.type(inpt)) - inpt = np.array([[0]]) - _tmp = sp.csr_matrix(inpt, dtype=complex, copy=copy) - self._data = fast_csr_matrix((_tmp.data, _tmp.indices, _tmp.indptr), - shape=_tmp.shape) - self.dims = [[int(inpt.shape[0])], [int(inpt.shape[1])]] - - if type == 'super': - # Type is not super, i.e. dims not explicitly passed, but oper shape - if dims == [[], []] and self.shape[0] == self.shape[1]: - sub_shape = np.sqrt(self.shape[0]) - # check if root of shape is int - if (sub_shape % 1) != 0: - raise Exception('Invalid shape for a super operator.') - - sub_shape = int(sub_shape) - self.dims = [[[sub_shape], [sub_shape]]] * 2 - - if superrep: - self.superrep = superrep - else: - if self.type == 'super' and self.superrep is None: - self.superrep = 'super' - - # clear type cache - self._type = None - - def copy(self): - """Create identical copy""" - return Qobj(inpt=self) - - def get_data(self): - """Gets underlying data.""" - return self._data - - # Here we perfrom a check of the csr matrix type during setting of Q.data - def set_data(self, data): - """Data setter - """ - if not isinstance(data, fast_csr_matrix): - raise TypeError('Qobj data must be in fast_csr format.') - - self._data = data - data = property(get_data, set_data) - - def __add__(self, other): - """ - ADDITION with Qobj on LEFT [ ex. Qobj+4 ] - """ - self._isunitary = None - - if not isinstance(other, Qobj): - if isinstance(other, (int, float, complex, np.integer, - np.floating, np.complexfloating, np.ndarray, - list, tuple)) or sp.issparse(other): - other = Qobj(other) - else: - return NotImplemented - - if np.prod(other.shape) == 1 and np.prod(self.shape) != 1: - # case for scalar quantum object - dat = other.data[0, 0] - if dat == 0: - return self - - out = Qobj() - - if self.type in ['oper', 'super']: - out.data = self.data + dat * fast_identity( - self.shape[0]) - else: - out.data = self.data - out.data.data = out.data.data + dat - - out.dims = self.dims - - if isinstance(dat, (int, float)): - out._isherm = self._isherm - else: - # We use _isherm here to prevent recalculating on self and - # other, relying on that bool(None) == False. - out._isherm = (True if self._isherm and other._isherm - else out.isherm) - - out.superrep = self.superrep - - return out - - elif np.prod(self.shape) == 1 and np.prod(other.shape) != 1: - # case for scalar quantum object - dat = self.data[0, 0] - if dat == 0: - return other - - out = Qobj() - if other.type in ['oper', 'super']: - out.data = dat * fast_identity(other.shape[0]) + other.data - else: - out.data = other.data - out.data.data = out.data.data + dat - out.dims = other.dims - - if isinstance(dat, complex): - out._isherm = out.isherm - else: - out._isherm = self._isherm - - out.superrep = self.superrep - - return out - - elif self.dims != other.dims: - raise TypeError('Incompatible quantum object dimensions') - - elif self.shape != other.shape: - raise TypeError('Matrix shapes do not match') - - else: # case for matching quantum objects - out = Qobj() - out.data = self.data + other.data - out.dims = self.dims - - if self.type in ['ket', 'bra', 'operator-ket', 'operator-bra']: - out._isherm = False - elif self._isherm is None or other._isherm is None: - out._isherm = out.isherm - elif not self._isherm and not other._isherm: - out._isherm = out.isherm - else: - out._isherm = self._isherm and other._isherm - - if self.superrep and other.superrep: - if self.superrep != other.superrep: - msg = ("Adding superoperators with different " + - "representations") - warnings.warn(msg) - - out.superrep = self.superrep - - return out - - def __radd__(self, other): - """ - ADDITION with Qobj on RIGHT [ ex. 4+Qobj ] - """ - return self + other - - def __sub__(self, other): - """ - SUBTRACTION with Qobj on LEFT [ ex. Qobj-4 ] - """ - return self + (-other) - - def __rsub__(self, other): - """ - SUBTRACTION with Qobj on RIGHT [ ex. 4-Qobj ] - """ - return (-self) + other - - # used - # pylint: disable=too-many-return-statements - def __mul__(self, other): - """ - MULTIPLICATION with Qobj on LEFT [ ex. Qobj*4 ] - """ - self._isunitary = None - - if isinstance(other, Qobj): - if self.dims[1] == other.dims[0]: - out = Qobj() - out.data = self.data * other.data - dims = [self.dims[0], other.dims[1]] - out.dims = dims - out.dims = dims - - out._isherm = None - - if self.superrep and other.superrep: - if self.superrep != other.superrep: - msg = ("Multiplying superoperators with different " + - "representations") - warnings.warn(msg) - - out.superrep = self.superrep - - return out - - elif np.prod(self.shape) == 1: - out = Qobj(other) - out.data *= self.data[0, 0] - out.superrep = other.superrep - return out - - elif np.prod(other.shape) == 1: - out = Qobj(self) - out.data *= other.data[0, 0] - out.superrep = self.superrep - return out - - else: - raise TypeError("Incompatible Qobj shapes") - - elif isinstance(other, np.ndarray): - if other.dtype == 'object': - return np.array([self * item for item in other], - dtype=object) - else: - return self.data * other - - elif isinstance(other, list): - # if other is a list, do element-wise multiplication - return np.array([self * item for item in other], - dtype=object) - - elif isinstance(other, (int, float, complex, - np.integer, np.floating, np.complexfloating)): - out = Qobj() - out.data = self.data * other - out.dims = self.dims - out.superrep = self.superrep - if isinstance(other, complex): - out._isherm = out.isherm - else: - out._isherm = self._isherm - - return out - - else: - return NotImplemented - - # keep for now - def __rmul__(self, other): - """ - MULTIPLICATION with Qobj on RIGHT [ ex. 4*Qobj ] - """ - if isinstance(other, np.ndarray): - if other.dtype == 'object': - return np.array([item * self for item in other], - dtype=object) - else: - return other * self.data - - elif isinstance(other, list): - # if other is a list, do element-wise multiplication - return np.array([item * self for item in other], - dtype=object) - - elif isinstance(other, (int, float, complex, - np.integer, np.floating, - np.complexfloating)): - out = Qobj() - out.data = other * self.data - out.dims = self.dims - out.superrep = self.superrep - if isinstance(other, complex): - out._isherm = out.isherm - else: - out._isherm = self._isherm - - return out - - else: - raise TypeError("Incompatible object for multiplication") - - # keep for now - def __truediv__(self, other): - return self.__div__(other) - - # keep for now - def __div__(self, other): - """ - DIVISION (by numbers only) - """ - if isinstance(other, Qobj): # if both are quantum objects - raise TypeError("Incompatible Qobj shapes " + - "[division with Qobj not implemented]") - - if isinstance(other, (int, float, complex, - np.integer, np.floating, np.complexfloating)): - out = Qobj() - out.data = self.data / other - out.dims = self.dims - if isinstance(other, complex): - out._isherm = out.isherm - else: - out._isherm = self._isherm - - out.superrep = self.superrep - - return out - - else: - raise TypeError("Incompatible object for division") - - # keep for now - def __neg__(self): - """ - NEGATION operation. - """ - out = Qobj() - out.data = -self.data - out.dims = self.dims - out.superrep = self.superrep - out._isherm = self._isherm - out._isunitary = self._isunitary - return out - - # needed by qobj_generators - def __getitem__(self, ind): - """ - GET qobj elements. - """ - out = self.data[ind] - if sp.issparse(out): - return np.asarray(out.todense()) - else: - return out - - # keep for now - def __eq__(self, other): - """ - EQUALITY operator. - """ - return bool(isinstance(other, Qobj) and - self.dims == other.dims and - not np.any(np.abs((self.data - other.data).data) > atol)) - - # keep for now - def __ne__(self, other): - """ - INEQUALITY operator. - """ - return not self == other - - # not needed functionally but is useful to keep for now - def __str__(self): - s = "" - t = self.type - shape = self.shape - if self.type in ['oper', 'super']: - s += ("Quantum object: " + - "dims = " + str(self.dims) + - ", shape = " + str(shape) + - ", type = " + t + - ", isherm = " + str(self.isherm) + - ( - ", superrep = {0.superrep}".format(self) - if t == "super" and self.superrep != "super" - else "" - ) + "\n") - else: - s += ("Quantum object: " + - "dims = " + str(self.dims) + - ", shape = " + str(shape) + - ", type = " + t + "\n") - s += "Qobj data =\n" - - if shape[0] > 10000 or shape[1] > 10000: - # if the system is huge, don't attempt to convert to a - # dense matrix and then to string, because it is pointless - # and is likely going to produce memory errors. Instead print the - # sparse data string representation - s += str(self.data) - - elif all(np.imag(self.data.data) == 0): - s += str(np.real(self.full())) - - else: - s += str(self.full()) - - return s - - # used by states - def dag(self): - """Adjoint operator of quantum object. - """ - out = Qobj() - out.data = zcsr_adjoint(self.data) - out.dims = [self.dims[1], self.dims[0]] - out._isherm = self._isherm - out.superrep = self.superrep - return out - - # breaks if removed - used in hamiltonian_model - def full(self, order='C', squeeze=False): - """Dense array from quantum object. - - Parameters - ---------- - order : str {'C', 'F'} - Return array in C (default) or Fortran ordering. - squeeze : bool {False, True} - Squeeze output array. - - Returns - ------- - data : array - Array of complex data from quantum objects `data` attribute. - """ - if squeeze: - return self.data.toarray(order=order).squeeze() - else: - return self.data.toarray(order=order) - - # breaks if removed - only ever actually used in tests for duffing_model_generators - # pylint: disable=unused-argument - def __array__(self, *arg, **kwarg): - """Numpy array from Qobj - For compatibility with np.array - """ - return self.full() - - # breaks if removed due to tensor - @property - def isherm(self): - """Is operator Hermitian. - - Returns: - bool: Operator is Hermitian or not. - """ - - if self._isherm is not None: - # used previously computed value - return self._isherm - - self._isherm = bool(zcsr_isherm(self.data)) - - return self._isherm - - # breaks if removed due to tensor - @isherm.setter - def isherm(self, isherm): - self._isherm = isherm - - @property - def type(self): - """Type of Qobj - """ - if not self._type: - self._type = type_from_dims(self.dims) - - return self._type - - @property - def shape(self): - """Shape of Qobj - """ - if self.data.shape == (1, 1): - return tuple([np.prod(self.dims[0]), np.prod(self.dims[1])]) - else: - return tuple(self.data.shape) - - # breaks if removed - called in pulse_controller - # when initial state being set - @property - def isket(self): - """Is ket vector""" - return self.type == 'ket' - - # breaks if removed - called in tensor - @property - def issuper(self): - """Is super operator""" - return self.type == 'super' diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/states.py b/qiskit/providers/aer/pulse/qutip_extra_lite/states.py deleted file mode 100644 index 2caaf2f561..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/states.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -# pylint: disable=invalid-name - -"""States -""" - -import numpy as np -from .qobj import Qobj -from .fastsparse import fast_csr_matrix - - -# used by qobj_generators -def fock_dm(N, n=0, offset=0): - """Density matrix representation of a Fock state - - Constructed via outer product of :func:`qutip.states.fock`. - - Args: - N (int): Number of Fock states in Hilbert space. - - n (int): Desired number state, defaults to 0 if omitted. - - offset (int): Energy level offset. - - Returns: - Qobj: Density matrix representation of Fock state. - - """ - psi = basis(N, n, offset=offset) - - return psi * psi.dag() - - -def basis(N, n=0, offset=0): - """Generates the vector representation of a Fock state. - - Args: - N (int): Number of Fock states in Hilbert space. - - n (int): Integer corresponding to desired number - state, defaults to 0 if omitted. - - offset (int): The lowest number state that is included - in the finite number state representation - of the state. - - Returns: - Qobj: Qobj representing the requested number state ``|n>``. - - Raises: - ValueError: Invalid input value. - - """ - if (not isinstance(N, (int, np.integer))) or N < 0: - raise ValueError("N must be integer N >= 0") - - if (not isinstance(n, (int, np.integer))) or n < offset: - raise ValueError("n must be integer n >= 0") - - if n - offset > (N - 1): # check if n is within bounds - raise ValueError("basis vector index need to be in n <= N-1") - - data = np.array([1], dtype=complex) - ind = np.array([0], dtype=np.int32) - ptr = np.array([0] * ((n - offset) + 1) + [1] * (N - (n - offset)), - dtype=np.int32) - - return Qobj(fast_csr_matrix((data, ind, ptr), shape=(N, 1)), isherm=False) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/tensor.py b/qiskit/providers/aer/pulse/qutip_extra_lite/tensor.py deleted file mode 100644 index 11863c2e1e..0000000000 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/tensor.py +++ /dev/null @@ -1,111 +0,0 @@ -# -*- coding: utf-8 -*- - -# 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. - -# This file is part of QuTiP: Quantum Toolbox in Python. -# -# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names -# of its contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -############################################################################### -""" -Module for the creation of composite quantum objects via the tensor product. -""" - -import numpy as np -# pylint: disable=no-name-in-module, import-error -from .cy.spmath import zcsr_kron -from .qobj import Qobj - - -def tensor(*args): - """Calculates the tensor product of input operators. - - Args: - args (array_like): List or array of quantum objects for tensor product. - - Returns: - qobj.Qobj: A composite quantum object. - - Raises: - TypeError: Requires at least one input argument. - """ - if not args: - raise TypeError("Requires at least one input argument") - - if len(args) == 1 and isinstance(args[0], (list, np.ndarray)): - # this is the case when tensor is called on the form: - # tensor([q1, q2, q3, ...]) - qlist = args[0] - - elif len(args) == 1 and isinstance(args[0], Qobj): - # tensor is called with a single Qobj as an argument, do nothing - return args[0] - - else: - # this is the case when tensor is called on the form: - # tensor(q1, q2, q3, ...) - qlist = args - - if not all([isinstance(q, Qobj) for q in qlist]): - # raise error if one of the inputs is not a quantum object - raise TypeError("One of inputs is not a quantum object") - - out = Qobj() - if qlist[0].issuper: - out.superrep = qlist[0].superrep - if not all([q.superrep == out.superrep for q in qlist]): - raise TypeError("In tensor products of superroperators, all must" + - "have the same representation") - - out.isherm = True - for n, q in enumerate(qlist): - if n == 0: - out.data = q.data - out.dims = q.dims - else: - out.data = zcsr_kron(out.data, q.data) - out.dims = [out.dims[0] + q.dims[0], out.dims[1] + q.dims[1]] - - out.isherm = out.isherm and q.isherm - - if not out.isherm: - out._isherm = None - - return out diff --git a/qiskit/providers/aer/pulse/system_models/hamiltonian_model.py b/qiskit/providers/aer/pulse/system_models/hamiltonian_model.py index 0cf3348d59..a4b6c90f1d 100644 --- a/qiskit/providers/aer/pulse/system_models/hamiltonian_model.py +++ b/qiskit/providers/aer/pulse/system_models/hamiltonian_model.py @@ -18,7 +18,7 @@ from collections import OrderedDict import numpy as np import numpy.linalg as la -from qiskit.providers.aer.aererror import AerError +from ...aererror import AerError from .string_model_parser.string_model_parser import HamiltonianParser @@ -217,7 +217,7 @@ def _compute_drift_data(self): ham_full = np.zeros((full_dim, full_dim), dtype=complex) for ham_part in self._system: - ham_full += ham_part[0].full() * eval(ham_part[1]) + ham_full += ham_part[0].data * eval(ham_part[1]) # Remap eigenvalues and eigenstates evals, estates = la.eigh(ham_full) diff --git a/qiskit/providers/aer/pulse/system_models/pulse_system_model.py b/qiskit/providers/aer/pulse/system_models/pulse_system_model.py index db139926da..72efc860b0 100644 --- a/qiskit/providers/aer/pulse/system_models/pulse_system_model.py +++ b/qiskit/providers/aer/pulse/system_models/pulse_system_model.py @@ -17,8 +17,8 @@ from warnings import warn from collections import OrderedDict -from qiskit.providers import BaseBackend -from qiskit.providers.aer.aererror import AerError +from qiskit.providers import BaseBackend, Backend +from ...aererror import AerError from .hamiltonian_model import HamiltonianModel @@ -30,8 +30,6 @@ class PulseSystemModel(): * ``"hamiltonian"``: a :class:`HamiltonianModel` object representing the Hamiltonian of the system. - * ``"qubit_freq_est"`` and ``"meas_freq_est"``: optional default values for - qubit and measurement frequencies. * ``"u_channel_lo"``: A description of :class:`ControlChannel` local oscillator frequencies in terms of qubit local oscillator frequencies. * ``"control_channel_labels"``: Optional list of identifying information for @@ -55,8 +53,6 @@ class PulseSystemModel(): """ def __init__(self, hamiltonian=None, - qubit_freq_est=None, - meas_freq_est=None, u_channel_lo=None, control_channel_labels=None, subsystem_list=None, @@ -65,10 +61,6 @@ def __init__(self, Args: hamiltonian (HamiltonianModel): The Hamiltonian of the system. - qubit_freq_est (list): list of qubit lo frequencies defaults to be used in simulation - if none are specified in the PulseQobj. - meas_freq_est (list): list of qubit meas frequencies defaults to be used in simulation - if none are specified in the PulseQobj. u_channel_lo (list): list of ControlChannel frequency specifications. control_channel_labels (list): list of labels for control channels, which can be of any type. @@ -78,10 +70,6 @@ def __init__(self, AerError: if hamiltonian is not None or a HamiltonianModel """ - # default type values - self._qubit_freq_est = qubit_freq_est - self._meas_freq_est = meas_freq_est - # necessary values if hamiltonian is not None and not isinstance(hamiltonian, HamiltonianModel): raise AerError("hamiltonian must be a HamiltonianModel object") @@ -106,27 +94,28 @@ def from_backend(cls, backend, subsystem_list=None): AerError: If channel or u_channel_lo are invalid. """ - if not isinstance(backend, BaseBackend): + if not isinstance(backend, (BaseBackend, Backend)): raise AerError("{} is not a Qiskit backend".format(backend)) # get relevant information from backend - defaults = backend.defaults() config = backend.configuration() if not config.open_pulse: raise AerError('{} is not an open pulse backend'.format(backend)) - # draw defaults - qubit_freq_est = getattr(defaults, 'qubit_freq_est', None) - meas_freq_est = getattr(defaults, 'meas_freq_est', None) + return cls.from_config(config, subsystem_list) + + @classmethod + def from_config(cls, configuration, subsystem_list=None): + """Construct a model from configuration and defaults.""" # draw from configuration # if no subsystem_list, use all for device - subsystem_list = subsystem_list or list(range(config.n_qubits)) - ham_string = config.hamiltonian + subsystem_list = subsystem_list or list(range(configuration.n_qubits)) + ham_string = configuration.hamiltonian hamiltonian = HamiltonianModel.from_dict(ham_string, subsystem_list) - u_channel_lo = getattr(config, 'u_channel_lo', None) - dt = getattr(config, 'dt', None) + u_channel_lo = getattr(configuration, 'u_channel_lo', None) + dt = getattr(configuration, 'dt', None) control_channel_labels = [None] * len(u_channel_lo) # populate control_channel_dict @@ -163,8 +152,6 @@ def from_backend(cls, backend, subsystem_list=None): control_channel_labels[u_idx] = {'driven_q': drive_idx, 'freq': u_string} return cls(hamiltonian=hamiltonian, - qubit_freq_est=qubit_freq_est, - meas_freq_est=meas_freq_est, u_channel_lo=u_channel_lo, control_channel_labels=control_channel_labels, subsystem_list=subsystem_list, @@ -190,8 +177,7 @@ def calculate_channel_frequencies(self, qubit_lo_freq=None): Args: qubit_lo_freq (list or None): list of qubit linear - oscillator drive frequencies. If None these will be calculated - using self._qubit_freq_est. + oscillator drive frequencies. Returns: OrderedDict: a dictionary of channel frequencies. @@ -200,10 +186,7 @@ def calculate_channel_frequencies(self, qubit_lo_freq=None): ValueError: If channel or u_channel_lo are invalid. """ if not qubit_lo_freq: - if not self._qubit_freq_est: - raise ValueError("No qubit_lo_freq to use.") - - qubit_lo_freq = self._qubit_freq_est + raise ValueError("qubit_lo_freq is a required function parameter.") if self.u_channel_lo is None: raise ValueError("{} has no u_channel_lo.".format(self.__class__.__name__)) diff --git a/qiskit/providers/aer/pulse/system_models/string_model_parser/gen_operator.py b/qiskit/providers/aer/pulse/system_models/string_model_parser/gen_operator.py new file mode 100644 index 0000000000..b18fad0b7c --- /dev/null +++ b/qiskit/providers/aer/pulse/system_models/string_model_parser/gen_operator.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +# 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. + +""" +Functions to construct terra operators +""" + +import numpy as np + +from qiskit.quantum_info.operators.operator import Operator + + +def _create_destroy(dim, creation=True): + a_x, b_x = (1, 0) if creation else (0, 1) + data_op = np.zeros((dim, dim)) + for i in range(dim - 1): + data_op[i + a_x, i + b_x] = np.sqrt(i + 1) + return Operator(data_op) + + +def create(dim): + """Creation operator. + Args: + dim (int): Dimension of Hilbert space. + Returns: + Operator: Operator representation of creation operator. + """ + return _create_destroy(dim, creation=True) + + +def destroy(dim): + """Destruction operator. + Args: + dim (int): Dimension of Hilbert space. + Returns: + Operator: Operator representation of destruction operator. + """ + return _create_destroy(dim, creation=False) + + +def num(dim): + """Operator representation for number operator. + Args: + dim (int): The dimension of the Hilbert space. + Returns: + Operator: Operator representation for number operator. + """ + return Operator(np.diag(np.arange(dim))) + + +def sigmax(): + """Operator representation for Pauli spin 1/2 sigma-x operator. + Returns: + Operator: Operator representation for sigma x. + """ + return Operator.from_label('X') + + +def sigmay(): + """Operator representation for Pauli spin 1/2 sigma-y operator. + Returns: + Operator: Operator representation for sigma y. + """ + return Operator.from_label('Y') + + +def sigmaz(): + """Operator representation for Pauli spin 1/2 sigma-z operator. + Returns: + Operator: Operator representation for sigma z. + """ + return Operator.from_label('Z') + + +def identity(dim): + """Identity operator. + Args: + dim (int): Dimension of Hilbert space. + Returns: + Operator: Operator representation of identity operator. + """ + return Operator(np.identity(dim, dtype=np.complex128)) + + +def basis(dim, n=0): + """Vector representation of a Fock state. + Args: + dim (int): Number of Fock states in Hilbert space. + n (int): Integer corresponding to desired number state, default value is 0. + Returns: + Operator: Opertor representing the requested number state ``|n>``. + """ + data_op = np.zeros((dim, 1)) + data_op[n] = 1 + return Operator(data_op) + + +def tensor(op_list): + """Tensor product of input operators. + Args: + op_list (array_like): List or array of Operators objects for tensor product. + Returns: + Operator: Operator representation of tensor product. + """ + ret = op_list[0] + for op in op_list[1:]: + ret = ret.tensor(op) + return ret + + +def state(state_vector): + """Operator representation of state vector. + Args: + state_vector (array_like): array representing the state-vector. + Returns: + Operator: State vector operator representation. + """ + return Operator(state_vector.reshape(1, state_vector.shape[0])) + + +def fock_dm(dim, n=0): + """Density matrix representation of a Fock state + Args: + dim (int): Number of Fock states in Hilbert space. + n (int): Desired number state, defaults to 0 if omitted. + Returns: + Operator: Density matrix Operator representation of Fock state. + """ + psi = basis(dim, n) + return psi * psi.adjoint() diff --git a/qiskit/providers/aer/pulse/system_models/string_model_parser/qobj_from_string.py b/qiskit/providers/aer/pulse/system_models/string_model_parser/operator_from_string.py similarity index 73% rename from qiskit/providers/aer/pulse/system_models/string_model_parser/qobj_from_string.py rename to qiskit/providers/aer/pulse/system_models/string_model_parser/operator_from_string.py index 96ade9ebf7..dc485f9d22 100644 --- a/qiskit/providers/aer/pulse/system_models/string_model_parser/qobj_from_string.py +++ b/qiskit/providers/aer/pulse/system_models/string_model_parser/operator_from_string.py @@ -15,7 +15,7 @@ """Module for creating quantum operators.""" -from ...qutip_extra_lite import qobj_generators +from . import operator_generators as op_gen def gen_oper(opname, index, h_osc, h_qub, states=None): @@ -41,23 +41,24 @@ def gen_oper(opname, index, h_osc, h_qub, states=None): if opname in ['X', 'Y', 'Z'] and dim > 2: if opname == 'X': - opr_tmp = qobj_generators.get_oper('A', dim) + qobj_generators.get_oper('C', dim) + opr_tmp = (op_gen.get_oper('A', dim) + + op_gen.get_oper('C', dim)) elif opname == 'Y': - opr_tmp = (-1j * qobj_generators.get_oper('A', dim) + - 1j * qobj_generators.get_oper('C', dim)) + opr_tmp = (-1j * op_gen.get_oper('A', dim) + + 1j * op_gen.get_oper('C', dim)) else: - opr_tmp = (qobj_generators.get_oper('I', dim) - - 2 * qobj_generators.get_oper('N', dim)) + opr_tmp = (op_gen.get_oper('I', dim) - + 2 * op_gen.get_oper('N', dim)) else: is_qubit = False dim = h_osc.get(index, 5) if opname == 'P': - opr_tmp = qobj_generators.get_oper(opname, dim, states) + opr_tmp = op_gen.get_oper(opname, dim, states) else: if opr_tmp is None: - opr_tmp = qobj_generators.get_oper(opname, dim) + opr_tmp = op_gen.get_oper(opname, dim) # reverse sort by index rev_h_osc = sorted(h_osc.items(), key=lambda x: x[0])[::-1] @@ -69,11 +70,11 @@ def gen_oper(opname, index, h_osc, h_qub, states=None): if ii == index and not is_qubit: opers.append(opr_tmp) else: - opers.append(qobj_generators.qeye(dd)) + opers.append(op_gen.qeye(dd)) for ii, dd in rev_h_qub: if ii == index and is_qubit: opers.append(opr_tmp) else: - opers.append(qobj_generators.qeye(dd)) + opers.append(op_gen.qeye(dd)) - return qobj_generators.tensor(opers) + return op_gen.tensor(opers) diff --git a/qiskit/providers/aer/pulse/qutip_extra_lite/qobj_generators.py b/qiskit/providers/aer/pulse/system_models/string_model_parser/operator_generators.py similarity index 84% rename from qiskit/providers/aer/pulse/qutip_extra_lite/qobj_generators.py rename to qiskit/providers/aer/pulse/system_models/string_model_parser/operator_generators.py index 8f8aeaa95e..dfb8d4b9a7 100644 --- a/qiskit/providers/aer/pulse/qutip_extra_lite/qobj_generators.py +++ b/qiskit/providers/aer/pulse/system_models/string_model_parser/operator_generators.py @@ -16,17 +16,14 @@ """Operators to use in simulator""" import numpy as np -from . import operators as ops -from . import states as st -from . import tensor as ten -from .qobj import Qobj +from . import gen_operator def sigmax(dim=2): """Qiskit wrapper of sigma-X operator. """ if dim == 2: - return ops.sigmax() + return gen_operator.sigmax() else: raise Exception('Invalid level specification of the qubit subspace') @@ -35,7 +32,7 @@ def sigmay(dim=2): """Qiskit wrapper of sigma-Y operator. """ if dim == 2: - return ops.sigmay() + return gen_operator.sigmay() else: raise Exception('Invalid level specification of the qubit subspace') @@ -44,7 +41,7 @@ def sigmaz(dim=2): """Qiskit wrapper of sigma-Z operator. """ if dim == 2: - return ops.sigmaz() + return gen_operator.sigmaz() else: raise Exception('Invalid level specification of the qubit subspace') @@ -52,37 +49,37 @@ def sigmaz(dim=2): def sigmap(dim=2): """Qiskit wrapper of sigma-plus operator. """ - return ops.create(dim) + return gen_operator.create(dim) def sigmam(dim=2): """Qiskit wrapper of sigma-minus operator. """ - return ops.destroy(dim) + return gen_operator.destroy(dim) def create(dim): """Qiskit wrapper of creation operator. """ - return ops.create(dim) + return gen_operator.create(dim) def destroy(dim): """Qiskit wrapper of annihilation operator. """ - return ops.destroy(dim) + return gen_operator.destroy(dim) def num(dim): """Qiskit wrapper of number operator. """ - return ops.num(dim) + return gen_operator.num(dim) def qeye(dim): """Qiskit wrapper of identity operator. """ - return ops.qeye(dim) + return gen_operator.identity(dim) def project(dim, states): @@ -90,7 +87,7 @@ def project(dim, states): """ ket, bra = states if ket in range(dim) and bra in range(dim): - return st.basis(dim, ket) * st.basis(dim, bra).dag() + return gen_operator.basis(dim, ket) * gen_operator.basis(dim, bra).adjoint() else: raise Exception('States are specified on the outside of Hilbert space %s' % states) @@ -98,25 +95,25 @@ def project(dim, states): def tensor(list_qobj): """ Qiskit wrapper of tensor product """ - return ten.tensor(list_qobj) + return gen_operator.tensor(list_qobj) def basis(level, pos): """ Qiskit wrapper of basis """ - return st.basis(level, pos) + return gen_operator.basis(level, pos) def state(state_vec): """ Qiskit wrapper of qobj """ - return Qobj(state_vec) + return gen_operator.state(state_vec) def fock_dm(level, eigv): """ Qiskit wrapper of fock_dm """ - return st.fock_dm(level, eigv) + return gen_operator.fock_dm(level, eigv) def qubit_occ_oper_dressed(target_qubit, estates, h_osc, h_qub, level=0): @@ -153,8 +150,8 @@ def qubit_occ_oper_dressed(target_qubit, estates, h_osc, h_qub, level=0): out_state = tensor(states) for ii, estate in enumerate(estates): - if out_state[ii] == 1: - proj_op += estate * estate.dag() + if out_state.data.flatten()[ii] == 1: + proj_op += estate @ estate.adjoint() return proj_op diff --git a/qiskit/providers/aer/pulse/system_models/string_model_parser/string_model_parser.py b/qiskit/providers/aer/pulse/system_models/string_model_parser/string_model_parser.py index e6fc47ddf6..698792f8e4 100644 --- a/qiskit/providers/aer/pulse/system_models/string_model_parser/string_model_parser.py +++ b/qiskit/providers/aer/pulse/system_models/string_model_parser/string_model_parser.py @@ -20,7 +20,7 @@ from collections import namedtuple, OrderedDict import numpy as np from .apply_str_func_to_qobj import apply_func -from .qobj_from_string import gen_oper +from .operator_from_string import gen_oper Token = namedtuple('Token', ('type', 'name')) diff --git a/qiskit/providers/aer/utils/__init__.py b/qiskit/providers/aer/utils/__init__.py index b64ad32a39..98bbfaa946 100644 --- a/qiskit/providers/aer/utils/__init__.py +++ b/qiskit/providers/aer/utils/__init__.py @@ -48,5 +48,3 @@ from .noise_transformation import approximate_quantum_error from .noise_transformation import approximate_noise_model from .noise_model_inserter import insert_noise - -from . import qobj_utils diff --git a/qiskit/providers/aer/utils/noise_transformation.py b/qiskit/providers/aer/utils/noise_transformation.py index 95e20bc2f7..80e687b395 100644 --- a/qiskit/providers/aer/utils/noise_transformation.py +++ b/qiskit/providers/aer/utils/noise_transformation.py @@ -214,10 +214,7 @@ def approximate_noise_model(model, *, error_dict["gate_qubits"] = [model._str2qubits(qubits_str)] error_list.append(error_dict) - approx_noise_model = NoiseModel.from_dict({ - "errors": error_list, - "x90_gates": model._x90_gates - }) + approx_noise_model = NoiseModel.from_dict({"errors": error_list}) # Update basis gates approx_noise_model._basis_gates = model._basis_gates return approx_noise_model @@ -607,7 +604,7 @@ def get_matrix_from_channel(channel, symbol): n = channel.rows M = numpy.zeros((n, n), dtype=numpy.complex_) for (i, j) in itertools.product(range(n), range(n)): - M[i, j] = numpy.complex( + M[i, j] = complex( Poly(channel[i, j], symbol).coeff_monomial(symbol)) return M @@ -633,7 +630,7 @@ def get_const_matrix_from_channel(channel, symbols): n = channel.rows M = numpy.zeros((n, n), dtype=numpy.complex_) for (i, j) in itertools.product(range(n), range(n)): - M[i, j] = numpy.complex( + M[i, j] = complex( Poly(channel[i, j], symbols).coeff_monomial(1)) return M diff --git a/qiskit/providers/aer/utils/qobj_utils.py b/qiskit/providers/aer/utils/qobj_utils.py deleted file mode 100644 index 3e660be819..0000000000 --- a/qiskit/providers/aer/utils/qobj_utils.py +++ /dev/null @@ -1,266 +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. -""" -Temporary hacks for qobj until Terra supports Aer instructions (likely 0.8) - -THESE SHOULD ONLY BE USED UNTIL A PROPER QUANTUM CIRCUIT INTERFACE -IS ADDED TO QISKIT TERRA. THEY WILL NOT BE SUPPORTED AFTER THAT. -""" - -import copy -import warnings -import numpy as np -from qiskit.qobj import QasmQobjInstruction - - -def append_instr(qobj, exp_index, instruction): - """Append a QasmQobjInstruction to a QobjExperiment. - - Args: - qobj (Qobj): a Qobj object. - exp_index (int): The index of the experiment in the qobj. - instruction (QasmQobjInstruction): instruction to insert. - - Returns: - qobj(Qobj): The Qobj object - """ - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - qobj.experiments[exp_index].instructions.append(instruction) - return qobj - - -def insert_instr(qobj, exp_index, item, pos): - """Insert a QasmQobjInstruction into a QobjExperiment. - - Args: - qobj (Qobj): a Qobj object - exp_index (int): The index of the experiment in the qobj. - item (QasmQobjInstruction): instruction to insert. - pos (int): the position to insert the item. - - Returns: - qobj(Qobj): The Qobj object - """ - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - qobj.experiments[exp_index].instructions.insert(pos, item) - return qobj - - -def get_instr_pos(qobj, exp_index, name): - """Return all locations of QasmQobjInstruction in a Qobj experiment. - - The return list is sorted in reverse order so iterating over it - to insert new items will work as expected. - - Args: - qobj (Qobj): a Qobj object - exp_index (int): The index of the experiment in the qobj - name (str): QasmQobjInstruction name to find - - Returns: - list[int]: A list of positions where the QasmQobjInstruction is located. - """ - warnings.warn( - 'This funnction is deprecated and will be removed in a future release.', - DeprecationWarning) - # Check only the name string of the item - positions = [ - i for i, val in enumerate(qobj.experiments[exp_index].instructions) - if val.name == name - ] - return positions - - -def unitary_instr(mat, qubits, label=None): - """Create a unitary gate QasmQobjInstruction. - - Args: - mat (matrix_like): an n-qubit unitary matrix - qubits (list[int]): qubits to apply the matrix to. - label (str): optional string label for the unitary matrix - - Returns: - QasmQobjInstruction: The qobj item for the unitary instruction. - - Raises: - ValueError: if the input matrix is not unitary - - Additional Information: - - Qubit Ordering: - The n-qubit matrix is ordered in little-endian with respect to - the qubits in the label string. For example. If M is a tensor - product of single qubit matrices `M = kron(M_(n-1), ..., M_1, M_0)` - then `M_0` is applied to `qubits[0]`, `M_1` to `qubits[1]` etc. - - Label string: - The string label is used for identifying the matrix in a noise - model so that noise may be applied to the implementation of - this matrix. - """ - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - array = np.array(mat, dtype=complex) - dim = 2**len(qubits) - if array.shape not in [(dim, dim), (1, dim)]: - raise ValueError("Invalid") - instruction = { - "name": "unitary", - "qubits": list(qubits), - "params": [np.array(mat, dtype=complex)] - } - if label is not None: - instruction["label"] = str(label) - return QasmQobjInstruction(**instruction) - - -def measure_instr(qubits, memory, registers=None): - """Create a multi-qubit measure instruction""" - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - if len(qubits) != len(memory): - raise ValueError("Number of qubits does not match number of memory") - if registers is None: - return QasmQobjInstruction(name='measure', - qubits=qubits, - memory=memory) - # Case where we also measure to registers - if len(qubits) != len(registers): - raise ValueError("Number of qubits does not match number of registers") - return QasmQobjInstruction(name='measure', - qubits=qubits, - memory=memory, - register=registers) - - -def reset_instr(qubits): - """Create a multi-qubit reset instruction""" - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - return QasmQobjInstruction(name='reset', qubits=qubits) - - -def barrier_instr(num_qubits): - """Create a barrier QasmQobjInstruction.""" - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - return QasmQobjInstruction(name='barrier', qubits=list(range(num_qubits))) - - -def iden_instr(qubit): - """Create a barrier QasmQobjInstruction.""" - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - return QasmQobjInstruction(name='id', qubits=[qubit]) - - -def snapshot_instr(snapshot_type, label, qubits=None, params=None): - """Create a snapshot qobj item. - - Args: - snapshot_type (str): the snapshot type identifier - label (str): the snapshot label string - qubits (list[int]): qubits snapshot applies to (optional) - params (custom): optional parameters for special snapshot types. - See additional information. - - Returns: - QasmQobjInstruction: The qobj item for the snapshot instruction. - - - Additional Information: - Snapshot types: - "statevector" -- returns the current statevector for each shot - "memory" -- returns the current memory hex-string for each shot - "register" -- returns the current register hex-string for each shot - "probabilities" -- returns the measurement outcome probabilities - averaged over all shots, but conditioned on the - current memory value. - This requires the qubits field to be set. - "expval_pauli" -- returns the expectation value of an operator - averaged over all shots, but conditioned on the - current memory value. - This requires the qubits field to be set and - the params field to be set. - "expval_matrix" -- same as expval_pauli but with different params - - Pauli expectation value params: - These are a list of terms [complex_coeff, pauli_str] - where string is in little endian: pauli_str CBA applies Pauli - A to qubits[0], B to qubits[1] and C to qubits[2]. - Example for op 0.5 XX + 0.7 IZ we have [[0.5, 'XX'], [0.7, 'IZ']] - - Matrix expectation value params: - TODO - """ - warnings.warn( - 'This function is deprecated and will be removed in a future release.' - ' Use the snapshot circuit instructions in' - ' `qiskit.provider.aer.extensions` instead.', DeprecationWarning) - snap = { - "name": "snapshot", - "snapshot_type": snapshot_type, - "label": str(label) - } - if qubits is not None: - snap["qubits"] = list(qubits) - if params is not None: - snap["params"] = params - # Check if single-matrix expectation value - if snapshot_type in ["expval", "expval_matrix"] and \ - isinstance(params, np.ndarray): - snap["name"] = "expval_matrix" - snap["params"] = [[1.0, qubits, params]] - # TODO: implicit conversion for Pauli expval params - return QasmQobjInstruction(**snap) - - -def insert_snapshots_after_barriers(qobj, snapshot): - """Insert a snapshot instruction after each barrier in qobj. - - The label of the input snapshot will be appended with "i" where - "i" ranges from 0 to the 1 - number of barriers. - - Args: - qobj (Qobj): a qobj to insert snapshots into - snapshot (QasmQobjInstruction): a snapshot instruction. - - Returns: - qobj(Qobj): The Qobj object - - Raises: - ValueError: if the name of the instruction is not an snapshot - - Additional Information: - """ - warnings.warn( - 'This function is deprecated and will be removed in a future release.', - DeprecationWarning) - if snapshot.name != "snapshot": - raise ValueError("Invalid snapshot instruction") - label = snapshot.label - for exp_index in range(len(qobj.experiments)): - positions = get_instr_pos(qobj, exp_index, "barrier") - for i, pos in reversed(list(enumerate(positions))): - item = copy.copy(snapshot) - item.label = label + "{}".format(i) - insert_instr(qobj, exp_index, item, pos) - return qobj diff --git a/releasenotes/notes/fix-statevector-precision-24350e9fd7368c24.yaml b/releasenotes/notes/0.6/fix-statevector-precision-24350e9fd7368c24.yaml similarity index 73% rename from releasenotes/notes/fix-statevector-precision-24350e9fd7368c24.yaml rename to releasenotes/notes/0.6/fix-statevector-precision-24350e9fd7368c24.yaml index a0fedb645a..6ce4056540 100644 --- a/releasenotes/notes/fix-statevector-precision-24350e9fd7368c24.yaml +++ b/releasenotes/notes/0.6/fix-statevector-precision-24350e9fd7368c24.yaml @@ -1,7 +1,7 @@ --- fixes: - | - Fixes bug in the :class:`qiskit.providers.aer.StatevectorSimulator` that + Fixes bug in the :class:`~qiskit.providers.aer.StatevectorSimulator` that caused it to always run as CPU with double-precision without SIMD/AVX2 support even on systems with AVX2, or when single-precision or the GPU method was specified in the backend options. diff --git a/releasenotes/notes/0.7/0.7.0-release-a6655712e304234b.yaml b/releasenotes/notes/0.7/0.7.0-release-a6655712e304234b.yaml new file mode 100644 index 0000000000..d6a3cf9be2 --- /dev/null +++ b/releasenotes/notes/0.7/0.7.0-release-a6655712e304234b.yaml @@ -0,0 +1,15 @@ +--- +prelude: > + This 0.7.0 release includes numerous performance improvements and + significant enhancements to the simulator interface, and drops support + for Python 3.5. + The main interface changes are configurable simulator backends, + and constructing preconfigured simulators from IBMQ backends. + Noise model an basis gate support has also been extended for most of the + Qiskit circuit library standard gates, including new support for 1 and 2-qubit + rotation gates. + Performance improvements include adding SIMD support to the density matrix + and unitary simulation methods, reducing the used memory and improving the + performance of circuits using statevector and density matrix snapshots, and + adding support for Kraus instructions to the gate fusion circuit optimization + for greatly improving the performance of noisy statevector simulations. diff --git a/releasenotes/notes/add-openblas-lapack-67ad654d1148a309.yaml b/releasenotes/notes/0.7/add-openblas-lapack-67ad654d1148a309.yaml similarity index 100% rename from releasenotes/notes/add-openblas-lapack-67ad654d1148a309.yaml rename to releasenotes/notes/0.7/add-openblas-lapack-67ad654d1148a309.yaml diff --git a/releasenotes/notes/0.7/basis-gates-acbc8a1a52a49413.yaml b/releasenotes/notes/0.7/basis-gates-acbc8a1a52a49413.yaml new file mode 100644 index 0000000000..fc69d63e90 --- /dev/null +++ b/releasenotes/notes/0.7/basis-gates-acbc8a1a52a49413.yaml @@ -0,0 +1,72 @@ +--- +features: + - | + Adds basis gate support for the :class:`qiskit.circuit.Delay` + instruction to the :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and + :class:`~qiskit.providers.aer.QasmSimulator`. + Note that this gate is treated as an identity gate during simulation + and the delay length parameter is ignored. + - | + Adds basis gate support for the single-qubit gate + :class:`qiskit.circuit.library.UGate` to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"``, ``"density_matrix"``, ``"matrix_product_state"``, + and ``"extended_stabilizer"`` methods of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds basis gate support for the phase gate + :class:`qiskit.circuit.library.PhaseGate` to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"``, ``"density_matrix"``, ``"matrix_product_state"``, + and ``"extended_stabilizer"`` methods of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds basis gate support for the controlled-phase gate + :class:`qiskit.circuit.library.CPhaseGate` to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"``, ``"density_matrix"``, and + ``"matrix_product_state"`` methods of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds support for the multi-controlled phase gate + :class:`qiskit.circuit.library.MCPhaseGate` to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"`` method of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds support for the :math:`\sqrt(X)` gate + :class:`qiskit.circuit.library.SXGate` to the + class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds support for 1 and 2-qubit Qiskit circuit library rotation gates + :class:`~qiskit.circuit.library.RXGate`, :class:`~qiskit.circuit.library.RYGate`, + :class:`~qiskit.circuit.library.RZGate`, :class:`~qiskit.circuit.library.RGate`, + :class:`~qiskit.circuit.library.RXXGate`, :class:`~qiskit.circuit.library.RYYGate`, + :class:`~qiskit.circuit.library.RZZGate`, :class:`~qiskit.circuit.library.RZXGate` + to the :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"`` and ``"density_matrix"`` methods of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Adds support for multi-controlled rotation gates ``"mcr"``, ``"mcrx"``, + ``"mcry"``, ``"mcrz"`` + to the :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + ``"statevector"`` method of the + :class:`~qiskit.providers.aer.QasmSimulator`. +deprecations: + - | + :meth:`qiskit.providers.aer.noise.NoiseModel.set_x90_single_qubit_gates` has + been deprecated as unrolling to custom basis gates has been added to the + qiskit transpiler. The correct way to use an X90 based noise model is to + define noise on the Sqrt(X) ``"sx"`` or ``"rx"`` gate and one of the single-qubit + phase gates ``"u1"``, ``"rx"``, or ``"p"`` in the noise model. diff --git a/releasenotes/notes/0.7/configurable-backends-a55cd6a39a0a1561.yaml b/releasenotes/notes/0.7/configurable-backends-a55cd6a39a0a1561.yaml new file mode 100644 index 0000000000..da08ec3068 --- /dev/null +++ b/releasenotes/notes/0.7/configurable-backends-a55cd6a39a0a1561.yaml @@ -0,0 +1,39 @@ +--- +features: + - | + Make simulator backends configurable. This allows setting persistant options + such as simulation method and noise model for each simulator backend object. + + The :class:`~qiskit.providers.aer.QasmSimulator` and + :class:`~qiskit.providers.aer.PulseSimulator` can also be configured from + an :class:`~qiskit.providers.ibmq.IBMQBackend` backend object using the + `:meth:`~qiskit.providers.aer.QasmSimulator.from_backend` method. + For the :class:`~qiskit.providers.aer.QasmSimulator` this will configure the coupling map, + basis gates, and basic device noise model based on the backend configuration and + properties. For the :class:`~qiskit.providers.aer.PulseSimulator` the system model + and defaults will be configured automatically from the backend configuration, properties and + defaults. + + For example a noisy density matrix simulator backend can be constructed as + ``QasmSimulator(method='density_matrix', noise_model=noise_model)``, or an ideal + matrix product state simulator as ``QasmSimulator(method='matrix_product_state')``. + + A benefit is that a :class:`~qiskit.providers.aer.PulseSimulator` instance configured from + a backend better serves as a drop-in replacement to the original backend, making it easier to + swap in and out a simulator and real backend, e.g. when testing code on a simulator before + using a real backend. + For example, in the following code-block, the :class:`~qiskit.providers.aer.PulseSimulator` is + instantiated from the ``FakeArmonk()`` backend. All configuration and default data is copied + into the simulator instance, and so when it is passed as an argument to ``assemble``, + it behaves as if the original backend was supplied (e.g. defaults from ``FakeArmonk`` will be + present and used by ``assemble``). + + .. code-block:: python + + armonk_sim = qiskit.providers.aer.PulseSimulator.from_backend(FakeArmonk()) + pulse_qobj = assemble(schedules, backend=armonk_sim) + armonk_sim.run(pulse_qobj) + + While the above example is small, the demonstrated 'drop-in replacement' behavior should + greatly improve the usability in more complicated work-flows, e.g. when calibration experiments + are constructed using backend attributes. diff --git a/releasenotes/notes/0.7/deprecate-snapshot-variance-7d02188deacf9a08.yaml b/releasenotes/notes/0.7/deprecate-snapshot-variance-7d02188deacf9a08.yaml new file mode 100644 index 0000000000..5ee827e4e0 --- /dev/null +++ b/releasenotes/notes/0.7/deprecate-snapshot-variance-7d02188deacf9a08.yaml @@ -0,0 +1,9 @@ +--- +deprecations: + - | + The ``variance`` kwarg of Snapshot instructions has been deprecated. This + function computed the sample variance in the snapshot due to noise model + sampling, not the variance due to measurement statistics so was often + being used incorrectly. If noise modeling variance is required single shot + snapshots should be used so variance can be computed manually in + post-processing. diff --git a/releasenotes/notes/drop-py35-814876e9d7354a39.yaml b/releasenotes/notes/0.7/drop-py35-814876e9d7354a39.yaml similarity index 100% rename from releasenotes/notes/drop-py35-814876e9d7354a39.yaml rename to releasenotes/notes/0.7/drop-py35-814876e9d7354a39.yaml diff --git a/releasenotes/notes/fix-cpp-for-loop-refs-5a6bb879bf58a4fb.yaml b/releasenotes/notes/0.7/fix-cpp-for-loop-refs-5a6bb879bf58a4fb.yaml similarity index 100% rename from releasenotes/notes/fix-cpp-for-loop-refs-5a6bb879bf58a4fb.yaml rename to releasenotes/notes/0.7/fix-cpp-for-loop-refs-5a6bb879bf58a4fb.yaml diff --git a/releasenotes/notes/0.7/fix-snapshot-copy-54ee685094d1e261.yaml b/releasenotes/notes/0.7/fix-snapshot-copy-54ee685094d1e261.yaml new file mode 100644 index 0000000000..b87a3cee53 --- /dev/null +++ b/releasenotes/notes/0.7/fix-snapshot-copy-54ee685094d1e261.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes a bug where snapshot data was always copied from C++ to Python rather + than moved where possible. This will halve memory usage and improve simulation + time when using large statevector or density matrix snapshots. diff --git a/releasenotes/notes/fix-y-expectation-1ad355c31b4c3247.yaml b/releasenotes/notes/0.7/fix-y-expectation-1ad355c31b4c3247.yaml similarity index 100% rename from releasenotes/notes/fix-y-expectation-1ad355c31b4c3247.yaml rename to releasenotes/notes/0.7/fix-y-expectation-1ad355c31b4c3247.yaml diff --git a/releasenotes/notes/0.7/fusion-threshold-33127b9b4e219640.yaml b/releasenotes/notes/0.7/fusion-threshold-33127b9b4e219640.yaml new file mode 100644 index 0000000000..53cc3db1d3 --- /dev/null +++ b/releasenotes/notes/0.7/fusion-threshold-33127b9b4e219640.yaml @@ -0,0 +1,19 @@ +--- +upgrade: + - | + Updates gate fusion default thresholds so that gate fusion will be applied + to circuits with of more than 14 qubits for statevector simulations on the + :class:`~qiskit.providers.aer.StatevectorSimulator` and + :class:`~qiskit.providers.aer.QasmSimulator`. + + For the ``"density_matrix"`` + method of the :class:`~qiskit.providers.aer.QasmSimulator` and for the + :class:`~qiskit.providers.aer.UnitarySimulator` gate fusion will be applied + to circuits with more than 7 qubits. + + Custom qubit threshold values can be set using the ``fusion_threshold`` + backend option ie ``backend.set_options(fusion_threshold=10)`` + - | + Changes ``fusion_threshold`` backend option to apply fusion when the + number of qubits is above the threshold, not equal or above the threshold, + to match the behavior of the OpenMP qubit threshold parameter. diff --git a/releasenotes/notes/global-phase-9fc2105dac076f48.yaml b/releasenotes/notes/0.7/global-phase-9fc2105dac076f48.yaml similarity index 100% rename from releasenotes/notes/global-phase-9fc2105dac076f48.yaml rename to releasenotes/notes/0.7/global-phase-9fc2105dac076f48.yaml diff --git a/releasenotes/notes/kraus-noise-fusion-5577d4a38862e966.yaml b/releasenotes/notes/0.7/kraus-noise-fusion-5577d4a38862e966.yaml similarity index 97% rename from releasenotes/notes/kraus-noise-fusion-5577d4a38862e966.yaml rename to releasenotes/notes/0.7/kraus-noise-fusion-5577d4a38862e966.yaml index 3a6558c5bc..28e76c61e7 100644 --- a/releasenotes/notes/kraus-noise-fusion-5577d4a38862e966.yaml +++ b/releasenotes/notes/0.7/kraus-noise-fusion-5577d4a38862e966.yaml @@ -3,4 +3,4 @@ features: - | Improves general noisy statevector simulation performance by adding a Kraus method to the gate fusion circuit optimization that allows applying gate - fusion to noisy statevector simulations with general Kraus noise. + fusion to noisy statevector simulations with general Kraus noise. \ No newline at end of file diff --git a/releasenotes/notes/0.7/move-final-snapshots-1cdc5e93b4695324.yaml b/releasenotes/notes/0.7/move-final-snapshots-1cdc5e93b4695324.yaml new file mode 100644 index 0000000000..eacd528ce9 --- /dev/null +++ b/releasenotes/notes/0.7/move-final-snapshots-1cdc5e93b4695324.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Use move semantics for statevector and density matrix snapshots for the + `"statevector"` and `"density_matrix"` methods of the + :class:`~qiskit.providers.aer.QasmSimulator` if they are the final + instruction in a circuit. This reduces the memory usage of the + simulator improves the performance by avoiding copying a large array in + the results. diff --git a/releasenotes/notes/0.7/mps-apply-kraus-3cc8de65a66385ab.yaml b/releasenotes/notes/0.7/mps-apply-kraus-3cc8de65a66385ab.yaml new file mode 100644 index 0000000000..7ec0f471fe --- /dev/null +++ b/releasenotes/notes/0.7/mps-apply-kraus-3cc8de65a66385ab.yaml @@ -0,0 +1,9 @@ +--- + +features: + - | + Adds support for general Kraus + :class:`~qiskit.providers.aer.noise.QauntumError` gate errors in the + :class:`~qiskit.providers.aer.noise.NoiseModel` to the + ``"matrix_product_state"`` method of the + :class:`~qiskit.providers.aer.QasmSimulator`. diff --git a/releasenotes/notes/0.7/mps-density-matrix-51f7d5b1ef51e746.yaml b/releasenotes/notes/0.7/mps-density-matrix-51f7d5b1ef51e746.yaml new file mode 100644 index 0000000000..e8ea0bf6ed --- /dev/null +++ b/releasenotes/notes/0.7/mps-density-matrix-51f7d5b1ef51e746.yaml @@ -0,0 +1,9 @@ +--- + +features: + - | + Adds support for density matrix snapshot instruction + :class:`qiskit.providers.aer.extensions.SnapshotDensityMatrix` to the + ``"matrix_product_state"`` method of the + :class:`~qiskit.providers.aer.QasmSimulator`. + diff --git a/releasenotes/notes/pybind-functors-a0b7b357fbe67dfb.yaml b/releasenotes/notes/0.7/pybind-functors-a0b7b357fbe67dfb.yaml similarity index 100% rename from releasenotes/notes/pybind-functors-a0b7b357fbe67dfb.yaml rename to releasenotes/notes/0.7/pybind-functors-a0b7b357fbe67dfb.yaml diff --git a/releasenotes/notes/0.7/simd-support-c2352ca3d639770f.yaml b/releasenotes/notes/0.7/simd-support-c2352ca3d639770f.yaml new file mode 100644 index 0000000000..b0cd4938f6 --- /dev/null +++ b/releasenotes/notes/0.7/simd-support-c2352ca3d639770f.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Extends the SIMD vectorization of the statevector simulation method to the + unitary matrix, superoperator matrix, and density matrix simulation methods. + This gives roughtly a 2x performance increase general simulation using the + :class:`~qiskit.providers.aer.UnitarySimulator`, the ``"density_matrix"`` + method of the :class:`~qiskit.providers.aer.QasmSimulator`, gate + fusion, and noise simulation. diff --git a/releasenotes/notes/0.7/validate-memory-statevector-d1b4fc48b002856f.yaml b/releasenotes/notes/0.7/validate-memory-statevector-d1b4fc48b002856f.yaml new file mode 100644 index 0000000000..e4d80eaeb9 --- /dev/null +++ b/releasenotes/notes/0.7/validate-memory-statevector-d1b4fc48b002856f.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Add missing available memory checks for the + :class:`~qiskit.providers.aer.StatevectorSimulator` and + :class:`~qiskit.providers.aer.UnitarySimulator`. This throws an exception if + the memory required to simulate the number of qubits in a circuit exceeds the + available memory of the system. diff --git a/releasenotes/notes/vector-class-ee57ad715b18cc55.yaml b/releasenotes/notes/0.7/vector-class-ee57ad715b18cc55.yaml similarity index 100% rename from releasenotes/notes/vector-class-ee57ad715b18cc55.yaml rename to releasenotes/notes/0.7/vector-class-ee57ad715b18cc55.yaml diff --git a/releasenotes/notes/add-python3.9-83b3b4e5c3d59571.yaml b/releasenotes/notes/add-python3.9-83b3b4e5c3d59571.yaml new file mode 100644 index 0000000000..dd4629c5cf --- /dev/null +++ b/releasenotes/notes/add-python3.9-83b3b4e5c3d59571.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + Python 3.9 support has been added in this release. You can now run Qiskit + Aer using Python 3.9 without building from source. +deprecations: + - | + Python 3.6 support has been deprecated and will be removed in a future + release. When support is removed you will need to upgrade the Python + version you're using to Python 3.7 or above. diff --git a/releasenotes/notes/aer-provider-instance-38b472f3ea541977.yaml b/releasenotes/notes/aer-provider-instance-38b472f3ea541977.yaml new file mode 100644 index 0000000000..95d97d7c51 --- /dev/null +++ b/releasenotes/notes/aer-provider-instance-38b472f3ea541977.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixes bug with :class:`~qiskit.providers.aer.AerProvider` where options set + on the returned backends using + :meth:`~qiskit.providers.aer.QasmSimulator.set_options` were stored in the + provider and would persist for subsequent calls to + :meth:`~qiskit.providers.aer.AerProvider.get_backend` for the same named + backend. Now every call to + and :meth:`~qiskit.providers.aer.AerProvider.backends` returns a new + instance of the simulator backend that can be configured. diff --git a/releasenotes/notes/allow_cuda_on_win-c3e5c920528eb282.yaml b/releasenotes/notes/allow_cuda_on_win-c3e5c920528eb282.yaml new file mode 100644 index 0000000000..ce10e274b3 --- /dev/null +++ b/releasenotes/notes/allow_cuda_on_win-c3e5c920528eb282.yaml @@ -0,0 +1,8 @@ +--- +features: + - | + It is now possible to build AER with CUDA enabled in Windows. +upgrade: + - | + ``pybind11`` minimum version required is 2.6 instead of 2.4. This is needed + in order to support CUDA enabled compilation in Windows. diff --git a/releasenotes/notes/basis-gates-acbc8a1a52a49413.yaml b/releasenotes/notes/basis-gates-acbc8a1a52a49413.yaml deleted file mode 100644 index fb0b1883d0..0000000000 --- a/releasenotes/notes/basis-gates-acbc8a1a52a49413.yaml +++ /dev/null @@ -1,33 +0,0 @@ ---- -features: - - | - Adds support for parameterized delay gate ``"delay"`` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and - :class:`~qiskit.providers.aer.QasmSimulator`. - By default this gate is treated as an identity gate during simulation. - - | - Adds support for parameterized phase gate ``"p"`` and controlled-phase - gate ``"cp"`` to the :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` and ``"density_matrix"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. - - | - Adds support for the multi-controlled phase gate ``"mcphase"`` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` method of the - :class:`~qiskit.providers.aer.QasmSimulator`. - - | - Adds support for the :math:`\sqrt(X)` gate ``"sx"`` to the - class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` and ``"density_matrix"`` methods of the - :class:`~qiskit.providers.aer.QasmSimulator`. -deprecations: - - | - :meth:`qiskit.providers.aer.noise.NoiseModel.set_x90_single_qubit_gates` has - been deprecated as unrolling to custom basis gates has been added to the - qiskit transpiler. The correct way to use an X90 based noise model is to - define noise on the Sqrt(X) "sx" or "rx" gate and one of the single-qubit - phase gates "u1", "rx", "p" in the noise model. diff --git a/releasenotes/notes/basis_gate_error_msg-04862cbd47f5f57f.yaml b/releasenotes/notes/basis_gate_error_msg-04862cbd47f5f57f.yaml new file mode 100644 index 0000000000..9744aabd11 --- /dev/null +++ b/releasenotes/notes/basis_gate_error_msg-04862cbd47f5f57f.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes bug in the error message returned when a circuit contains unsupported + simulator instructions. Previously some supported instructions were also + being listed in the error message along with the unsupported instructions. diff --git a/releasenotes/notes/cmake-optional-conan-bc99a895fc4345e1.yaml b/releasenotes/notes/cmake-optional-conan-bc99a895fc4345e1.yaml new file mode 100644 index 0000000000..ba3343d86f --- /dev/null +++ b/releasenotes/notes/cmake-optional-conan-bc99a895fc4345e1.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + Add the CMake flag ``DISABLE_CONAN`` (default=``OFF``)s. When installing from source, + setting this to ``ON`` allows bypassing the Conan package manager to find libraries + that are already installed on your system. This is also available as an environment + variable ``DISABLE_CONAN``, which takes precedence over the CMake flag. + This is not the official procedure to build AER. Thus, the user is responsible + of providing all needed libraries and corresponding files to make them findable to CMake. diff --git a/releasenotes/notes/cython-dep-removed-ffc095dcc575c449.yaml b/releasenotes/notes/cython-dep-removed-ffc095dcc575c449.yaml new file mode 100644 index 0000000000..7f51030ec2 --- /dev/null +++ b/releasenotes/notes/cython-dep-removed-ffc095dcc575c449.yaml @@ -0,0 +1,4 @@ +--- +upgrade: + - | + Code that dependes on Aer has been removed, so Cython is no longer a dependency. diff --git a/releasenotes/notes/diagnoal-b06c925f44540f8e.yaml b/releasenotes/notes/diagnoal-b06c925f44540f8e.yaml new file mode 100644 index 0000000000..c2d86c5942 --- /dev/null +++ b/releasenotes/notes/diagnoal-b06c925f44540f8e.yaml @@ -0,0 +1,8 @@ +--- + +features: + - | + Added support for MPS::apply_diagonal_matrix. + + + diff --git a/releasenotes/notes/extended_stabilizer_norm_estimation-d17632efe8d2bb19.yaml b/releasenotes/notes/extended_stabilizer_norm_estimation-d17632efe8d2bb19.yaml new file mode 100644 index 0000000000..1a8851c2dd --- /dev/null +++ b/releasenotes/notes/extended_stabilizer_norm_estimation-d17632efe8d2bb19.yaml @@ -0,0 +1,39 @@ +--- +features: + - | + Introduce a new method for performing measurements with the Extended + Stabilizer simulator, called Norm Estimation. This method can be used by passing + the following options to the ``QasmSimulator`` provider:: + + simulator = QasmSimulator( + method='extended_stabilizer, + extended_stabilizer_sampling_method="norm_estimation" + ) + + The norm estimation method is slower than the alternative `metropolis` + or `resampled_metropolis` options, but gives better performance on circuits + with sparse output distributions. See the documentation of the ``QasmSimulator`` + provider for more information. +fixes: + - | + Fixes an issue where the Extended Stabilizer simulator would give incorrect + results on quantum circuits with sparse output distributions. Refer to + `#306 ` for more information + and examples. +upgrade: + - | + The following options for the Extended Stabilizer simulator have changed. + + + ``extended_stabilizer_measure_sampling``: This option has been replaced + by the options ``extended_stabilizer_sampling_method``, which controls + how we simulate qubit measurement. + + + ``extended_stabilizer_mixing_time``: This option has been renamed as + ``extended_stabilizer_metropolis_mixing_time`` to clarify it only applies + to the `metropolis` and `resampled_metropolis` sampling methods. + + + ``extended_stabilizer_norm_estimation_samples``: This option has been renamed + to ``extended_stabilizer_norm_estimation_default_samples``. + + One additional option, ``extended_stabilizer_norm_estimation_repetitions`` has been + added, whih controls part of the behaviour of the norm estimation sampling method. diff --git a/releasenotes/notes/fix-backend-noise-model-f22e9d6254d70852.yaml b/releasenotes/notes/fix-backend-noise-model-f22e9d6254d70852.yaml new file mode 100644 index 0000000000..4bccf758cf --- /dev/null +++ b/releasenotes/notes/fix-backend-noise-model-f22e9d6254d70852.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixes issue with setting :class:`~qiskit.providers.aer.QasmSimulator` + basis gates when using ``"method"`` and ``"noise_model"`` options + together, and when using them with a simulator constructed using + :meth:`~qiskit.providers.aer.QasmSimulator.from_backend`. Now the + listed basis gates will be the intersection of gates supported by + the backend configuration, simulation method, and noise model basis + gates. If the intersection of the noise model basis gates and + simulator basis gates is empty a warning will be logged. diff --git a/releasenotes/notes/fix-basis-gates-3b5c64c3bbfee1c7.yaml b/releasenotes/notes/fix-basis-gates-3b5c64c3bbfee1c7.yaml new file mode 100644 index 0000000000..4ea46d74fc --- /dev/null +++ b/releasenotes/notes/fix-basis-gates-3b5c64c3bbfee1c7.yaml @@ -0,0 +1,14 @@ +--- +fixes: + - | + Fix bug where the `"sx"`` gate :class:`~qiskit.circuit.library.SXGate` was + not listed as a supported gate in the C++ code, in `StateOpSet` of + `matrix_product_state.hp`. + - | + Fix bug where ``"csx"``, ``"cu2"``, ``"cu3"`` were incorrectly listed as + supported basis gates for the ``"density_matrix"`` method of the + :class:`~qiskit.providers.aer.QasmSimulator`. + - | + Fix bug where parameters were passed incorrectly between functions in + `matrix_product_state_internal.cpp`, causing wrong simulation, as well + as reaching invalid states, which in turn caused an infinite loop. diff --git a/releasenotes/notes/fix-c-if-large-register-79d6649395e57ff5.yaml b/releasenotes/notes/fix-c-if-large-register-79d6649395e57ff5.yaml new file mode 100644 index 0000000000..384a52439a --- /dev/null +++ b/releasenotes/notes/fix-c-if-large-register-79d6649395e57ff5.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Fixes a bug that resulted in `c_if` not working when the + width of the conditional register was greater than 64. See + `#1077 `. diff --git a/releasenotes/notes/fix-conditional-noise-178f5d03b97a8dd5.yaml b/releasenotes/notes/fix-conditional-noise-178f5d03b97a8dd5.yaml new file mode 100644 index 0000000000..5feff33f54 --- /dev/null +++ b/releasenotes/notes/fix-conditional-noise-178f5d03b97a8dd5.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Fixes a bug ([#1153](https://github.com/Qiskit/qiskit-aer/issues/1153)) + where noise on conditional gates was always being applied regardless of + whether the conditional gate was actually applied based on the classical + register value. Now noise on a conditional gate will only be applied in + the case where the conditional gate is applied. diff --git a/releasenotes/notes/fix-noise-basis-gates-ecdfa43394ff78e3.yaml b/releasenotes/notes/fix-noise-basis-gates-ecdfa43394ff78e3.yaml new file mode 100644 index 0000000000..5c9edc2253 --- /dev/null +++ b/releasenotes/notes/fix-noise-basis-gates-ecdfa43394ff78e3.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixes bug in + :meth:`~qiskit.providers.aer.noise.NoiseModel.from_backend` and + :meth:`~qiskit.providers.aer.QasmSimulator.from_backend` where + :attr:`~qiskit.providers.aer.noise.NoiseModel.basis_gates` was set + incorrectly for IBMQ devices with basis gate set + ``['id', 'rz', 'sx', 'x', 'cx']``. Now the noise model will always + have the same basis gates as the backend basis gates regardless of + whether those instructions have errors in the noise model or not. diff --git a/releasenotes/notes/fix-omp-nested-9d120cd1a08725ab.yaml b/releasenotes/notes/fix-omp-nested-9d120cd1a08725ab.yaml new file mode 100644 index 0000000000..bab20a0be8 --- /dev/null +++ b/releasenotes/notes/fix-omp-nested-9d120cd1a08725ab.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixes a bug with nested OpenMP flag was being set to true when it + shouldn't be. diff --git a/releasenotes/notes/fix-truncation-85010290548c2d16.yaml b/releasenotes/notes/fix-truncation-85010290548c2d16.yaml new file mode 100644 index 0000000000..3fddd50ae2 --- /dev/null +++ b/releasenotes/notes/fix-truncation-85010290548c2d16.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixes a bug when applying truncation in the matrix product state method of the QasmSimulator. + diff --git a/releasenotes/notes/initialize-in-mps-e9f8b70c798e7e25.yaml b/releasenotes/notes/initialize-in-mps-e9f8b70c798e7e25.yaml new file mode 100644 index 0000000000..e02790fb1e --- /dev/null +++ b/releasenotes/notes/initialize-in-mps-e9f8b70c798e7e25.yaml @@ -0,0 +1,6 @@ +--- + +features: + - | + Added support for `initialize` in the MPS simulation method. + diff --git a/releasenotes/notes/issue1126-e2d51f660b0078db.yaml b/releasenotes/notes/issue1126-e2d51f660b0078db.yaml new file mode 100644 index 0000000000..dde0ecfd45 --- /dev/null +++ b/releasenotes/notes/issue1126-e2d51f660b0078db.yaml @@ -0,0 +1,7 @@ +--- + +fixes: + - | + Fixed issue #1126: bug in reporting measurement of a single qubit. The bug + occured when copying the measured value to the output data structure. + diff --git a/releasenotes/notes/kraus-bug-a1940e9b12e40f70.yaml b/releasenotes/notes/kraus-bug-a1940e9b12e40f70.yaml new file mode 100644 index 0000000000..2ca171188f --- /dev/null +++ b/releasenotes/notes/kraus-bug-a1940e9b12e40f70.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + In MPS, apply_kraus was operating directly on the input bits in the + parameter qubits, instead of on the internal qubits. In the MPS algorithm, + the qubits are constantly moving around so all operations should be applied + to the internal qubits. diff --git a/releasenotes/notes/pauli-instruction-c3aa2c0c634e642e.yaml b/releasenotes/notes/pauli-instruction-c3aa2c0c634e642e.yaml new file mode 100644 index 0000000000..7a01c3e25d --- /dev/null +++ b/releasenotes/notes/pauli-instruction-c3aa2c0c634e642e.yaml @@ -0,0 +1,10 @@ +--- + +features: + - | + Adds support for optimized N-qubit Pauli gate ( + :class:`qiskit.circuit.library.generalized_gates.PauliGate`) to the + :class:`~qiskit.providers.aer.StatevectorSimulator`, + :class:`~qiskit.providers.aer.UnitarySimulator`, and the + statevector and density matrix methods of the + :class:`~qiskit.providers.aer.QasmSimulator`. diff --git a/releasenotes/notes/remove-deprecated-ba6eb1060207af99.yaml b/releasenotes/notes/remove-deprecated-ba6eb1060207af99.yaml new file mode 100644 index 0000000000..c5e6c4bfc7 --- /dev/null +++ b/releasenotes/notes/remove-deprecated-ba6eb1060207af99.yaml @@ -0,0 +1,6 @@ +--- +upgrade: + - | + Removed x90 gate decomposition from noise models that was deprecated + in qiskit-aer 0.7. This decomposition is now done by using regular + noise model basis gates and the qiskit transpiler. diff --git a/releasenotes/notes/rotation-gates-7c09f4565329ca78.yaml b/releasenotes/notes/rotation-gates-7c09f4565329ca78.yaml deleted file mode 100644 index 367cfa18ac..0000000000 --- a/releasenotes/notes/rotation-gates-7c09f4565329ca78.yaml +++ /dev/null @@ -1,16 +0,0 @@ ---- -features: - - | - Adds support for 1 and 2-qubit rotation gates ``"rx"``, ``"ry"``, ``"rz"``, - ``"r"``, ``"rxx"``, ``"ryy"``, ``"rzz"``, ``"rzx"`` to the - :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` and ``"density_matrix"`` methods of the - :class:`~qiskit.providers.aer.StatevectorSimulator`. - - | - Adds support for multi-controlled rotation gates ``"mcr"``, ``"mcrx"``, - ``"mcry"``, ``"mcrz"`` - to the :class:`~qiskit.providers.aer.StatevectorSimulator`, - :class:`~qiskit.providers.aer.UnitarySimulator`, and the - ``"statevector"`` method of the - :class:`~qiskit.providers.aer.StatevectorSimulator`. diff --git a/releasenotes/notes/sample_measure-bug-4ce3a1a6553e4900.yaml b/releasenotes/notes/sample_measure-bug-4ce3a1a6553e4900.yaml new file mode 100644 index 0000000000..bb75ccb5cb --- /dev/null +++ b/releasenotes/notes/sample_measure-bug-4ce3a1a6553e4900.yaml @@ -0,0 +1,10 @@ +--- + +fixes: + - | + When invoking MPS::sample_measure, we need to first sort the qubits to the + default ordering because this is the assumption in qasm_controller.This is + done by invoking the method move_all_qubits_to_sorted_ordering. It was + correct in sample_measure_using_apply_measure, but missing in + sample_measure_using_probabilities. + diff --git a/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml b/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml new file mode 100644 index 0000000000..2261807c93 --- /dev/null +++ b/releasenotes/notes/save-data-instructions-24b127612c9f6502.yaml @@ -0,0 +1,107 @@ +--- +features: + - | + Adds a :class:`qiskit.providers.aer.library.SaveExpectationValue` + quantum circuit instruction for saving the expectation value + :math:`\langle H\rangle = Tr[H\rho]` of a Hermitian operator :math:`H` + for the simulator state :math:`\rho`. + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_expectation_value`` + circuit method which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveExpectationValueVariance` + quantum circuit instruction for saving the expectation value + :math:`\langle H\rangle = Tr[H\rho]`, and variance + :math:`Var(H) = \langle H^2\rangle - \langle H\rangle^2`, of a + Hermitian operator :math:`H` for the simulator state :math:`\rho`. + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_expectation_value_variance`` + circuit method which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveStatevector` quantum circuit + instruction for saving the current statevector of the + :class:`~qiskit.providers.aer.StatevectorSimulator` or + :class:`~qiskit.providers.aer.QasmSimulator` with a supported simulation + method (``"statevector"``, ``"statevector_gpu"``, + ``"matrix_product_state"``, ``"extended_stabilizer"``). Note that this + instruction always acts on the full width of the circuit being executed. + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_statevector`` circuit method + which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveDensityMatrix` quantum circuit + instruction for saving the reduced density matrix on the specified qubits + for the current simulator state of the + :class:`~qiskit.providers.aer.StatevectorSimulator` or + :class:`~qiskit.providers.aer.QasmSimulator` with a supported simulation + method (``"density_matrix"``, ``"density_matrix_gpu"``, ``"statevector"``, + ``"statevector_gpu"``, ``"matrix_product_state"``). + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_density_matrix`` circuit method + which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveProbabilities` and + :class:`qiskit.providers.aer.library.SaveProbabilitiesDict` quantum + circuit instruction for saving all measurement outcome probabilities for + the :class:`~qiskit.providers.aer.StatevectorSimulator` or + :class:`~qiskit.providers.aer.QasmSimulator` with a supported simulation + method (``"statevector"``, ``"statevector_gpu"``, ``"density_matrix"``, + ``"density_matrix_gpu"``, ``"matrix_product_state"``, ``"stabilizer"``). + + These instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_probabilities`` and + :class:`~qiskit.providers.aer.library.save_probabilities_dict`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveAmplitudes` + circuit instruction for saving complex statevector amplitudes for + the :class:`~qiskit.providers.aer.StatevectorSimulator` or + :class:`~qiskit.providers.aer.QasmSimulator` with a supported simulation + method (``"statevector"``, ``"statevector_gpu"``, + ``"matrix_product_state"``). + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_amplitudes`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveAmplitudesSquared` + circuit instruction for saving specific probability values (squared + statevector amplitude) for + the :class:`~qiskit.providers.aer.StatevectorSimulator` or + :class:`~qiskit.providers.aer.QasmSimulator` with a supported simulation + method (``"statevector"``, ``"statevector_gpu"``, ``"density_matrix"``, + ``"density_matrix_gpu"``, ``"matrix_product_state"``, ``"stabilizer``). + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_amplitudes_squared`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveStabilizer` + circuit instruction for saving the state of the `"stabilizer"` method + of the :class:`~qiskit.providers.aer.QasmSimulator` as a Clifford. + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_stabilizer`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveUnitary` + circuit instruction for saving the state of the + :class:`~qiskit.providers.aer.UnitarySimulator`. + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_unitary`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. + - | + Adds a :class:`qiskit.providers.aer.library.SaveState` + circuit instruction for saving the state of the + :class:`~qiskit.providers.aer.QasmSimulator`. The format of the saved + state depends on the simulation method + (eg. statevector, density matrix, etc). + + This instruction can also be appended to a quantum circuit by using the + :class:`~qiskit.providers.aer.library.save_state`` circuit + methods which is added to ``QuantumCircuit`` when importing Aer. diff --git a/releasenotes/notes/update-conan-0e891458bcc96ee2.yaml b/releasenotes/notes/update-conan-0e891458bcc96ee2.yaml new file mode 100644 index 0000000000..a7355f6610 --- /dev/null +++ b/releasenotes/notes/update-conan-0e891458bcc96ee2.yaml @@ -0,0 +1,7 @@ +--- +upgrade: + - | + The minimum version of `Conan `__ has been increased to 1.31.2. This was necessary + to fix a compatibility issue with newer versions of the `urllib3 `__ (which + is a dependency of Conan). It also adds native support for AppleClang 12 which is useful for users with + new Apple computers. diff --git a/requirements-dev.txt b/requirements-dev.txt index 0b1eb58f5a..8629319616 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,10 +1,9 @@ stestr>=2.5.0 cmake!=3.17.1,!=3.17.0 -conan>=1.22.2 +conan>=1.31.2 scikit-build -cython asv -cvxpy>=1.0.0 +cvxpy>=1.0.0,<1.1.8;python_version>'3.6' and python_version<='3.8' pylint pycodestyle Sphinx>=1.8.3 diff --git a/setup.py b/setup.py index e0d5ad6cc6..bca776800e 100644 --- a/setup.py +++ b/setup.py @@ -3,38 +3,39 @@ """ Main setup file for qiskit-aer """ - +import distutils.util +import importlib +import inspect import os +import setuptools import subprocess import sys -import inspect +import platform + PACKAGE_NAME = os.getenv('QISKIT_AER_PACKAGE_NAME', 'qiskit-aer') +_DISABLE_CONAN = distutils.util.strtobool(os.getenv("DISABLE_CONAN", "OFF").lower()) -try: - from Cython.Build import cythonize -except ImportError: - import subprocess - subprocess.call([sys.executable, '-m', 'pip', 'install', 'Cython>=0.27.1']) - from Cython.Build import cythonize - -try: - from conans import client -except ImportError: - subprocess.call([sys.executable, '-m', 'pip', 'install', 'conan']) - from conans import client +if not _DISABLE_CONAN: + try: + from conans import client + except ImportError: + subprocess.call([sys.executable, '-m', 'pip', 'install', 'conan>=1.31.2']) + from conans import client try: from skbuild import setup except ImportError: subprocess.call([sys.executable, '-m', 'pip', 'install', 'scikit-build']) from skbuild import setup + try: import pybind11 except ImportError: - subprocess.call([sys.executable, '-m', 'pip', 'install', 'pybind11>=2.4']) + subprocess.call([sys.executable, '-m', 'pip', 'install', 'pybind11>=2.6']) + +from skbuild import setup -import setuptools # These are requirements that are both runtime/install dependencies and # also build time/setup requirements and will be added to both lists @@ -42,8 +43,7 @@ common_requirements = [ 'numpy>=1.16.3', 'scipy>=1.0', - 'cython>=0.27.1', - 'pybind11>=2.4' # This isn't really an install requirement, + 'pybind11>=2.6' # This isn't really an install requirement, # Pybind11 is required to be pre-installed for # CMake to successfully find header files. # This should be fixed in the CMake build files. @@ -52,10 +52,11 @@ setup_requirements = common_requirements + [ 'scikit-build', 'cmake!=3.17,!=3.17.0', - 'conan>=1.22.2' ] +if not _DISABLE_CONAN: + setup_requirements.append('conan>=1.22.2') -requirements = common_requirements + ['qiskit-terra>=0.12.0'] +requirements = common_requirements + ['qiskit-terra>=0.16.0'] if not hasattr(setuptools, 'find_namespace_packages') or not inspect.ismethod( @@ -75,6 +76,12 @@ with open(README_PATH) as readme_file: README = readme_file.read() + +cmake_args = ["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9"] +is_win_32_bit = (platform.system() == 'Windows' and platform.architecture()[0] == "32bit") +if is_win_32_bit: + cmake_args.append("-DCMAKE_GENERATOR_PLATFORM=Win32") + setup( name=PACKAGE_NAME, version=VERSION, @@ -100,13 +107,14 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", "Topic :: Scientific/Engineering", ], python_requires=">=3.6", install_requires=requirements, setup_requires=setup_requirements, include_package_data=True, - cmake_args=["-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9"], + cmake_args=cmake_args, keywords="qiskit aer simulator quantum addon backend", zip_safe=False ) diff --git a/src/controllers/controller.hpp b/src/controllers/controller.hpp index 9e53b7cee2..f20e0e3e3f 100755 --- a/src/controllers/controller.hpp +++ b/src/controllers/controller.hpp @@ -37,15 +37,21 @@ #include #endif +#ifdef AER_MPI +#include +#endif + + // Base Controller #include "framework/creg.hpp" #include "framework/qobj.hpp" -#include "framework/results/experiment_data.hpp" +#include "framework/results/experiment_result.hpp" #include "framework/results/result.hpp" #include "framework/rng.hpp" #include "noise/noise_model.hpp" #include "transpile/basic_opts.hpp" #include "transpile/truncate_qubits.hpp" +#include "transpile/cacheblocking.hpp" namespace AER { namespace Base { @@ -137,14 +143,14 @@ class Controller { virtual void execute_circuit(Circuit &circ, Noise::NoiseModel &noise, const json_t &config, - ExperimentResult &exp_result); + ExperimentResult &result); // Abstract method for executing a circuit. // This method must initialize a state and return output data for // the required number of shots. virtual void run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, uint_t shots, uint_t rng_seed, - ExperimentData &data) const = 0; + ExperimentResult &result) const = 0; //------------------------------------------------------------------------- // State validation @@ -179,6 +185,13 @@ class Controller { // Validation threshold for validating states and operators double validation_threshold_ = 1e-8; + // Save counts as memory list + bool save_creg_memory_ = false; + + // Save count data + void save_count_data(ExperimentResult &result, + const ClassicalRegister &creg) const; + //----------------------------------------------------------------------- // Parallelization Config //----------------------------------------------------------------------- @@ -189,7 +202,7 @@ class Controller { // Set parallelization for experiments virtual void set_parallelization_experiments(const std::vector &circuits, - const Noise::NoiseModel &noise); + const std::vector &noise); // Set parallelization for a circuit virtual void set_parallelization_circuit(const Circuit &circuit, @@ -199,8 +212,37 @@ class Controller { virtual size_t required_memory_mb(const Circuit &circuit, const Noise::NoiseModel &noise) const = 0; + // Set distributed parallelization + virtual void + set_distributed_parallelization(const std::vector &circuits, + const std::vector &noise); + + virtual bool multiple_chunk_required(const Circuit &circuit, + const Noise::NoiseModel &noise) const; + + void save_exception_to_results(Result &result,const std::exception &e); + + + //setting cache blocking transpiler + Transpile::CacheBlocking transpile_cache_blocking(const Circuit& circ, + const Noise::NoiseModel& noise, + const json_t& config, + const size_t complex_size,bool is_matrix) const; + + // Get system memory size size_t get_system_memory_mb(); + size_t get_gpu_memory_mb(); + + uint_t get_distributed_num_processes(bool par_shots) const; + + size_t get_min_memory_mb() const + { + if(num_gpus_ > 0){ + return max_gpu_memory_mb_ / num_gpus_; //return per GPU memory size + } + return max_memory_mb_; + } // The maximum number of threads to use for various levels of parallelization int max_parallel_threads_; @@ -209,6 +251,8 @@ class Controller { int max_parallel_experiments_; int max_parallel_shots_; size_t max_memory_mb_; + size_t max_gpu_memory_mb_; + int num_gpus_; //max number of GPU per process // use explicit parallelization bool explicit_parallelization_; @@ -217,6 +261,33 @@ class Controller { int parallel_experiments_; int parallel_shots_; int parallel_state_update_; + + bool parallel_nested_ = false; + + //max number of qubits in given circuits + int max_qubits_; + + //results are stored independently in each process if true + bool accept_distributed_results_ = true; + + //distributed experiments (MPI) + int distributed_experiments_rank_ = 0; + int distributed_experiments_group_id_ = 0; + uint_t distributed_experiments_num_processes_ = 1; + int distributed_experiments_ = 1; + uint_t num_process_per_experiment_; + uint_t distributed_experiments_begin_; + uint_t distributed_experiments_end_; + + //distributed shots (MPI) + int distributed_shots_rank_ = 0; + int distributed_shots_ = 1; + + //process information (MPI) + int myrank_ = 0; + int num_processes_ = 1; + + uint_t cache_block_qubit_ = 0; }; //========================================================================= @@ -232,6 +303,9 @@ void Controller::set_config(const json_t &config) { // Load validation threshold JSON::get_value(validation_threshold_, "validation_threshold", config); + // Load config for memory (creg list data) + JSON::get_value(save_creg_memory_, "memory", config); + #ifdef _OPENMP // Load OpenMP maximum thread settings if (JSON::check_key("max_parallel_threads", config)) @@ -251,6 +325,7 @@ void Controller::set_config(const json_t &config) { max_parallel_threads_ = 1; max_parallel_shots_ = 1; max_parallel_experiments_ = 1; + parallel_nested_ = false; #endif // Load configurations for parallelization @@ -282,6 +357,16 @@ void Controller::set_config(const json_t &config) { parallel_shots_ = std::max({parallel_shots_, 1}); parallel_state_update_ = std::max({parallel_state_update_, 1}); } + + if (JSON::check_key("accept_distributed_results", config)) { + JSON::get_value(accept_distributed_results_, "accept_distributed_results", config); + } + + //enable multiple qregs if cache blocking is enabled + cache_block_qubit_ = 0; + if(JSON::check_key("blocking_qubits", config)){ + JSON::get_value(cache_block_qubit_,"blocking_qubits", config); + } } void Controller::clear_config() { @@ -297,13 +382,22 @@ void Controller::clear_parallelization() { parallel_experiments_ = 1; parallel_shots_ = 1; parallel_state_update_ = 1; + parallel_nested_ = false; + + num_process_per_experiment_ = 1; + distributed_experiments_ = 1; + distributed_shots_ = 1; + + num_gpus_ = 0; explicit_parallelization_ = false; - max_memory_mb_ = get_system_memory_mb() / 2; + max_memory_mb_ = get_system_memory_mb(); + max_gpu_memory_mb_ = get_gpu_memory_mb(); } void Controller::set_parallelization_experiments( - const std::vector &circuits, const Noise::NoiseModel &noise) { + const std::vector &circuits, const std::vector &noise) +{ // Use a local variable to not override stored maximum based // on currently executed circuits const auto max_experiments = @@ -311,24 +405,31 @@ void Controller::set_parallelization_experiments( ? std::min({max_parallel_experiments_, max_parallel_threads_}) : max_parallel_threads_; - if (max_experiments == 1) { + if (max_experiments == 1 && num_processes_ == 1) { // No parallel experiment execution parallel_experiments_ = 1; return; } // If memory allows, execute experiments in parallel +#ifdef AER_MPI + std::vector required_memory_mb_list(distributed_experiments_end_ - distributed_experiments_begin_); + for (size_t j = 0; j < distributed_experiments_end_-distributed_experiments_begin_; j++) { + required_memory_mb_list[j] = required_memory_mb(circuits[j+distributed_experiments_begin_], noise[j+distributed_experiments_begin_]) / num_process_per_experiment_; + } +#else std::vector required_memory_mb_list(circuits.size()); for (size_t j = 0; j < circuits.size(); j++) { - required_memory_mb_list[j] = required_memory_mb(circuits[j], noise); + required_memory_mb_list[j] = required_memory_mb(circuits[j], noise[j]); } +#endif std::sort(required_memory_mb_list.begin(), required_memory_mb_list.end(), std::greater<>()); size_t total_memory = 0; parallel_experiments_ = 0; for (size_t required_memory_mb : required_memory_mb_list) { total_memory += required_memory_mb; - if (total_memory > max_memory_mb_) + if (total_memory > max_memory_mb_*num_process_per_experiment_) break; ++parallel_experiments_; } @@ -336,9 +437,15 @@ void Controller::set_parallelization_experiments( if (parallel_experiments_ <= 0) throw std::runtime_error( "a circuit requires more memory than max_memory_mb."); +#ifdef AER_MPI + parallel_experiments_ = + std::min({parallel_experiments_, max_experiments, + max_parallel_threads_, static_cast(distributed_experiments_end_ - distributed_experiments_begin_)}); +#else parallel_experiments_ = std::min({parallel_experiments_, max_experiments, max_parallel_threads_, static_cast(circuits.size())}); +#endif } void Controller::set_parallelization_circuit(const Circuit &circ, @@ -359,16 +466,21 @@ void Controller::set_parallelization_circuit(const Circuit &circ, // Parallel shots is > 1 // Limit parallel shots by available memory and number of shots // And assign the remaining threads to state update - int circ_memory_mb = required_memory_mb(circ, noise); - if (max_memory_mb_ < circ_memory_mb) + int circ_memory_mb = required_memory_mb(circ, noise) / num_process_per_experiment_; + if (max_memory_mb_ + max_gpu_memory_mb_ < circ_memory_mb) throw std::runtime_error( "a circuit requires more memory than max_memory_mb."); // If circ memory is 0, set it to 1 so that we don't divide by zero circ_memory_mb = std::max({1, circ_memory_mb}); +#ifdef AER_MPI + int shots = (circ.shots * (distributed_shots_rank_ + 1)/distributed_shots_) - (circ.shots * distributed_shots_rank_ /distributed_shots_); +#else + int shots = circ.shots; +#endif parallel_shots_ = std::min({static_cast(max_memory_mb_ / circ_memory_mb), - max_shots, static_cast(circ.shots)}); + max_shots, shots}); } parallel_state_update_ = (parallel_shots_ > 1) @@ -376,6 +488,87 @@ void Controller::set_parallelization_circuit(const Circuit &circ, : std::max({1, max_parallel_threads_ / parallel_experiments_}); } +void Controller::set_distributed_parallelization(const std::vector &circuits, + const std::vector &noise) +{ + std::vector required_memory_mb_list(circuits.size()); + num_process_per_experiment_ = 1; + for (size_t j = 0; j < circuits.size(); j++) { + size_t size = required_memory_mb(circuits[j], noise[j]); + if(size > max_memory_mb_ + max_gpu_memory_mb_){ + num_process_per_experiment_ = std::max(num_process_per_experiment_,(size + (max_memory_mb_+max_gpu_memory_mb_) - 1) / (max_memory_mb_+max_gpu_memory_mb_)); + } + } + while((num_processes_ % num_process_per_experiment_) != 0){ + num_process_per_experiment_++; + } + + distributed_experiments_ = num_processes_ / num_process_per_experiment_; + + if(circuits.size() < distributed_experiments_){ + // e.g. np = 8, circuits = 3, npe = 2, de = 4 -> 3 , then np_in_group = [3,3,2] + // np = 4, circuits = 1, npe = 2, de = 2 -> 1 , then np_in_group = [4] + distributed_experiments_ = circuits.size(); + + distributed_experiments_num_processes_ = (num_processes_ + distributed_experiments_ - 1)/distributed_experiments_; + distributed_experiments_group_id_ = myrank_ / distributed_experiments_num_processes_; + if((distributed_experiments_group_id_+1)*distributed_experiments_num_processes_ > num_processes_){ + distributed_experiments_num_processes_ = num_processes_ - distributed_experiments_group_id_*distributed_experiments_num_processes_; + } + + if(distributed_experiments_num_processes_ > num_process_per_experiment_ && (distributed_experiments_num_processes_ % num_process_per_experiment_) == 0){ + distributed_shots_ = distributed_experiments_num_processes_ / num_process_per_experiment_; + distributed_shots_rank_ = 0; + } + else{ + //shots are not distributed + distributed_shots_ = 1; + distributed_shots_rank_ = 0; + } + distributed_experiments_rank_ = myrank_ % distributed_experiments_; + + distributed_experiments_begin_ = distributed_experiments_group_id_; + distributed_experiments_end_ = distributed_experiments_begin_ + 1; + } + else{ + distributed_experiments_group_id_ = myrank_ / num_process_per_experiment_; + distributed_experiments_rank_ = myrank_ % num_process_per_experiment_; + distributed_experiments_num_processes_ = num_process_per_experiment_; + + distributed_experiments_begin_ = circuits.size() * distributed_experiments_group_id_ / distributed_experiments_; + distributed_experiments_end_ = circuits.size() * (distributed_experiments_group_id_ + 1) / distributed_experiments_; + + //shots are not distributed + distributed_shots_ = 1; + distributed_shots_rank_ = 0; + } +} + +uint_t Controller::get_distributed_num_processes(bool par_shots) const +{ + if(par_shots){ + return num_process_per_experiment_; + } + else{ + return distributed_experiments_num_processes_; //no shot distribution, parallelize this experiment by processes in group + } +} + +bool Controller::multiple_chunk_required(const Circuit &circ, + const Noise::NoiseModel &noise) const +{ + if(circ.num_qubits < 3) + return false; + + if(num_process_per_experiment_ > 1 || Controller::get_min_memory_mb() < required_memory_mb(circ, noise)) + return true; + + if(cache_block_qubit_ >= 2 && cache_block_qubit_ < circ.num_qubits) + return true; + + return false; +} + size_t Controller::get_system_memory_mb() { size_t total_physical_memory = 0; #if defined(__linux__) || defined(__APPLE__) @@ -388,6 +581,44 @@ size_t Controller::get_system_memory_mb() { GlobalMemoryStatusEx(&status); total_physical_memory = status.ullTotalPhys; #endif +#ifdef AER_MPI + //get minimum memory size per process + uint64_t locMem,minMem; + locMem = total_physical_memory; + MPI_Allreduce(&locMem,&minMem,1,MPI_UINT64_T,MPI_MIN,MPI_COMM_WORLD); + total_physical_memory = minMem; +#endif + + return total_physical_memory >> 20; +} + +size_t Controller::get_gpu_memory_mb() { + size_t total_physical_memory = 0; +#ifdef AER_THRUST_CUDA + int iDev,nDev,j; + if(cudaGetDeviceCount(&nDev) != cudaSuccess){ + cudaGetLastError(); + nDev = 0; + } + for(iDev=0;iDev> 20; } @@ -434,8 +665,8 @@ bool Controller::validate_memory_requirements(const state_t &state, if (max_memory_mb_ == 0) return true; - size_t required_mb = state.required_memory_mb(circ.num_qubits, circ.ops); - if (max_memory_mb_ < required_mb) { + size_t required_mb = state.required_memory_mb(circ.num_qubits, circ.ops) / num_process_per_experiment_; + if (max_memory_mb_+max_gpu_memory_mb_ < required_mb) { if (throw_except) { std::string name = ""; JSON::get_value(name, "name", circ.header); @@ -447,10 +678,47 @@ bool Controller::validate_memory_requirements(const state_t &state, return true; } +void Controller::save_exception_to_results(Result &result,const std::exception &e) +{ + result.status = Result::Status::error; + result.message = e.what(); + for(auto& res : result.results){ + res.status = ExperimentResult::Status::error; + res.message = e.what(); + } +} + +Transpile::CacheBlocking Controller::transpile_cache_blocking(const Circuit& circ, + const Noise::NoiseModel& noise, + const json_t& config, + const size_t complex_size,bool is_matrix) const +{ + Transpile::CacheBlocking cache_block_pass; + + cache_block_pass.set_config(config); + if(!cache_block_pass.enabled()){ + //if blocking is not set by config, automatically set if required + if(multiple_chunk_required(circ,noise)){ + int nplace = num_process_per_experiment_; + if(num_gpus_ > 0) + nplace *= num_gpus_; + cache_block_pass.set_blocking(circ.num_qubits, get_min_memory_mb() << 20, nplace, complex_size,is_matrix); + } + } + + return cache_block_pass; +} + //------------------------------------------------------------------------- // Qobj execution //------------------------------------------------------------------------- -Result Controller::execute(const json_t &qobj_js) { +Result Controller::execute(const json_t &qobj_js) +{ +#ifdef AER_MPI + MPI_Comm_size(MPI_COMM_WORLD,&num_processes_); + MPI_Comm_rank(MPI_COMM_WORLD,&myrank_); +#endif + // Load QOBJ in a try block so we can catch parsing errors and still return // a valid JSON output containing the error message. try { @@ -475,12 +743,13 @@ Result Controller::execute(const json_t &qobj_js) { } // Stop the timer and add total timing data including qobj parsing auto timer_stop = myclock_t::now(); - result.metadata["time_taken"] = - std::chrono::duration(timer_stop - timer_start).count(); + auto time_taken = std::chrono::duration(timer_stop - 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; @@ -493,49 +762,119 @@ Result Controller::execute(const json_t &qobj_js) { Result Controller::execute(std::vector &circuits, const Noise::NoiseModel &noise_model, - const json_t &config) { + const json_t &config) +{ // Start QOBJ timer auto timer_start = myclock_t::now(); // Initialize Result object for the given number of experiments - const auto num_circuits = circuits.size(); - Result result(num_circuits); + Result result(circuits.size()); + // Make a copy of the noise model for each circuit execution + // so that it can be modified if required + std::vector circ_noise_models(circuits.size(),noise_model); // Execute each circuit in a try block try { + //truncate circuits before experiment settings (to get correct required_memory_mb value) + if (truncate_qubits_) { + for(size_t j = 0; j < circuits.size(); j++) { + // Truncate unused qubits from circuit and noise model + Transpile::TruncateQubits truncate_pass; + truncate_pass.set_config(config); + truncate_pass.optimize_circuit(circuits[j], circ_noise_models[j], circuits[j].opset(), + result.results[j]); + } + } + +#ifdef AER_MPI + try{ + //catch exception raised by required_memory_mb because of invalid simulation method + set_distributed_parallelization(circuits, circ_noise_models); + } + catch (std::exception &e) { + save_exception_to_results(result,e); + } + + const auto num_circuits = distributed_experiments_end_ - distributed_experiments_begin_; + result.resize(num_circuits); +#endif + + //get max qubits for this process (to allocate qubit register at once) + max_qubits_ = 0; +#ifdef AER_MPI + for (size_t j = distributed_experiments_begin_; j < distributed_experiments_end_; j++) { +#else + for (size_t j = 0; j < circuits.size(); j++) { +#endif + if(circuits[j].num_qubits > max_qubits_){ + max_qubits_ = circuits[j].num_qubits; + } + } + if (!explicit_parallelization_) { // set parallelization for experiments - set_parallelization_experiments(circuits, noise_model); + try{ + //catch exception raised by required_memory_mb because of invalid simulation method + set_parallelization_experiments(circuits, circ_noise_models); + } + catch (std::exception &e) { + save_exception_to_results(result,e); + } } #ifdef _OPENMP - result.metadata["omp_enabled"] = true; + result.metadata.add(true, "omp_enabled"); #else - result.metadata["omp_enabled"] = false; + result.metadata.add(false, "omp_enabled"); +#endif + result.metadata.add(parallel_experiments_, "parallel_experiments"); + result.metadata.add(max_memory_mb_, "max_memory_mb"); + result.metadata.add(max_gpu_memory_mb_,"max_gpu_memory_mb"); + + //store rank and number of processes, if no distribution rank=0 procs=1 is set + result.metadata.add(num_processes_,"num_mpi_processes"); + result.metadata.add(myrank_,"mpi_rank"); +#ifdef AER_MPI + result.metadata.add(distributed_experiments_,"distributed_experiments"); + result.metadata.add(distributed_experiments_group_id_,"distributed_experiments_group_id"); + result.metadata.add(distributed_experiments_rank_,"distributed_experiments_rank_in_group"); #endif - result.metadata["parallel_experiments"] = parallel_experiments_; - result.metadata["max_memory_mb"] = max_memory_mb_; #ifdef _OPENMP - if (parallel_shots_ > 1 || parallel_state_update_ > 1) + // Check if circuit parallelism is nested with one of the others + if (parallel_experiments_ > 1 && parallel_experiments_ < max_parallel_threads_) { + // Nested parallel experiments + parallel_nested_ = true; + #ifdef _WIN32 omp_set_nested(1); + #else + omp_set_max_active_levels(3); + #endif + result.metadata.add(parallel_nested_, "omp_nested"); + } else { + parallel_nested_ = false; + #ifdef _WIN32 + omp_set_nested(0); + #else + omp_set_max_active_levels(1); + #endif + } +#endif + uint_t offset = 0; +#ifdef AER_MPI + offset = distributed_experiments_begin_; #endif // then- and else-blocks have intentionally duplication. // Nested omp has significant overheads even though a guard condition exists. + const int NUM_RESULTS = result.results.size(); if (parallel_experiments_ > 1) { #pragma omp parallel for num_threads(parallel_experiments_) for (int j = 0; j < result.results.size(); ++j) { - // Make a copy of the noise model for each circuit execution - // so that it can be modified if required - auto circ_noise_model = noise_model; - execute_circuit(circuits[j], circ_noise_model, config, result.results[j]); + execute_circuit(circuits[j + offset], circ_noise_models[j + offset], config, result.results[j]); } } else { for (int j = 0; j < result.results.size(); ++j) { - // Make a copy of the noise model for each circuit execution - // so that it can be modified if required - auto circ_noise_model = noise_model; - execute_circuit(circuits[j], circ_noise_model, config, result.results[j]); + execute_circuit(circuits[j + offset], circ_noise_models[j + offset], config, result.results[j]); } } @@ -544,7 +883,7 @@ Result Controller::execute(std::vector &circuits, bool all_failed = true; result.status = Result::Status::completed; - for (size_t i = 0; i < result.results.size(); ++i) { + for (int i = 0; i < NUM_RESULTS; ++i) { auto& experiment = result.results[i]; if (experiment.status == ExperimentResult::Status::completed) { all_failed = false; @@ -560,8 +899,8 @@ Result Controller::execute(std::vector &circuits, // Stop the timer and add total timing data auto timer_stop = myclock_t::now(); - result.metadata["time_taken"] = - std::chrono::duration(timer_stop - timer_start).count(); + auto time_taken = std::chrono::duration(timer_stop - timer_start).count(); + result.metadata.add(time_taken, "time_taken"); } // If execution failed return valid output reporting error catch (std::exception &e) { @@ -574,27 +913,27 @@ Result Controller::execute(std::vector &circuits, void Controller::execute_circuit(Circuit &circ, Noise::NoiseModel &noise, const json_t &config, - ExperimentResult &exp_result) { - + ExperimentResult &result) +{ // Start individual circuit timer auto timer_start = myclock_t::now(); // state circuit timer // Initialize circuit json return - exp_result.data.set_config(config); + result.legacy_data.set_config(config); // Execute in try block so we can catch errors and return the error message // for individual circuit failures. try { // Remove barriers from circuit Transpile::ReduceBarrier barrier_pass; - barrier_pass.optimize_circuit(circ, noise, circ.opset(), exp_result.data); + barrier_pass.optimize_circuit(circ, noise, circ.opset(), result); // Truncate unused qubits from circuit and noise model if (truncate_qubits_) { Transpile::TruncateQubits truncate_pass; truncate_pass.set_config(config); truncate_pass.optimize_circuit(circ, noise, circ.opset(), - exp_result.data); + result); } // set parallelization for this circuit @@ -602,29 +941,57 @@ void Controller::execute_circuit(Circuit &circ, set_parallelization_circuit(circ, noise); } + int shots = circ.shots; +#ifdef AER_MPI + if(parallel_shots_ > 1 && distributed_shots_ > 1){ //if shots can be distributed + shots = (circ.shots * (distributed_shots_rank_ + 1)/distributed_shots_) - (circ.shots * distributed_shots_rank_ /distributed_shots_); + } +#endif + // Single shot thread execution if (parallel_shots_ <= 1) { - run_circuit(circ, noise, config, circ.shots, circ.seed, exp_result.data); + run_circuit(circ, noise, config, shots, circ.seed, result); // Parallel shot thread execution } else { // Calculate shots per thread std::vector subshots; for (int j = 0; j < parallel_shots_; ++j) { - subshots.push_back(circ.shots / parallel_shots_); + subshots.push_back(shots / parallel_shots_); } // If shots is not perfectly divisible by threads, assign the remainder - for (int j = 0; j < int(circ.shots % parallel_shots_); ++j) { + for (int j = 0; j < int(shots % parallel_shots_); ++j) { subshots[j] += 1; } // Vector to store parallel thread output data - std::vector par_data(parallel_shots_); + std::vector par_results(parallel_shots_); std::vector error_msgs(parallel_shots_); + + #ifdef _OPENMP + if (!parallel_nested_) { + if (parallel_shots_ > 1 && parallel_state_update_ > 1) { + // Nested parallel shots + state update + #ifdef _WIN32 + omp_set_nested(1); + #else + omp_set_max_active_levels(2); + #endif + result.metadata.add(true, "omp_nested"); + } else { + #ifdef _WIN32 + omp_set_nested(0); + #else + omp_set_max_active_levels(1); + #endif + } + } + #endif + #pragma omp parallel for if (parallel_shots_ > 1) num_threads(parallel_shots_) for (int i = 0; i < parallel_shots_; i++) { try { run_circuit(circ, noise, config, subshots[i], circ.seed + i, - par_data[i]); + par_results[i]); } catch (std::runtime_error &error) { error_msgs[i] = error.what(); } @@ -636,36 +1003,46 @@ void Controller::execute_circuit(Circuit &circ, // Accumulate results across shots // Use move semantics to avoid copying data - for (auto &datum : par_data) { - exp_result.data.combine(std::move(datum)); + for (auto &res : par_results) { + result.combine(std::move(res)); } } // Report success - exp_result.status = ExperimentResult::Status::completed; + result.status = ExperimentResult::Status::completed; // Pass through circuit header and add metadata - exp_result.header = circ.header; - exp_result.shots = circ.shots; - exp_result.seed = circ.seed; - // Move any metadata from the subclass run_circuit data - // to the experiment result metadata field - for (const auto &pair : exp_result.data.metadata()) { - exp_result.add_metadata(pair.first, pair.second); + result.header = circ.header; + result.shots = shots; + result.seed = circ.seed; + result.metadata.add(parallel_shots_, "parallel_shots"); + result.metadata.add(parallel_state_update_, "parallel_state_update"); +#ifdef AER_MPI + if(parallel_shots_ > 1 && distributed_shots_ > 1){ + result.metadata.add(distributed_shots_,"distributed_shots"); } - // Remove the metatdata field from data - exp_result.data.metadata().clear(); - exp_result.metadata["parallel_shots"] = parallel_shots_; - exp_result.metadata["parallel_state_update"] = parallel_state_update_; +#endif // Add timer data auto timer_stop = myclock_t::now(); // stop timer double time_taken = std::chrono::duration(timer_stop - timer_start).count(); - exp_result.time_taken = time_taken; + result.time_taken = time_taken; } // If an exception occurs during execution, catch it and pass it to the output catch (std::exception &e) { - exp_result.status = ExperimentResult::Status::error; - exp_result.message = e.what(); + result.status = ExperimentResult::Status::error; + result.message = e.what(); + } +} + + +void Controller::save_count_data(ExperimentResult &result, + const ClassicalRegister &creg) const { + if (creg.memory_size() > 0) { + std::string memory_hex = creg.memory_hex(); + result.data.add_accum(static_cast(1ULL), "counts", memory_hex); + if (save_creg_memory_) { + result.data.add_list(std::move(memory_hex), "memory"); + } } } diff --git a/src/controllers/qasm_controller.hpp b/src/controllers/qasm_controller.hpp index 2ff6ff719e..a408b2e83d 100755 --- a/src/controllers/qasm_controller.hpp +++ b/src/controllers/qasm_controller.hpp @@ -16,17 +16,18 @@ #define _aer_qasm_controller_hpp_ #include "controller.hpp" -#include "framework/avx2_detect.hpp" #include "simulators/density_matrix/densitymatrix_state.hpp" +#include "simulators/density_matrix/densitymatrix_state_chunk.hpp" #include "simulators/extended_stabilizer/extended_stabilizer_state.hpp" #include "simulators/matrix_product_state/matrix_product_state.hpp" #include "simulators/stabilizer/stabilizer_state.hpp" #include "simulators/statevector/qubitvector.hpp" -#include "simulators/statevector/qubitvector_avx2.hpp" #include "simulators/statevector/statevector_state.hpp" +#include "simulators/statevector/statevector_state_chunk.hpp" #include "simulators/superoperator/superoperator_state.hpp" #include "transpile/delay_measure.hpp" #include "transpile/fusion.hpp" +#include "transpile/cacheblocking.hpp" namespace AER { namespace Simulator { @@ -50,7 +51,7 @@ namespace Simulator { * zero in result data [Default: 1e-10] * - "statevector_parallel_threshold" (int): Threshold that number of qubits * must be greater than to enable OpenMP parallelization at State - * level [Default: 13] + * level [Default: 14] * - "statevector_sample_measure_opt" (int): Threshold that number of qubits * must be greater than to enable indexing optimization during * measure sampling [Default: 10] @@ -119,7 +120,7 @@ namespace Simulator { * - fusion_max_qubit (int): Maximum number of qubits for a operation generated * in a fusion optimization [Default: 5] * - fusion_threshold (int): Threshold that number of qubits must be greater - * than or equal to enable fusion optimization [Default: 20] + * than or equal to enable fusion optimization [Default: 14] * **************************************************************************/ @@ -166,9 +167,6 @@ class QasmController : public Base::Controller { // Simulation precision enum class Precision { double_precision, single_precision }; - // Fusion method - using FusionMethod = Transpile::Fusion::Method; - //----------------------------------------------------------------------- // Base class abstract method override //----------------------------------------------------------------------- @@ -181,7 +179,7 @@ class QasmController : public Base::Controller { const json_t& config, uint_t shots, uint_t rng_seed, - ExperimentData& data) const override; + ExperimentResult& result) const override; //---------------------------------------------------------------- // Utility functions @@ -206,11 +204,16 @@ class QasmController : public Base::Controller { const Circuit& circ, const Noise::NoiseModel& noise) override; + // Set distributed parallelization + virtual void + set_distributed_parallelization(const std::vector &circuits, + const std::vector &noise) override; + // Return a fusion transpilation pass configured for the current // method, circuit and config Transpile::Fusion transpile_fusion(Method method, - const json_t& config, - FusionMethod fusion_method = FusionMethod::unitary) const; + const Operations::OpSet &opset, + const json_t& config) const; //---------------------------------------------------------------- // Run circuit helpers @@ -225,7 +228,7 @@ class QasmController : public Base::Controller { uint_t rng_seed, const Initstate_t& initial_state, const Method method, - ExperimentData& data) const; + ExperimentResult& result) const; // Execute a single shot a of circuit by initializing the state vector // to initial_state, running all ops in circ, and updating data with @@ -234,7 +237,7 @@ class QasmController : public Base::Controller { void run_single_shot(const Circuit& circ, State_t& state, const Initstate_t& initial_state, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const; // Execute multiple shots a of circuit by initializing the state vector @@ -246,7 +249,7 @@ class QasmController : public Base::Controller { State_t& state, const Initstate_t& initial_state, const Method method, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const; template @@ -257,7 +260,7 @@ class QasmController : public Base::Controller { State_t& state, const Initstate_t& initial_state, const Method method, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const; //---------------------------------------------------------------- @@ -270,7 +273,7 @@ class QasmController : public Base::Controller { void measure_sampler(const std::vector& meas_ops, uint_t shots, State_t& state, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const; // Check if measure sampling optimization is valid for the input circuit @@ -349,10 +352,6 @@ void QasmController::set_config(const json_t& config) { } } - // Check for extended stabilizer measure sampling - JSON::get_value(extended_stabilizer_measure_sampling_, - "extended_stabilizer_measure_sampling", config); - // DEPRECATED: Add custom initial state if (JSON::get_value(initial_statevector_, "initial_statevector", config)) { // Raise error if method is set to stabilizer or ch @@ -391,35 +390,35 @@ void QasmController::run_circuit(const Circuit& circ, const json_t& config, uint_t shots, uint_t rng_seed, - ExperimentData& data) const { + ExperimentResult& result) const { // Validate circuit for simulation method switch (simulation_method(circ, noise, true)) { case Method::statevector: { - bool avx2_enabled = is_avx2_supported(); - - if (simulation_precision_ == Precision::double_precision) { - if (avx2_enabled) { - return run_circuit_helper< - Statevector::State>>( + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper>>( circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector, data); + Method::statevector, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector, result); } - // Double-precision Statevector simulation - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector, data); - } else { - if (avx2_enabled) { + } + else{ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector, result); + } else { // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( + return run_circuit_helper>>( circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector, data); + Method::statevector, result); } - // Single-precision Statevector simulation - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector, data); } } case Method::statevector_thrust_gpu: { @@ -428,18 +427,35 @@ void QasmController::run_circuit(const Circuit& circ, "QasmController: method statevector_gpu is not supported on this " "system"); #else - if (simulation_precision_ == Precision::double_precision) { - // Double-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector_thrust_gpu, data); - } else { - // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector_thrust_gpu, data); + if(Base::Controller::multiple_chunk_required(circ,noise) || (parallel_shots_ > 1 || parallel_experiments_ > 1)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_gpu,result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_gpu,result); + } + } + else{ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_gpu,result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_gpu,result); + } } #endif } @@ -449,34 +465,68 @@ void QasmController::run_circuit(const Circuit& circ, "QasmController: method statevector_thrust is not supported on this " "system"); #else - if (simulation_precision_ == Precision::double_precision) { - // Double-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector_thrust_cpu, data); - } else { - // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, initial_statevector_, - Method::statevector_thrust_cpu, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_cpu,result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_cpu,result); + } + } + else{ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_cpu,result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, initial_statevector_, + Method::statevector_thrust_cpu,result); + } } #endif } case Method::density_matrix: { - if (simulation_precision_ == Precision::double_precision) { - // Double-precision density matrix simulation - return run_circuit_helper< - DensityMatrix::State>>( - circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix, data); - } else { - // Single-precision density matrix simulation - return run_circuit_helper< - DensityMatrix::State>>( - circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix, result); + } else { + // Single-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix, result); + } + } + else{ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision density matrix simulation + return run_circuit_helper< + DensityMatrix::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix, result); + } else { + // Single-precision density matrix simulation + return run_circuit_helper< + DensityMatrix::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix, result); + } } } case Method::density_matrix_thrust_gpu: { @@ -485,18 +535,35 @@ void QasmController::run_circuit(const Circuit& circ, "QasmController: method density_matrix_gpu is not supported on this " "system"); #else - if (simulation_precision_ == Precision::double_precision) { - // Double-precision density matrix simulation - return run_circuit_helper< - DensityMatrix::State>>( - circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix_thrust_gpu, data); - } else { - // Single-precision density matrix simulation - return run_circuit_helper< - DensityMatrix::State>>( - circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix_thrust_gpu, data); + if(Base::Controller::multiple_chunk_required(circ,noise) || (parallel_shots_ > 1 || parallel_experiments_ > 1)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_gpu,result); + } else { + // Single-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_gpu,result); + } + } + else{ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision density matrix simulation + return run_circuit_helper< + DensityMatrix::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_gpu,result); + } else { + // Single-precision density matrix simulation + return run_circuit_helper< + DensityMatrix::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_gpu,result); + } } #endif case Method::density_matrix_thrust_cpu: @@ -506,19 +573,36 @@ void QasmController::run_circuit(const Circuit& circ, "this " "system"); #else + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (simulation_precision_ == Precision::double_precision) { + // Double-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_cpu,result); + } else { + // Single-precision density matrix simulation + return run_circuit_helper< + DensityMatrixChunk::State>>( + circ, noise, config, shots, rng_seed, cvector_t(), + Method::density_matrix_thrust_cpu,result); + } + } + else{ if (simulation_precision_ == Precision::double_precision) { // Double-precision density matrix simulation return run_circuit_helper< DensityMatrix::State>>( circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix_thrust_cpu, data); + Method::density_matrix_thrust_cpu, result); } else { // Single-precision density matrix simulation return run_circuit_helper< DensityMatrix::State>>( circ, noise, config, shots, rng_seed, cvector_t(), - Method::density_matrix_thrust_cpu, data); + Method::density_matrix_thrust_cpu, result); } + } #endif } case Method::stabilizer: { @@ -526,16 +610,16 @@ void QasmController::run_circuit(const Circuit& circ, // TODO: Stabilizer doesn't yet support custom state initialization return run_circuit_helper( circ, noise, config, shots, rng_seed, Clifford::Clifford(), - Method::stabilizer, data); + Method::stabilizer, result); case Method::extended_stabilizer: return run_circuit_helper( circ, noise, config, shots, rng_seed, CHSimulator::Runner(), - Method::extended_stabilizer, data); + Method::extended_stabilizer, result); case Method::matrix_product_state: return run_circuit_helper( circ, noise, config, shots, rng_seed, MatrixProductState::MPS(), - Method::matrix_product_state, data); + Method::matrix_product_state, result); default: throw std::runtime_error("QasmController:Invalid simulation method"); @@ -798,29 +882,46 @@ size_t QasmController::required_memory_mb( } Transpile::Fusion QasmController::transpile_fusion(Method method, - const json_t& config, - FusionMethod fusion_method) const { + const Operations::OpSet &opset, + const json_t& config) const { Transpile::Fusion fusion_pass; + fusion_pass.set_parallelization(parallel_state_update_); + + if (opset.contains(Operations::OpType::superop)) { + fusion_pass.allow_superop = true; + } + if (opset.contains(Operations::OpType::kraus)) { + fusion_pass.allow_kraus = true; + } switch (method) { - case Method::statevector: - case Method::statevector_thrust_gpu: - case Method::statevector_thrust_cpu: case Method::density_matrix: case Method::density_matrix_thrust_gpu: case Method::density_matrix_thrust_cpu: { - if (fusion_method == FusionMethod::superop) { - fusion_pass.allow_superop = true; - } else if (fusion_method == FusionMethod::kraus) { - fusion_pass.allow_kraus = true; + // Halve the default threshold and max fused qubits for density matrix + fusion_pass.threshold /= 2; + fusion_pass.max_qubit /= 2; + break; + } + case Method::matrix_product_state: { + // Disable fusion by default, but allow it to be enabled by config settings + fusion_pass.active = false; + } + case Method::statevector: + case Method::statevector_thrust_gpu: + case Method::statevector_thrust_cpu: { + if (fusion_pass.allow_kraus) { + // Halve default max fused qubits for Kraus noise fusion + fusion_pass.max_qubit /= 2; } - fusion_pass.set_config(config); break; } default: { fusion_pass.active = false; - break; + return fusion_pass; } } + // Override default fusion settings with custom config + fusion_pass.set_config(config); return fusion_pass; } @@ -864,6 +965,59 @@ void QasmController::set_parallelization_circuit( } } + +void QasmController::set_distributed_parallelization(const std::vector &circuits, + const std::vector &noise) +{ +#ifdef AER_MPI + uint_t i,ncircuits; + bool sample_opt = true; + + ncircuits = circuits.size(); + for(i=0;i 1 && + (noise[i].has_quantum_errors() || + !check_measure_sampling_opt(circuits[i], Method::statevector))) { + sample_opt = false; + } + break; + } + case Method::density_matrix: + case Method::density_matrix_thrust_gpu: + case Method::density_matrix_thrust_cpu: { + if (circuits[i].shots > 1 && + !check_measure_sampling_opt(circuits[i], Method::density_matrix)) { + sample_opt = false; + } + break; + } + default: { + sample_opt = false; + } + } + if(!sample_opt){ + break; + } + } + + + if(sample_opt){ + Base::Controller::set_distributed_parallelization(circuits, noise); + + //shots are not distributed + Base::Controller::distributed_shots_ = 1; + Base::Controller::distributed_shots_rank_ = 0; + } +#endif +} + //------------------------------------------------------------------------- // Run circuit helpers //------------------------------------------------------------------------- @@ -876,7 +1030,8 @@ void QasmController::run_circuit_helper(const Circuit& circ, uint_t rng_seed, const Initstate_t& initial_state, const Method method, - ExperimentData& data) const { + ExperimentResult& result) const +{ // Initialize new state object State_t state; @@ -886,6 +1041,7 @@ void QasmController::run_circuit_helper(const Circuit& circ, // Set state config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_distribution(Base::Controller::get_distributed_num_processes(shots == circ.shots)); state.set_global_phase(circ.global_phase_angle); // Rng engine @@ -893,18 +1049,15 @@ void QasmController::run_circuit_helper(const Circuit& circ, rng.set_seed(rng_seed); // Output data container - data.set_config(config); - data.add_metadata("method", state.name()); - state.add_metadata(data); + result.set_config(config); + result.metadata.add(state.name(), "method"); + state.add_metadata(result); // Add measure sampling to metadata // Note: this will set to `true` if sampling is enabled for the circuit - data.add_metadata("measure_sampling", false); - + result.metadata.add(false, "measure_sampling"); // Choose execution method based on noise and method - Circuit opt_circ; - FusionMethod fusion_method = FusionMethod::unitary; // Ideal circuit if (noise.is_ideal()) { @@ -921,7 +1074,6 @@ void QasmController::run_circuit_helper(const Circuit& circ, // Sample noise using SuperOp method auto noise_superop = noise; noise_superop.activate_superop_method(); - fusion_method = FusionMethod::superop; opt_circ = noise_superop.sample_noise(circ, rng); } // Kraus noise sampling @@ -929,13 +1081,12 @@ void QasmController::run_circuit_helper(const Circuit& circ, noise.opset().contains(Operations::OpType::superop)) { auto noise_kraus = noise; noise_kraus.activate_kraus_method(); - fusion_method = FusionMethod::kraus; opt_circ = noise_kraus.sample_noise(circ, rng); } // General circuit noise sampling else { run_circuit_with_sampled_noise(circ, noise, config, shots, state, - initial_state, method, data, rng); + initial_state, method, result, rng); return; } @@ -943,24 +1094,37 @@ void QasmController::run_circuit_helper(const Circuit& circ, Noise::NoiseModel dummy_noise; Transpile::DelayMeasure measure_pass; measure_pass.set_config(config); - measure_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), data); + measure_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); + + auto fusion_pass = transpile_fusion(method, opt_circ.opset(), config); + fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); + + bool is_matrix = false; + if(method == Method::density_matrix || method == Method::density_matrix_thrust_gpu || method == Method::density_matrix_thrust_cpu) + is_matrix = true; + auto cache_block_pass = transpile_cache_blocking(opt_circ,noise,config,(simulation_precision_ == Precision::single_precision) ? sizeof(std::complex) : sizeof(std::complex),is_matrix); + cache_block_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); + + uint_t block_bits = 0; + if(cache_block_pass.enabled()) + block_bits = cache_block_pass.block_bits(); - auto fusion_pass = transpile_fusion(method, config, fusion_method); - fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), data); + //allocate qubit register + state.allocate(Base::Controller::max_qubits_,block_bits); // Run simulation - run_multi_shot(opt_circ, shots, state, initial_state, method, data, rng); + run_multi_shot(opt_circ, shots, state, initial_state, method, result, rng); } template void QasmController::run_single_shot(const Circuit& circ, State_t& state, const Initstate_t& initial_state, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const { initialize_state(circ, state, initial_state); - state.apply_ops(circ.ops, data, rng); - state.add_creg_to_data(data); + state.apply_ops(circ.ops, result, rng, true); + Base::Controller::save_count_data(result, state.creg()); } template @@ -969,7 +1133,7 @@ void QasmController::run_multi_shot(const Circuit& circ, State_t& state, const Initstate_t& initial_state, const Method method, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const { // Check if measure sampler and optimization are valid if (check_measure_sampling_opt(circ, method)) { @@ -979,21 +1143,22 @@ void QasmController::run_multi_shot(const Circuit& circ, // Run circuit instructions before first measure std::vector ops(circ.ops.begin(), circ.ops.begin() + pos); + bool final_ops = (pos == circ.ops.size()); initialize_state(circ, state, initial_state); - state.apply_ops(ops, data, rng); + state.apply_ops(ops, result, rng, final_ops); // Get measurement operations and set of measured qubits ops = std::vector(circ.ops.begin() + pos, circ.ops.end()); - measure_sampler(ops, shots, state, data, rng); + measure_sampler(ops, shots, state, result, rng); // Add measure sampling metadata - data.add_metadata("measure_sampling", true); + result.metadata.add(true, "measure_sampling"); } else { // Perform standard execution if we cannot apply the // measurement sampling optimization while (shots-- > 0) { - run_single_shot(circ, state, initial_state, data, rng); + run_single_shot(circ, state, initial_state, result, rng); } } } @@ -1007,22 +1172,35 @@ void QasmController::run_circuit_with_sampled_noise(const Circuit& circ, State_t& state, const Initstate_t& initial_state, const Method method, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const { - // Transpilation for circuit noise method - auto fusion_pass = transpile_fusion(method, config, FusionMethod::unitary); + auto fusion_pass = transpile_fusion(method, circ.opset(), config); Transpile::DelayMeasure measure_pass; measure_pass.set_config(config); Noise::NoiseModel dummy_noise; + bool is_matrix = false; + if(method == Method::density_matrix || method == Method::density_matrix_thrust_gpu || method == Method::density_matrix_thrust_cpu) + is_matrix = true; + auto cache_block_pass = transpile_cache_blocking(circ,noise,config,(simulation_precision_ == Precision::single_precision) ? sizeof(std::complex) : sizeof(std::complex),is_matrix); + // Sample noise using circuit method while (shots-- > 0) { Circuit noise_circ = noise.sample_noise(circ, rng); noise_circ.shots = 1; - measure_pass.optimize_circuit(noise_circ, dummy_noise, state.opset(), data); - fusion_pass.optimize_circuit(noise_circ, dummy_noise, state.opset(), data); - run_single_shot(noise_circ, state, initial_state, data, rng); + measure_pass.optimize_circuit(noise_circ, dummy_noise, state.opset(), result); + fusion_pass.optimize_circuit(noise_circ, dummy_noise, state.opset(), result); + cache_block_pass.optimize_circuit(noise_circ, dummy_noise, state.opset(), result); + + uint_t block_bits = 0; + if(cache_block_pass.enabled()) + block_bits = cache_block_pass.block_bits(); + + //allocate qubit register + state.allocate(Base::Controller::max_qubits_,block_bits); + + run_single_shot(noise_circ, state, initial_state, result, rng); } } @@ -1037,12 +1215,6 @@ bool QasmController::check_measure_sampling_opt(const Circuit& circ, return false; } - // Check if stabilizer measure sampling has been disabled - if (method == Method::extended_stabilizer && - !extended_stabilizer_measure_sampling_) { - return false; - } - // Check if non-density matrix simulation and circuit contains // a stochastic instruction before measurement // ie. initialize, reset, kraus, superop, conditional @@ -1068,12 +1240,12 @@ void QasmController::measure_sampler( const std::vector& meas_roerror_ops, uint_t shots, State_t& state, - ExperimentData& data, + ExperimentResult& result, RngEngine& rng) const { // Check if meas_circ is empty, and if so return initial creg if (meas_roerror_ops.empty()) { while (shots-- > 0) { - state.add_creg_to_data(data); + Base::Controller::save_count_data(result, state.creg()); } return; } @@ -1142,11 +1314,8 @@ void QasmController::measure_sampler( creg.apply_roerror(roerror, rng); } - auto memory = creg.memory_hex(); - data.add_memory_count(memory); - data.add_pershot_memory(memory); - - data.add_pershot_register(creg.register_hex()); + // Save count data + Base::Controller::save_count_data(result, creg); // pop off processed sample all_samples.pop_back(); diff --git a/src/controllers/statevector_controller.hpp b/src/controllers/statevector_controller.hpp index 1632f4e67d..db5c9a9cfe 100755 --- a/src/controllers/statevector_controller.hpp +++ b/src/controllers/statevector_controller.hpp @@ -17,8 +17,9 @@ #include "controller.hpp" #include "simulators/statevector/statevector_state.hpp" -#include "simulators/statevector/qubitvector_avx2.hpp" +#include "simulators/statevector/statevector_state_chunk.hpp" #include "transpile/fusion.hpp" +#include "transpile/cacheblocking.hpp" namespace AER { namespace Simulator { @@ -38,7 +39,7 @@ namespace Simulator { * zero in result data [Default: 1e-10] * - "statevector_parallel_threshold" (int): Threshold that number of qubits * must be greater than to enable OpenMP parallelization at State - * level [Default: 13] + * level [Default: 14] * - "statevector_sample_measure_opt" (int): Threshold that number of qubits * must be greater than to enable indexing optimization during * measure sampling [Default: 10] @@ -103,7 +104,7 @@ class StatevectorController : public Base::Controller { const Noise::NoiseModel& noise, const json_t& config, uint_t shots, uint_t rng_seed, - ExperimentData &data) const override; + ExperimentResult &result) const override; // Execute n-shots of a circuit on the input state template @@ -111,7 +112,7 @@ class StatevectorController : public Base::Controller { const Noise::NoiseModel& noise, const json_t& config, uint_t shots, uint_t rng_seed, - ExperimentData &data) const; + ExperimentResult &result) const; //----------------------------------------------------------------------- // Custom initial state //----------------------------------------------------------------------- @@ -122,6 +123,7 @@ class StatevectorController : public Base::Controller { // Precision of statevector Precision precision_ = Precision::double_precision; + }; //========================================================================= @@ -201,41 +203,60 @@ size_t StatevectorController::required_memory_mb( void StatevectorController::run_circuit( const Circuit& circ, const Noise::NoiseModel& noise, const json_t& config, - uint_t shots, uint_t rng_seed, ExperimentData &data) const { + uint_t shots, uint_t rng_seed, ExperimentResult &result) const { switch (method_) { case Method::automatic: case Method::statevector_cpu: { - bool avx2_enabled = is_avx2_supported(); - if (precision_ == Precision::double_precision) { - if(avx2_enabled){ - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, result); } - // Double-precision Statevector simulation - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, data); - } else { - // Single-precision Statevector simulation - if(avx2_enabled){ - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, data); + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper>>( + circ, noise, config, shots, rng_seed, result); } - return run_circuit_helper>>( - circ, noise, config, shots, rng_seed, data); } } case Method::statevector_thrust_gpu: { #ifdef AER_THRUST_CUDA - if (precision_ == Precision::double_precision) { - // Double-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, data); - } else { - // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, result); + } } #else throw std::runtime_error( @@ -246,16 +267,31 @@ void StatevectorController::run_circuit( } case Method::statevector_thrust_cpu: { #ifdef AER_THRUST_CPU - if (precision_ == Precision::double_precision) { - // Double-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, data); - } else { - // Single-precision Statevector simulation - return run_circuit_helper< - Statevector::State>>( - circ, noise, config, shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + StatevectorChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision Statevector simulation + return run_circuit_helper< + Statevector::State>>( + circ, noise, config, shots, rng_seed, result); + } } #else throw std::runtime_error( @@ -273,13 +309,17 @@ void StatevectorController::run_circuit( template void StatevectorController::run_circuit_helper( const Circuit& circ, const Noise::NoiseModel& noise, const json_t& config, - uint_t shots, uint_t rng_seed, ExperimentData &data) const { + uint_t shots, uint_t rng_seed, ExperimentResult &result) const +{ // Initialize state State_t state; // Validate circuit and throw exception if invalid operations exist validate_state(state, circ, noise, true); + // Validate memory requirements and throw exception if not enough memory + validate_memory_requirements(state, circ, true); + // Check for custom initial state, and if so check it matches num qubits if (!initial_state_.empty()) { if (initial_state_.size() != 1ULL << circ.num_qubits) { @@ -294,6 +334,7 @@ void StatevectorController::run_circuit_helper( // Set config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_distribution(Base::Controller::num_process_per_experiment_); state.set_global_phase(circ.global_phase_angle); // Rng engine @@ -301,20 +342,27 @@ void StatevectorController::run_circuit_helper( rng.set_seed(rng_seed); // Output data container - data.set_config(config); + result.set_config(config); // Optimize circuit - const std::vector* op_ptr = &circ.ops; - Transpile::Fusion fusion_pass(5, 20); // 20-qubit default threshold + Transpile::Fusion fusion_pass; fusion_pass.set_config(config); - Circuit opt_circ; + fusion_pass.set_parallelization(parallel_state_update_); + + Circuit opt_circ = circ; // copy circuit + Noise::NoiseModel dummy_noise; // dummy object for transpile pass if (fusion_pass.active && circ.num_qubits >= fusion_pass.threshold) { - opt_circ = circ; // copy circuit - Noise::NoiseModel dummy_noise; // dummy object for transpile pass - fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), data); - op_ptr = &opt_circ.ops; + fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); } + Transpile::CacheBlocking cache_block_pass = transpile_cache_blocking(opt_circ,dummy_noise,config,(precision_ == Precision::single_precision) ? sizeof(std::complex) : sizeof(std::complex),false); + cache_block_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); + + uint_t block_bits = 0; + if(cache_block_pass.enabled()) + block_bits = cache_block_pass.block_bits(); + state.allocate(Base::Controller::max_qubits_,block_bits); + // Run single shot collecting measure data or snapshots if (initial_state_.empty()) { state.initialize_qreg(circ.num_qubits); @@ -322,11 +370,11 @@ void StatevectorController::run_circuit_helper( state.initialize_qreg(circ.num_qubits, initial_state_); } state.initialize_creg(circ.num_memory, circ.num_registers); - state.apply_ops(*op_ptr, data, rng); - state.add_creg_to_data(data); + state.apply_ops(opt_circ.ops, result, rng); + Base::Controller::save_count_data(result, state.creg()); // Add final state to the data - data.add_additional_data("statevector", state.qreg().move_to_vector()); + state.save_data_single(result, "statevector", state.move_to_vector()); } //------------------------------------------------------------------------- diff --git a/src/controllers/unitary_controller.hpp b/src/controllers/unitary_controller.hpp index 74f143a71e..f54f52d5b2 100755 --- a/src/controllers/unitary_controller.hpp +++ b/src/controllers/unitary_controller.hpp @@ -17,7 +17,9 @@ #include "controller.hpp" #include "simulators/unitary/unitary_state.hpp" +#include "simulators/unitary/unitary_state_chunk.hpp" #include "transpile/fusion.hpp" +#include "transpile/cacheblocking.hpp" namespace AER { namespace Simulator { @@ -93,13 +95,13 @@ class UnitaryController : public Base::Controller { virtual void run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, uint_t shots, - uint_t rng_seed, ExperimentData &data) const override; + uint_t rng_seed, ExperimentResult &result) const override; template void run_circuit_helper(const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, uint_t shots, - uint_t rng_seed, ExperimentData &data) const; + uint_t rng_seed, ExperimentResult &result) const; //----------------------------------------------------------------------- // Custom initial state @@ -192,34 +194,64 @@ void UnitaryController::run_circuit(const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, uint_t shots, - uint_t rng_seed, ExperimentData &data) const { + uint_t rng_seed, ExperimentResult &result) const { switch (method_) { case Method::automatic: case Method::unitary_cpu: { - if (precision_ == Precision::double_precision) { - // Double-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>(circ, noise, config, - shots, rng_seed, data); - } else { - // Single-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>(circ, noise, config, - shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>(circ, noise, config, + shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>(circ, noise, config, + shots, rng_seed, result); + } + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>(circ, noise, config, + shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>(circ, noise, config, + shots, rng_seed, result); + } } } case Method::unitary_thrust_gpu: { #ifdef AER_THRUST_CUDA - if (precision_ == Precision::double_precision) { - // Double-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>( - circ, noise, config, shots, rng_seed, data); - } else { - // Single-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>( - circ, noise, config, shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>( + circ, noise, config, shots, rng_seed, result); + } } #else throw std::runtime_error( @@ -229,16 +261,31 @@ void UnitaryController::run_circuit(const Circuit &circ, } case Method::unitary_thrust_cpu: { #ifdef AER_THRUST_CPU - if (precision_ == Precision::double_precision) { - // Double-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>( - circ, noise, config, shots, rng_seed, data); - } else { - // Single-precision unitary simulation - return run_circuit_helper< - QubitUnitary::State>>( - circ, noise, config, shots, rng_seed, data); + if(Base::Controller::multiple_chunk_required(circ,noise)){ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitaryChunk::State>>( + circ, noise, config, shots, rng_seed, result); + } + } + else{ + if (precision_ == Precision::double_precision) { + // Double-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>( + circ, noise, config, shots, rng_seed, result); + } else { + // Single-precision unitary simulation + return run_circuit_helper< + QubitUnitary::State>>( + circ, noise, config, shots, rng_seed, result); + } } #else throw std::runtime_error( @@ -254,13 +301,17 @@ void UnitaryController::run_circuit(const Circuit &circ, template void UnitaryController::run_circuit_helper( const Circuit &circ, const Noise::NoiseModel &noise, const json_t &config, - uint_t shots, uint_t rng_seed, ExperimentData &data) const { + uint_t shots, uint_t rng_seed, ExperimentResult &result) const +{ // Initialize state State_t state; // Validate circuit and throw exception if invalid operations exist validate_state(state, circ, noise, true); + // Validate memory requirements and throw exception if not enough memory + validate_memory_requirements(state, circ, true); + // Check for custom initial state, and if so check it matches num qubits if (!initial_unitary_.empty()) { auto nrows = initial_unitary_.GetRows(); @@ -282,6 +333,7 @@ void UnitaryController::run_circuit_helper( // Set state config state.set_config(config); state.set_parallalization(parallel_state_update_); + state.set_distribution(Base::Controller::num_process_per_experiment_); state.set_global_phase(circ.global_phase_angle); // Rng engine (not actually needed for unitary controller) @@ -289,33 +341,42 @@ void UnitaryController::run_circuit_helper( rng.set_seed(rng_seed); // Output data container - data.set_config(config); - data.add_metadata("method", state.name()); + result.set_config(config); + result.metadata.add(state.name(), "method"); // Optimize circuit - const std::vector* op_ptr = &circ.ops; - Transpile::Fusion fusion_pass(5, 10); // 10-qubit default threshold + Transpile::Fusion fusion_pass; + fusion_pass.threshold /= 2; // Halve default threshold for unitary simulator fusion_pass.set_config(config); - Circuit opt_circ; + fusion_pass.set_parallelization(parallel_state_update_); + + Circuit opt_circ = circ; // copy circuit + Noise::NoiseModel dummy_noise; // dummy object for transpile pass if (fusion_pass.active && circ.num_qubits >= fusion_pass.threshold) { - opt_circ = circ; // copy circuit - Noise::NoiseModel dummy_noise; // dummy object for transpile pass - fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), data); - op_ptr = &opt_circ.ops; + fusion_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); } + Transpile::CacheBlocking cache_block_pass = transpile_cache_blocking(opt_circ,dummy_noise,config,(precision_ == Precision::single_precision) ? sizeof(std::complex) : sizeof(std::complex),true); + cache_block_pass.optimize_circuit(opt_circ, dummy_noise, state.opset(), result); + + uint_t block_bits = 0; + if(cache_block_pass.enabled()) + block_bits = cache_block_pass.block_bits(); + state.allocate(Base::Controller::max_qubits_,block_bits); + // Run single shot collecting measure data or snapshots + if (initial_unitary_.empty()) { state.initialize_qreg(circ.num_qubits); } else { state.initialize_qreg(circ.num_qubits, initial_unitary_); } state.initialize_creg(circ.num_memory, circ.num_registers); - state.apply_ops(*op_ptr, data, rng); - state.add_creg_to_data(data); + state.apply_ops(opt_circ.ops, result, rng); + Base::Controller::save_count_data(result, state.creg()); // Add final state unitary to the data - data.add_additional_data("unitary", state.qreg().move_to_matrix()); + state.save_data_single(result, "unitary", state.move_to_matrix()); } //------------------------------------------------------------------------- diff --git a/src/framework/avx2_detect.hpp b/src/framework/avx2_detect.hpp index ef4e7b7cb2..49f5d73955 100644 --- a/src/framework/avx2_detect.hpp +++ b/src/framework/avx2_detect.hpp @@ -18,7 +18,14 @@ #include #include #include + #include "misc/common_macros.hpp" +#if defined(_MSC_VER) + #include +#elif defined(GNUC_AVX2) + #include +#endif + namespace { inline void ccpuid(int cpu_info[4], int function_id){ diff --git a/src/framework/circuit.hpp b/src/framework/circuit.hpp index 171a077d17..3219d44ff5 100755 --- a/src/framework/circuit.hpp +++ b/src/framework/circuit.hpp @@ -91,10 +91,11 @@ class Circuit { inline void set_random_seed() {seed = std::random_device()();} private: - Operations::OpSet opset_; // Set of operation types contained in circuit - std::set qubitset_; // Set of qubits used in the circuit - std::set memoryset_; // Set of memory bits used in the circuit - std::set registerset_; // Set of register bits used in the circuit + Operations::OpSet opset_; // Set of operation types contained in circuit + std::set qubitset_; // Set of qubits used in the circuit + std::set memoryset_; // Set of memory bits used in the circuit + std::set registerset_; // Set of register bits used in the circuit + std::set saveset_; // Set of key names for save data instructions }; // Json conversion function @@ -112,6 +113,7 @@ void Circuit::set_params() { qubitset_.clear(); memoryset_.clear(); registerset_.clear(); + saveset_.clear(); can_sample = true; first_measure_pos = 0; @@ -127,6 +129,15 @@ void Circuit::set_params() { memoryset_.insert(op.memory.begin(), op.memory.end()); registerset_.insert(op.registers.begin(), op.registers.end()); + // Check for duplicate save keys + if (Operations::SAVE_TYPES.find(op.type) != Operations::SAVE_TYPES.end()) { + auto pair = saveset_.insert(op.string_params[0]); + if (!pair.second) { + throw std::invalid_argument("Duplicate key \"" + op.string_params[0] + + "\" in save instruction."); + } + } + // Compute measure sampling check if (can_sample) { if (first_measure) { diff --git a/src/framework/creg.hpp b/src/framework/creg.hpp index f386173432..2a79a0e871 100755 --- a/src/framework/creg.hpp +++ b/src/framework/creg.hpp @@ -138,20 +138,6 @@ bool ClassicalRegister::check_conditional(const Operations::Op &op) const { // Check if op is conditional if (op.conditional) return (creg_register_[creg_register_.size() - op.conditional_reg - 1] == '1'); - - // DEPRECATED: old style conditional - if (op.old_conditional) { - std::string current; - auto mask = Utils::padleft(Utils::hex2bin(op.old_conditional_mask, false), - '0', creg_memory_.size()); - for (size_t pos=0; pos < mask.size(); pos++) { - if (mask[pos] == '1') - current.push_back(creg_memory_[pos]); - } - auto val = Utils::padleft(Utils::hex2bin(op.old_conditional_val, false), - '0', current.size()); - return (val == current); - } // Op is not conditional return true; @@ -178,13 +164,20 @@ void ClassicalRegister::apply_bfunc(const Operations::Op &op) { } else { // We need to use big ints so we implement the bit-mask via the binary string // representation rather than using a big integer class - std::string mask_bin = Utils::hex2bin(mask); // has 0b prefix while creg_register_ doesn't - size_t length = std::min(mask_bin.size() - 2, creg_register_.size()); // -2 to remove 0b + std::string mask_bin = Utils::hex2bin(mask, false); + size_t length = std::min(mask_bin.size(), creg_register_.size()); std::string masked_val = std::string(length, '0'); for (size_t rev_pos = 0; rev_pos < length; rev_pos++) { masked_val[length - 1 - rev_pos] = (mask_bin[mask_bin.size() - 1 - rev_pos] & creg_register_[creg_register_.size() - 1 - rev_pos]); } + // remove leading 0's + size_t end_i = masked_val.find('1'); + if (end_i == std::string::npos) + masked_val = "0"; + else + masked_val.erase(0, end_i); + masked_val = Utils::bin2hex(masked_val); // convert to hex string // Using string comparison to compare to target value compared = masked_val.compare(target_val); diff --git a/src/framework/json.hpp b/src/framework/json.hpp index baa518e3a8..a296522404 100755 --- a/src/framework/json.hpp +++ b/src/framework/json.hpp @@ -47,7 +47,7 @@ namespace JSON { * @param name: file name to load. * @returns: the loaded json. */ -inline json_t load(std::string name); +inline json_t load(const std::string& name); /** * Check if a key exists in a json_t object. @@ -55,7 +55,7 @@ inline json_t load(std::string name); * @param js: the json_t to search for key. * @returns: true if the key exists, false otherwise. */ -inline bool check_key(std::string key, const json_t &js); +inline bool check_key(const std::string& key, const json_t &js); /** * Check if all keys exists in a json_t object. @@ -63,7 +63,7 @@ inline bool check_key(std::string key, const json_t &js); * @param js: the json_t to search for keys. * @returns: true if all keys exists, false otherwise. */ -inline bool check_keys(std::vector keys, const json_t &js); +inline bool check_keys(const std::vector& keys, const json_t &js); /** * Load a json_t object value into a variable if the key name exists. @@ -72,7 +72,7 @@ inline bool check_keys(std::vector keys, const json_t &js); * @param js: the json_t to search for key. * @returns: true if the keys exists and val was set, false otherwise. */ -template bool get_value(T &var, std::string key, const json_t &js); +template bool get_value(T &var, const std::string& key, const json_t &js); } // end namespace JSON @@ -153,7 +153,7 @@ void to_json(json_t &js, const matrix &mat); // JSON Helper Functions //------------------------------------------------------------------------------ -json_t JSON::load(std::string name) { +json_t JSON::load(const std::string& name) { if (name == "") { json_t js; return js; // Return empty node if no config file @@ -174,7 +174,7 @@ json_t JSON::load(std::string name) { return js; } -bool JSON::check_key(std::string key, const json_t &js) { +bool JSON::check_key(const std::string& key, const json_t &js) { // returns false if the value is 'null' if (js.find(key) != js.end() && !js[key].is_null()) return true; @@ -182,15 +182,15 @@ bool JSON::check_key(std::string key, const json_t &js) { return false; } -bool JSON::check_keys(std::vector keys, const json_t &js) { +bool JSON::check_keys(const std::vector& keys, const json_t &js) { bool pass = true; - for (auto s : keys) + for (const auto& s : keys) pass &= check_key(s, js); return pass; } template -bool JSON::get_value(T &var, std::string key, const json_t &js) { +bool JSON::get_value(T &var, const std::string& key, const json_t &js) { if (check_key(key, js)) { var = js[key].get(); return true; diff --git a/src/framework/linalg/linops/linops_json.hpp b/src/framework/linalg/linops/linops_json.hpp index 03581ea5a7..8473bfbb82 100755 --- a/src/framework/linalg/linops/linops_json.hpp +++ b/src/framework/linalg/linops/linops_json.hpp @@ -28,7 +28,7 @@ namespace Linalg { //---------------------------------------------------------------------------- // Linear operations //---------------------------------------------------------------------------- -json_t& iadd(json_t& lhs, const json_t& rhs) { +inline json_t& iadd(json_t& lhs, const json_t& rhs) { // Null case if (lhs.is_null()) { lhs = rhs; @@ -57,12 +57,12 @@ json_t& iadd(json_t& lhs, const json_t& rhs) { return lhs; } -json_t add(const json_t& lhs, const json_t& rhs) { +inline json_t add(const json_t& lhs, const json_t& rhs) { json_t result = lhs; return iadd(result, rhs); } -json_t& isub(json_t& lhs, const json_t& rhs) { +inline json_t& isub(json_t& lhs, const json_t& rhs) { // Null case if (rhs.is_null()) { return lhs; diff --git a/src/framework/linalg/square.hpp b/src/framework/linalg/square.hpp index 43e4ba50c2..6baae7054f 100755 --- a/src/framework/linalg/square.hpp +++ b/src/framework/linalg/square.hpp @@ -157,7 +157,7 @@ Vector square(const Vector& vec) { // Entrywise square of JSON //---------------------------------------------------------------------------- -json_t& isquare(json_t& data) { +inline json_t& isquare(json_t& data) { // Terminating case if (data.is_number()) { double val = data; @@ -180,7 +180,7 @@ json_t& isquare(json_t& data) { throw std::invalid_argument("Input JSONs cannot be squared."); } -json_t square(const json_t& data) { +inline json_t square(const json_t& data) { json_t result = data; return isquare(result); } diff --git a/src/framework/linalg/vector.hpp b/src/framework/linalg/vector.hpp index 075ea74805..ab7e163073 100755 --- a/src/framework/linalg/vector.hpp +++ b/src/framework/linalg/vector.hpp @@ -203,6 +203,7 @@ template Vector::Vector(Vector &&other) noexcept : size_(other.size_), data_(other.data_) { other.data_ = nullptr; + other.size_ = 0; } //----------------------------------------------------------------------- @@ -214,6 +215,7 @@ template Vector &Vector::operator=(Vector &&other) noexcept { size_ = other.size_; data_ = other.data_; other.data_ = nullptr; + other.size_ = 0; return *this; } diff --git a/src/framework/operations.hpp b/src/framework/operations.hpp index 916b504a9f..63f1115859 100755 --- a/src/framework/operations.hpp +++ b/src/framework/operations.hpp @@ -37,8 +37,25 @@ enum class RegComparison {Equal, NotEqual, Less, LessEqual, Greater, GreaterEqua // Enum class for operation types enum class OpType { gate, measure, reset, bfunc, barrier, snapshot, - matrix, diagonal_matrix, multiplexer, kraus, superop, roerror, - noise_switch, initialize, nop + matrix, diagonal_matrix, multiplexer, initialize, sim_op, nop, + // Noise instructions + kraus, superop, roerror, noise_switch, + // Save instructions + save_state, save_expval, save_expval_var, save_statevec, save_statevec_ket, + save_densmat, save_probs, save_probs_ket, save_amps, save_amps_sq, + save_stabilizer, save_unitary +}; + +enum class DataSubType { + single, c_single, list, c_list, accum, c_accum, average, c_average +}; + +static const std::unordered_set SAVE_TYPES = { + OpType::save_state, OpType::save_expval, OpType::save_expval_var, + OpType::save_statevec, OpType::save_statevec_ket, + OpType::save_densmat, OpType::save_probs, OpType::save_probs_ket, + OpType::save_amps, OpType::save_amps_sq, OpType::save_stabilizer, + OpType::save_unitary }; inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { @@ -58,6 +75,41 @@ inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { case OpType::barrier: stream << "barrier"; break; + case OpType::save_state: + stream << "save_state"; + break; + case OpType::save_expval: + stream << "save_expval"; + break; + case OpType::save_expval_var: + stream << "save_expval_var"; + case OpType::save_statevec: + stream << "save_statevector"; + break; + case OpType::save_statevec_ket: + stream << "save_statevector_dict"; + break; + case OpType::save_densmat: + stream << "save_density_matrix"; + break; + case OpType::save_probs: + stream << "save_probabilities"; + break; + case OpType::save_probs_ket: + stream << "save_probabilities_dict"; + break; + case OpType::save_amps: + stream << "save_amplitudes"; + break; + case OpType::save_amps_sq: + stream << "save_amplitudes_sq"; + break; + case OpType::save_stabilizer: + stream << "save_stabilizer"; + break; + case OpType::save_unitary: + stream << "save_unitary"; + break; case OpType::snapshot: stream << "snapshot"; break; @@ -85,6 +137,9 @@ inline std::ostream& operator<<(std::ostream& stream, const OpType& type) { case OpType::initialize: stream << "initialize"; break; + case OpType::sim_op: + stream << "sim_op"; + break; case OpType::nop: stream << "nop"; break; @@ -106,6 +161,7 @@ struct Op { reg_t qubits; // qubits operation acts on std::vector regs; // list of qubits for matrixes std::vector params; // real or complex params for gates + std::vector int_params; // integer parameters std::vector string_params; // used or snapshot label, and boolean functions // Conditional Operations @@ -113,11 +169,6 @@ struct Op { uint_t conditional_reg; // (opt) the (single) register location to look up for conditional RegComparison bfunc; // (opt) boolean function relation - // DEPRECATED: Old style conditionals (remove in 0.3) - bool old_conditional = false; // is gate old style conditional gate - std::string old_conditional_mask; // hex string for conditional mask - std::string old_conditional_val; // hex string for conditional value - // Measurement reg_t memory; // (opt) register operation it acts on (measure) reg_t registers; // (opt) register locations it acts on (measure, conditional) @@ -128,7 +179,11 @@ struct Op { // Readout error std::vector probs; - // Snapshots + // Expvals + std::vector> expval_params; + + // Legacy Snapshots + DataSubType save_type = DataSubType::single; using pauli_component_t = std::pair; // Pair (coeff, label_string) using matrix_component_t = std::pair>>; // vector of Pair(qubits, matrix), combined with coefficient std::vector params_expval_pauli; @@ -231,6 +286,19 @@ inline Op make_unitary(const reg_t &qubits, cmatrix_t &&mat, std::string label = return op; } +inline Op make_diagonal(const reg_t &qubits, cvector_t &&vec, std::string label = "") { + Op op; + op.type = OpType::diagonal_matrix; + op.name = "diagonal"; + op.qubits = qubits; + op.params = std::move(vec); + + if (label != "") + op.string_params = {label}; + + return op; +} + inline Op make_superop(const reg_t &qubits, const cmatrix_t &mat) { Op op; op.type = OpType::superop; @@ -394,6 +462,12 @@ Op json_to_op_measure(const json_t &js); Op json_to_op_reset(const json_t &js); Op json_to_op_bfunc(const json_t &js); Op json_to_op_initialize(const json_t &js); +Op json_to_op_pauli(const json_t &js); + +// Save data +Op json_to_op_save_default(const json_t &js, OpType op_type); +Op json_to_op_save_expval(const json_t &js, bool variance); +Op json_to_op_save_amps(const json_t &js, bool squared); // Snapshots Op json_to_op_snapshot(const json_t &js); @@ -414,7 +488,7 @@ Op json_to_op_roerror(const json_t &js); // Optional instruction parameters enum class Allowed {Yes, No}; -void add_condtional(const Allowed val, Op& op, const json_t &js); +void add_conditional(const Allowed val, Op& op, const json_t &js); //------------------------------------------------------------------------------ @@ -443,6 +517,31 @@ Op json_to_op(const json_t &js) { return json_to_op_diagonal(js); if (name == "superop") return json_to_op_superop(js); + // Save + if (name == "save_state") + return json_to_op_save_default(js, OpType::save_state); + if (name == "save_expval") + return json_to_op_save_expval(js, false); + if (name == "save_expval_var") + return json_to_op_save_expval(js, true); + if (name == "save_statevector") + return json_to_op_save_default(js, OpType::save_statevec); + if (name == "save_statevector_dict") + return json_to_op_save_default(js, OpType::save_statevec_ket); + if (name == "save_stabilizer") + return json_to_op_save_default(js, OpType::save_stabilizer); + if (name == "save_unitary") + return json_to_op_save_default(js, OpType::save_unitary); + if (name == "save_density_matrix") + return json_to_op_save_default(js, OpType::save_densmat); + if (name == "save_probabilities") + return json_to_op_save_default(js, OpType::save_probs); + if (name == "save_probabilities_dict") + return json_to_op_save_default(js, OpType::save_probs_ket); + if (name == "save_amplitudes") + return json_to_op_save_amps(js, false); + if (name == "save_amplitudes_sq") + return json_to_op_save_amps(js, true); // Snapshot if (name == "snapshot") return json_to_op_snapshot(js); @@ -458,6 +557,8 @@ Op json_to_op(const json_t &js) { return json_to_op_kraus(js); if (name == "roerror") return json_to_op_roerror(js); + if (name == "pauli") + return json_to_op_pauli(js); // Default assume gate return json_to_op_gate(js); } @@ -471,6 +572,8 @@ json_t op_to_json(const Op &op) { ret["regs"] = op.regs; if (!op.params.empty()) ret["params"] = op.params; + else if (!op.int_params.empty()) + ret["params"] = op.int_params; if (op.conditional) ret["conditional"] = op.conditional_reg; if (!op.memory.empty()) @@ -488,7 +591,7 @@ json_t op_to_json(const Op &op) { //------------------------------------------------------------------------------ -void add_condtional(const Allowed allowed, Op& op, const json_t &js) { +void add_conditional(const Allowed allowed, Op& op, const json_t &js) { // Check conditional if (JSON::check_key("conditional", js)) { // If instruction isn't allow to be conditional throw an exception @@ -496,16 +599,8 @@ void add_condtional(const Allowed allowed, Op& op, const json_t &js) { throw std::invalid_argument("Invalid instruction: \"" + op.name + "\" cannot be conditional."); } // If instruction is allowed to be conditional add parameters - if (js["conditional"].is_number()) { - // New style conditional - op.conditional_reg = js["conditional"]; - op.conditional = true; - } else { - // DEPRECATED: old style conditional (remove in 0.3) - JSON::get_value(op.old_conditional_mask, "mask", js["conditional"]); - JSON::get_value(op.old_conditional_val, "val", js["conditional"]); - op.old_conditional = true; - } + op.conditional_reg = js["conditional"]; + op.conditional = true; } } @@ -527,7 +622,7 @@ Op json_to_op_gate(const json_t &js) { op.string_params = {op.name}; // Conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); // Validation check_empty_name(op); @@ -549,7 +644,7 @@ Op json_to_op_barrier(const json_t &js) { op.name = "barrier"; JSON::get_value(op.qubits, "qubits", js); // Check conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); return op; } @@ -563,7 +658,7 @@ Op json_to_op_measure(const json_t &js) { JSON::get_value(op.registers, "register", js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); // Validation check_empty_qubits(op); @@ -585,7 +680,7 @@ Op json_to_op_reset(const json_t &js) { JSON::get_value(op.qubits, "qubits", js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); // Validation check_empty_qubits(op); @@ -602,7 +697,7 @@ Op json_to_op_initialize(const json_t &js) { JSON::get_value(op.params, "params", js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); // Validation check_empty_qubits(op); @@ -611,6 +706,31 @@ Op json_to_op_initialize(const json_t &js) { return op; } +Op json_to_op_pauli(const json_t &js){ + Op op; + op.type = OpType::gate; + op.name = "pauli"; + JSON::get_value(op.qubits, "qubits", js); + JSON::get_value(op.string_params, "params", js); + + // Check for optional label + // If label is not specified record the gate name as the label + std::string label; + JSON::get_value(label, "label", js); + if (label != "") + op.string_params.push_back(label); + else + op.string_params.push_back(op.name); + + // Conditional + add_conditional(Allowed::No, op, js); + + // Validation + check_empty_qubits(op); + check_duplicate_qubits(op); + + return op; +} //------------------------------------------------------------------------------ // Implementation: Boolean Functions @@ -657,7 +777,7 @@ Op json_to_op_bfunc(const json_t &js) { } // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); // Validation if (op.registers.empty()) { @@ -673,10 +793,9 @@ Op json_to_op_roerror(const json_t &js) { op.name = "roerror"; JSON::get_value(op.memory, "memory", js); JSON::get_value(op.registers, "register", js); - JSON::get_value(op.probs, "probabilities", js); // DEPRECATED: Remove in 0.4 JSON::get_value(op.probs, "params", js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); return op; } @@ -707,7 +826,7 @@ Op json_to_op_unitary(const json_t &js) { op.string_params.push_back(label); // Conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); return op; } @@ -736,7 +855,7 @@ Op json_to_op_diagonal(const json_t &js) { op.string_params.push_back(label); // Conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); return op; } @@ -748,7 +867,7 @@ Op json_to_op_superop(const json_t &js) { JSON::get_value(op.qubits, "qubits", js); JSON::get_value(op.mats, "params", js); // Check conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); // Validation check_empty_qubits(op); check_duplicate_qubits(op); @@ -769,7 +888,7 @@ Op json_to_op_multiplexer(const json_t &js) { // Construct op auto op = make_multiplexer(qubits, mats, label); // Conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); return op; } @@ -784,7 +903,7 @@ Op json_to_op_kraus(const json_t &js) { check_empty_qubits(op); check_duplicate_qubits(op); // Conditional - add_condtional(Allowed::Yes, op, js); + add_conditional(Allowed::Yes, op, js); return op; } @@ -795,7 +914,91 @@ Op json_to_op_noise_switch(const json_t &js) { op.name = "noise_switch"; JSON::get_value(op.params, "params", js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); + return op; +} + +//------------------------------------------------------------------------------ +// Implementation: Save data deserialization +//------------------------------------------------------------------------------ + +Op json_to_op_save_default(const json_t &js, OpType op_type) { + Op op; + op.type = op_type; + JSON::get_value(op.name, "name", js); + + // Get subtype + static const std::unordered_map subtypes { + {"single", DataSubType::single}, + {"c_single", DataSubType::c_single}, + {"average", DataSubType::average}, + {"c_average", DataSubType::c_average}, + {"list", DataSubType::list}, + {"c_list", DataSubType::c_list}, + {"accum", DataSubType::accum}, + {"c_accum", DataSubType::c_accum}, + }; + std::string subtype; + JSON::get_value(subtype, "snapshot_type", js); + auto subtype_it = subtypes.find(subtype); + if (subtype_it == subtypes.end()) { + throw std::runtime_error("Invalid data subtype \"" + subtype + + "\" in save data instruction."); + } + op.save_type = subtype_it->second; + + // Get data key + op.string_params.emplace_back(""); + JSON::get_value(op.string_params[0], "label", js); + + // Add optional qubits field + JSON::get_value(op.qubits, "qubits", js); + return op; +} + +Op json_to_op_save_expval(const json_t &js, bool variance) { + // Initialized default save instruction params + auto op_type = (variance) ? OpType::save_expval_var + : OpType::save_expval; + Op op = json_to_op_save_default(js, op_type); + + // Parse Pauli operator components + const auto threshold = 1e-12; // drop small components + // Get components + if (JSON::check_key("params", js) && js["params"].is_array()) { + for (const auto &comp : js["params"]) { + // Get complex coefficient + std::vector coeffs = comp[1]; + if (std::abs(coeffs[0]) > threshold || std::abs(coeffs[1]) > threshold) { + std::string pauli = comp[0]; + if (pauli.size() != op.qubits.size()) { + throw std::invalid_argument(std::string("Invalid expectation value save instruction ") + + "(Pauli label does not match qubit number.)."); + } + op.expval_params.emplace_back(pauli, coeffs[0], coeffs[1]); + } + } + } else { + throw std::invalid_argument("Invalid save expectation value \"params\"."); + } + + // Check edge case of all coefficients being empty + // In this case the operator had all coefficients zero, or sufficiently close + // to zero that they were all truncated. + if (op.expval_params.empty()) { + std::string pauli(op.qubits.size(), 'I'); + op.expval_params.emplace_back(pauli, 0., 0.); + } + + return op; +} + +Op json_to_op_save_amps(const json_t &js, bool squared) { + // Initialized default save instruction params + auto op_type = (squared) ? OpType::save_amps_sq + : OpType::save_amps; + Op op = json_to_op_save_default(js, op_type); + JSON::get_value(op.int_params, "params", js); return op; } @@ -814,7 +1017,7 @@ Op json_to_op_snapshot(const json_t &js) { // Default snapshot: has "type", "label", "qubits" auto op = json_to_op_snapshot_default(js); // Conditional - add_condtional(Allowed::No, op, js); + add_conditional(Allowed::No, op, js); return op; } @@ -836,21 +1039,15 @@ Op json_to_op_snapshot_default(const json_t &js) { Op json_to_op_snapshot_pauli(const json_t &js) { - // Load default snapshot parameters Op op = json_to_op_snapshot_default(js); - // Check qubits are valid - check_empty_qubits(op); - check_duplicate_qubits(op); - - // Parse Pauli operator components const auto threshold = 1e-15; // drop small components // Get components if (JSON::check_key("params", js) && js["params"].is_array()) { for (const auto &comp : js["params"]) { // Check component is length-2 array if (!comp.is_array() || comp.size() != 2) - throw std::invalid_argument("Invalid Pauli expval snapshot (param component " + + throw std::invalid_argument("Invalid Pauli expval params (param component " + comp.dump() + " invalid)."); // Get complex coefficient complex_t coeff = comp[0]; @@ -871,7 +1068,7 @@ Op json_to_op_snapshot_pauli(const json_t &js) { } // end if > threshold } // end component loop } else { - throw std::invalid_argument("Invalid Pauli snapshot \"params\"."); + throw std::invalid_argument("Invalid Pauli expectation value value snapshot \"params\"."); } // Check edge case of all coefficients being empty // In this case the operator had all coefficients zero, or sufficiently close @@ -882,6 +1079,7 @@ Op json_to_op_snapshot_pauli(const json_t &js) { complex_t coeff(0); op.params_expval_pauli.emplace_back(coeff, pauli); } + return op; } diff --git a/src/framework/opset.hpp b/src/framework/opset.hpp index 66a474e110..850b1e6bfd 100755 --- a/src/framework/opset.hpp +++ b/src/framework/opset.hpp @@ -36,24 +36,30 @@ class OpSet { } }; - public: // Alias for set of OpTypes using optypeset_t = std::unordered_set; // Public data members - optypeset_t optypes; // A set of op types - stringset_t gates; // A set of names for OpType::gates - stringset_t snapshots; // set of types for OpType::snapshot + optypeset_t optypes; // A set of op types + stringset_t gates; // A set of names for OpType::gates + stringset_t snapshots; // set of types for OpType::snapshot OpSet() = default; - OpSet(const optypeset_t &_optypes, - const stringset_t &_gates, + OpSet(const optypeset_t &_optypes, const stringset_t &_gates, const stringset_t &_snapshots) - : optypes(_optypes), gates(_gates), snapshots(_snapshots) {} - - OpSet(const std::vector &ops) { for (const auto &op : ops) {insert(op);} } + : optypes(_optypes), gates(_gates), snapshots(_snapshots) {} + + OpSet(optypeset_t &&_optypes, stringset_t &&_gates, stringset_t &&_snapshots) + : optypes(std::move(_optypes)), gates(std::move(_gates)), + snapshots(std::move(_snapshots)) {} + + OpSet(const std::vector &ops) { + for (const auto &op : ops) { + insert(op); + } + } //----------------------------------------------------------------------- // Insert operations to the OpSet @@ -64,7 +70,7 @@ class OpSet { // Add additional op to the opset void insert(const Op &_op); - + //----------------------------------------------------------------------- // Check if operations are in the OpSet //----------------------------------------------------------------------- @@ -111,13 +117,17 @@ class OpSet { // Return a set of all gates in a set not contained in the OpSet stringset_t difference_gates(const stringset_t &_gates) const; - + // Return a set of all snapshots in a set not contained in the OpSet stringset_t difference_snapshots(const stringset_t &_snapshots) const; + // Return the difference between two unordered sets + template + static std::unordered_set + unorderedset_difference(const std::unordered_set &first, + const std::unordered_set &second); }; - //------------------------------------------------------------------------------ // OpSet class methods //------------------------------------------------------------------------------ @@ -131,18 +141,14 @@ void OpSet::insert(const Op &op) { } void OpSet::insert(const OpSet &opset) { - optypes.insert(opset.optypes.begin(), - opset.optypes.end()); - gates.insert(opset.gates.begin(), - opset.gates.end()); - snapshots.insert(opset.snapshots.begin(), - opset.snapshots.end()); + optypes.insert(opset.optypes.begin(), opset.optypes.end()); + gates.insert(opset.gates.begin(), opset.gates.end()); + snapshots.insert(opset.snapshots.begin(), opset.snapshots.end()); } bool OpSet::contains(const OpSet &_opset) const { - return (contains(_opset.optypes) - && contains_gates(_opset.gates) - && contains_snapshots(_opset.snapshots)); + return (contains(_opset.optypes) && contains_gates(_opset.gates) && + contains_snapshots(_opset.snapshots)); } bool OpSet::contains(const Op &_op) const { @@ -157,7 +163,7 @@ bool OpSet::contains(const Op &_op) const { } bool OpSet::contains(const std::vector &_ops) const { - for (const auto &op: _ops) { + for (const auto &op : _ops) { if (!contains(op)) return false; } @@ -165,7 +171,7 @@ bool OpSet::contains(const std::vector &_ops) const { } bool OpSet::contains(const OpType &_optype) const { - return !(optypes.find(_optype) == optypes.end()); + return optypes.count(_optype) == 1; } bool OpSet::contains(const optypeset_t &_optypes) const { @@ -177,7 +183,7 @@ bool OpSet::contains(const optypeset_t &_optypes) const { } bool OpSet::contains_gates(const std::string &_gate) const { - return !(gates.find(_gate) == gates.end()); + return gates.count(_gate) == 1; } bool OpSet::contains_gates(const stringset_t &_gates) const { @@ -189,7 +195,7 @@ bool OpSet::contains_gates(const stringset_t &_gates) const { } bool OpSet::contains_snapshots(const std::string &_snapshot) const { - return !(snapshots.find(_snapshot) == snapshots.end()); + return snapshots.count(_snapshot) == 1; } bool OpSet::contains_snapshots(const stringset_t &_snapshots) const { @@ -209,34 +215,35 @@ OpSet OpSet::difference(const OpSet &_opset) const { OpSet ret; ret.optypes = difference(_opset.optypes); ret.gates = difference_gates(_opset.gates); - ret.snapshots = difference_gates(_opset.snapshots); + ret.snapshots = difference_snapshots(_opset.snapshots); return ret; } // Return a set of all optypes in set not contained in the OpSet OpSet::optypeset_t OpSet::difference(const optypeset_t &_optypes) const { - optypeset_t ret; - std::set_difference(_optypes.begin(), _optypes.end(), - optypes.begin(), optypes.end(), - std::inserter(ret, ret.begin())); - return ret; + return unorderedset_difference(optypes, _optypes); } // Return a set of all gates in a set not contained in the OpSet stringset_t OpSet::difference_gates(const stringset_t &_gates) const { - stringset_t ret; - std::set_difference(_gates.begin(), _gates.end(), - gates.begin(), gates.end(), - std::inserter(ret, ret.begin())); - return ret; + return unorderedset_difference(gates, _gates); } // Return a set of all snapshots in a set not contained in the OpSet stringset_t OpSet::difference_snapshots(const stringset_t &_snapshots) const { - stringset_t ret; - std::set_difference(_snapshots.begin(), _snapshots.end(), - snapshots.begin(), snapshots.end(), - std::inserter(ret, ret.begin())); + return unorderedset_difference(snapshots, _snapshots); +} + +template +std::unordered_set +OpSet::unorderedset_difference(const std::unordered_set &first, + const std::unordered_set &second) { + std::unordered_set ret; + for (const auto &item : second) { + if (first.count(item) == 0) { + ret.insert(item); + } + } return ret; } @@ -249,12 +256,12 @@ stringset_t OpSet::difference_snapshots(const stringset_t &_snapshots) const { // Ostream overload for opset //------------------------------------------------------------------------- -inline std::ostream& operator<<(std::ostream& out, - const AER::Operations::OpSet& opset) { +inline std::ostream &operator<<(std::ostream &out, + const AER::Operations::OpSet &opset) { bool first = true; out << "{"; if (!opset.optypes.empty()) { - out << "\"optypes\": " << opset.optypes; + out << "\"instructions\": " << opset.optypes; first = false; } if (!opset.gates.empty()) { diff --git a/src/framework/pybind_basics.hpp b/src/framework/pybind_basics.hpp new file mode 100755 index 0000000000..9a86c5d408 --- /dev/null +++ b/src/framework/pybind_basics.hpp @@ -0,0 +1,205 @@ +/** + * 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. + */ + +#ifndef _aer_framework_pybind_basics_hpp_ +#define _aer_framework_pybind_basics_hpp_ + +#include +#include + +#include "framework/linalg/vector.hpp" +#include "framework/matrix.hpp" + +#include "framework/pybind_json.hpp" + +namespace AerToPy { + +//============================================================================ +// Pybind11 move conversion of basic types +//============================================================================ + +// Move an arbitrary object to Python by calling Pybind11 cast with move +// Template specialization is used with this function for adding custom +// conversion for other types +// NOTE: Can this function be replaced by overload py::cast for custom types? +template py::object to_python(T &&obj); + +// Move a matrix to Python via conversion to Numpy array +template py::object to_python(matrix &&obj); + +// Move a Vector to Python via conversion to Numpy array +template py::object to_python(AER::Vector &&obj); + +// Move a Vector to Python via recusivly calling to_python on elements +template py::object to_python(std::vector &&obj); + +// Move an Unordered string map to Python object by calling to_python on elements +template py::object to_python(std::unordered_map &&obj); + +// Move an Unordered string map into an existing Python dict +template +void add_to_python(py::dict &pydata, std::unordered_map &&obj); + + +// Template specialization for moving numeric std::vectors to Numpy arrays +template <> py::object to_python(std::vector &&obj); +template <> py::object to_python(std::vector &&obj); +template <> py::object to_python(std::vector &&obj); +template <> py::object to_python(std::vector &&obj); +template <> py::object to_python(std::vector> &&obj); +template <> py::object to_python(std::vector> &&obj); + +// Template specialization for JSON +// NOTE: this copies rather than moves +template <> py::object to_python(json_t &&obj); + +//------------------------------------------------------------------------------ +// Convert To Numpy Arrays +//------------------------------------------------------------------------------ + +// Convert a matrix to a 2D Numpy array in Fortan order +template +py::array_t to_numpy(matrix &&obj); + +// Convert a Vector to a 1D Numpy array +template +py::array_t to_numpy(AER::Vector &&obj); + +// Convert a vector to a 1D Numpy array +template +py::array_t to_numpy(std::vector &&obj); + +//============================================================================ +// Implementation +//============================================================================ + +//------------------------------------------------------------------------------ +// Basic Types +//------------------------------------------------------------------------------ + +template +py::object to_python(T &&obj) { + return py::cast(obj, py::return_value_policy::move); +} + +template <> +py::object to_python(json_t &&obj) { + py::object pydata; + from_json(obj, pydata); + return pydata; +} + +template +py::object to_python(std::unordered_map &&obj) { + py::dict pydata; + add_to_python(pydata, std::move(obj)); + return std::move(pydata); +} + +template +void add_to_python(py::dict &pydata, std::unordered_map &&obj) { + for(auto& elt : obj) { + pydata[elt.first.data()] = to_python(std::move(elt.second)); + } +} + +template +py::object to_python(std::vector &&obj) { + py::list pydata; + for(auto& elt : obj) { + pydata.append(to_python(std::move(elt))); + } + return std::move(pydata); +} + +template +py::object to_python(matrix &&obj) { + return to_numpy(std::move(obj)); +} + +template +py::object to_python(AER::Vector &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector> &&obj) { + return to_numpy(std::move(obj)); +} + +template <> +py::object to_python(std::vector> &&obj) { + return to_numpy(std::move(obj)); +} + +//------------------------------------------------------------------------------ +// Array Types +//------------------------------------------------------------------------------ + +template +py::array_t to_numpy(matrix &&src) { + std::array shape {static_cast(src.GetRows()), + static_cast(src.GetColumns())}; + matrix* src_ptr = new matrix(std::move(src)); + auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); + return py::array_t(shape, src_ptr->data(), capsule); +} + +template +py::array_t to_numpy(AER::Vector &&src) { + AER::Vector* src_ptr = new AER::Vector(std::move(src)); + auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); + return py::array_t( + src_ptr->size(), // shape of array + src_ptr->data(), // c-style contiguous strides for vector + capsule // numpy array references this parent + ); +} + + +template +py::array_t to_numpy(std::vector &&src) { + std::vector* src_ptr = new std::vector(std::move(src)); + auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); + return py::array_t( + src_ptr->size(), // shape of array + src_ptr->data(), // c-style contiguous strides for vector + capsule // numpy array references this parent + ); +} + +//------------------------------------------------------------------------------ +} // end namespace AerToPy +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/pybind_json.hpp b/src/framework/pybind_json.hpp index 975c0cc7ac..83de9c93b6 100755 --- a/src/framework/pybind_json.hpp +++ b/src/framework/pybind_json.hpp @@ -49,181 +49,10 @@ namespace nl = nlohmann; using namespace pybind11::literals; using json_t = nlohmann::json; -#include "framework/results/result.hpp" -#include "framework/results/data/average_data.hpp" - //------------------------------------------------------------------------------ -// Aer C++ -> Python Conversion +// Nlohman JSON <--> Python Conversion //------------------------------------------------------------------------------ -namespace AerToPy { - -/** - * Convert a 1D contiguous container into a numpy array - * @param src is a vector - * @returns a python object (py::array_t) - */ -template -py::array_t array_from_sequence(Sequence& src); -template -py::array_t array_from_sequence(Sequence&& src); - -/** - * Convert a Matrix into a numpy array - * @param mat is a Matrix - * @returns a python object (py::array_t) - */ -template -py::array_t array_from_matrix(matrix &&mat); -template -py::array_t array_from_matrix(matrix &mat); - -/** - * Convert a Vector into a numpy array - * @param mat is a Vector - * @returns a python object (py::array_t) - */ -template -py::array_t array_from_vector(AER::Vector &&vec); -template -py::array_t array_from_vector(AER::Vector &vec); - -/** - * Convert a AverageData to a python object - * @param avg_data is an AverageData - * @returns a py::dict - */ -template -py::object from_avg_data(AER::AverageData &&avg_data); -template -py::object from_avg_data(AER::AverageData &avg_data); - -// JSON specialization -template<> -py::object from_avg_data(AER::AverageData &&avg_data); - -/** - * Convert a AverageData to a python object - * @param avg_data is an AverageData - * @returns a py::dict - */ -template -py::object from_avg_data(AER::AverageData> &&avg_data); -template -py::object from_avg_data(AER::AverageData> &avg_data); - - -/** - * Convert a AverageData to a python object - * @param avg_data is an AverageData - * @returns a py::dict - */ -template -py::object from_avg_data(AER::AverageData> &&avg_data); -template -py::object from_avg_data(AER::AverageData> &avg_data); - -/** - * Convert a AverageData to a python object - * @param avg_data is an AverageData - * @returns a py::dict - */ -template -py::object from_avg_data(AER::AverageData> &&avg_data); -template -py::object from_avg_data(AER::AverageData> &avg_data); - -/** - * Convert a AverageSnapshot to a python object - * @param avg_snap is an AverageSnapshot - * @returns a py::dict - */ -template -py::object from_avg_snap(AER::AverageSnapshot &&avg_snap); -template -py::object from_avg_snap(AER::AverageSnapshot &avg_snap); - -/** - * Convert a PershotSnapshot to a python object - * @param avg_snap is an PershotSnapshot - * @returns a py::dict - */ -template -py::object from_pershot_snap(AER::PershotSnapshot &&snap); -template -py::object from_pershot_snap(AER::PershotSnapshot &snap); - -/** - * Convert a PershotData to a python object - * @param data is an PershotData - * @returns a py::dict - */ -template -py::object from_pershot_data(AER::PershotData &&data); -template -py::object from_pershot_data(AER::PershotData &data); - -// JSON specialization -template<> -py::object from_pershot_data(AER::PershotData &&avg_data); - -/** - * Convert a PershotData to a python object - * @param data is an PershotData - * @returns a py::dict - */ -template -py::object from_pershot_data(AER::PershotData> &&data); -template -py::object from_pershot_data(AER::PershotData> &data); - - -/** - * Convert a PershotData to a python object - * @param data is an PershotData - * @returns a py::dict - */ -template -py::object from_pershot_data(AER::PershotData> &&data); -template -py::object from_pershot_data(AER::PershotData> &data); - -/** - * Convert a PershotData to a python object - * @param data is an PershotData - * @returns a py::dict - */ -template -py::object from_pershot_data(AER::PershotData> &&data); -template -py::object from_pershot_data(AER::PershotData> &data); - -/** - * Convert an ExperimentData to a python object - * @param result is an ExperimentData - * @returns a py::dict - */ -py::object from_data(AER::ExperimentData &&result); -py::object from_data(AER::ExperimentData &result); - -/** - * Convert an ExperimentResult to a python object - * @param result is an ExperimentResult - * @returns a py::dict - */ -py::object from_experiment(AER::ExperimentResult &&result); -py::object from_experiment(AER::ExperimentResult &result); - -/** - * Convert a Result to a python object - * @param result is a Result - * @returns a py::dict - */ -py::object from_result(AER::Result &&result); -py::object from_result(AER::Result &result); - -} //end namespace AerToPy - namespace std { /** @@ -466,444 +295,4 @@ void std::from_json(const json_t &js, py::object &o) { //------------------------------------------------------------------------------ -//============================================================================ -// Pybind Conversion for Simulator types -//============================================================================ - -template -py::array_t AerToPy::array_from_sequence(Sequence& seq) { - return AerToPy::array_from_sequence(std::move(seq)); -} - -template -py::array_t AerToPy::array_from_sequence(Sequence&& seq) { - // Move entire object to heap (Ensure is moveable!). Memory handled via Python capsule - Sequence* seq_ptr = new Sequence(std::move(seq)); - auto capsule = py::capsule(seq_ptr, [](void* p) { delete reinterpret_cast(p); }); - return py::array_t( - seq_ptr->size(), // shape of array - seq_ptr->data(), // c-style contiguous strides for Sequence - capsule // numpy array references this parent - ); -} - -template -py::array_t AerToPy::array_from_vector(AER::Vector &src) { - return AerToPy::array_from_vector(std::move(src)); -} - -template -py::array_t AerToPy::array_from_vector(AER::Vector &&src) { - // Move entire object to heap (Ensure is moveable!). Memory handled via Python capsule - AER::Vector* src_ptr = new AER::Vector(std::move(src)); - auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); - return py::array_t(src_ptr->size(), src_ptr->data(), capsule); -} - -template -py::array_t AerToPy::array_from_matrix(matrix &src) { - return AerToPy::array_from_matrix(std::move(src)); -} - -template -py::array_t AerToPy::array_from_matrix(matrix &&src) { - std::array shape {static_cast(src.GetRows()), - static_cast(src.GetColumns())}; - matrix* src_ptr = new matrix(std::move(src)); - auto capsule = py::capsule(src_ptr, [](void* p) { delete reinterpret_cast*>(p); }); - return py::array_t(shape, src_ptr->data(), capsule); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData &avg_data) { - return AerToPy::from_avg_data(std::move(avg_data)); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData &&avg_data) { - py::dict d; - d["value"] = avg_data.mean(); - if (avg_data.has_variance()) { - d["variance"] = avg_data.variance(); - } - return std::move(d); -} - -template <> -py::object AerToPy::from_avg_data(AER::AverageData &&avg_data) { - py::dict d; - py::object py_mean; - from_json(avg_data.mean(), py_mean); - d["value"] = std::move(py_mean); - if (avg_data.has_variance()) { - py::object py_var; - from_json(avg_data.variance(), py_var); - d["variance"] = std::move(py_var); - } - return std::move(d); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &avg_data) { - return AerToPy::from_avg_data(std::move(avg_data)); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &&avg_data) { - py::dict d; - d["value"] = AerToPy::array_from_vector(avg_data.mean()); - if (avg_data.has_variance()) { - d["variance"] = AerToPy::array_from_vector(avg_data.variance()); - } - return std::move(d); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &avg_data) { - return AerToPy::from_avg_data(std::move(avg_data)); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &&avg_data) { - py::dict d; - d["value"] = AerToPy::array_from_matrix(avg_data.mean()); - if (avg_data.has_variance()) { - d["variance"] = AerToPy::array_from_matrix(avg_data.variance()); - } - return std::move(d); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &avg_data) { - return AerToPy::from_avg_data(std::move(avg_data)); -} - -template -py::object AerToPy::from_avg_data(AER::AverageData> &&avg_data) { - py::dict d; - d["value"] = AerToPy::array_from_sequence(avg_data.mean()); - if (avg_data.has_variance()) { - d["variance"] = AerToPy::array_from_sequence(avg_data.variance()); - } - return std::move(d); -} - -template -py::object AerToPy::from_avg_snap(AER::AverageSnapshot &avg_snap) { - return AerToPy::from_avg_snap(std::move(avg_snap)); -} - -template -py::object AerToPy::from_avg_snap(AER::AverageSnapshot &&avg_snap) { - py::dict d; - for (auto &outer_pair : avg_snap.data()) { - py::list d1; - for (auto &inner_pair : outer_pair.second) { - // Store mean and variance for snapshot - py::dict datum = AerToPy::from_avg_data(inner_pair.second); - // Add memory key if there are classical registers - auto memory = inner_pair.first; - if ( ! memory.empty()) { - datum["memory"] = inner_pair.first; - } - // Add to list of output - d1.append(std::move(datum)); - } - d[outer_pair.first.data()] = std::move(d1); - } - return std::move(d); -} - -template -py::object AerToPy::from_pershot_snap(AER::PershotSnapshot &snap) { - return AerToPy::from_pershot_snap(std::move(snap)); -} -template -py::object AerToPy::from_pershot_snap(AER::PershotSnapshot &&snap) { - py::dict d; - // string PershotData - for (auto &pair : snap.data()) - d[pair.first.data()] = AerToPy::from_pershot_data(pair.second); - return std::move(d); -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData &&data) { - return py::cast(data.data(), py::return_value_policy::move); -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData &data) { - return AerToPy::from_pershot_data(std::move(data)); -} - -template<> -py::object AerToPy::from_pershot_data(AER::PershotData &&data) { - py::object tmp; - from_json(data.data(), tmp); - return tmp; -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &&data) { - py::list l; - for (auto &&item : data.data()) { - l.append(AerToPy::array_from_matrix(item)); - } - return std::move(l); -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &data) { - return AerToPy::from_pershot_data(std::move(data)); -} - - - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &&data) { - py::list l; - for (auto &&item : data.data()) { - l.append(AerToPy::array_from_vector(item)); - } - return std::move(l); -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &data) { - return AerToPy::from_pershot_data(std::move(data)); -} - - - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &&data) { - py::list l; - for (auto &&item : data.data()) { - l.append(AerToPy::array_from_sequence(item)); - } - return std::move(l); -} - -template -py::object AerToPy::from_pershot_data(AER::PershotData> &data) { - return AerToPy::from_pershot_data(std::move(data)); -} - - -py::object AerToPy::from_data(AER::ExperimentData &datum) { - return AerToPy::from_data(std::move(datum)); -} - -py::object AerToPy::from_data(AER::ExperimentData &&datum) { - py::dict pydata; - - // Measure data - if (datum.return_counts_ && ! datum.counts_.empty()) { - pydata["counts"] = std::move(datum.counts_); - } - if (datum.return_memory_ && ! datum.memory_.empty()) { - pydata["memory"] = std::move(datum.memory_); - } - if (datum.return_register_ && ! datum.register_.empty()) { - pydata["register"] = std::move(datum.register_); - } - - // Add additional data - for (auto &pair : datum.additional_data()) { - py::object tmp; - from_json(pair.second, tmp); - pydata[pair.first.data()] = std::move(tmp); - } - for (auto &pair : datum.additional_data>()) { - pydata[pair.first.data()] = pair.second; - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_sequence(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_sequence(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_vector(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_vector(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_matrix(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = AerToPy::array_from_matrix(pair.second); - } - for (auto &pair : datum.additional_data>>()) { - pydata[pair.first.data()] = pair.second; - } - for (auto &pair : datum.additional_data>()) { - pydata[pair.first.data()] = pair.second; - } - - // Snapshot data - if (datum.return_snapshots_) { - py::dict snapshots; - - // Average snapshots - for (auto &pair : datum.average_snapshots()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - for (auto &pair : datum.average_snapshots>()) { - snapshots[pair.first.data()] = AerToPy::from_avg_snap(pair.second); - } - // Singleshot snapshot data - // Note these will override the average snapshots - // if they share the same type string - - for (auto &pair : datum.pershot_snapshots()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - for (auto &pair : datum.pershot_snapshots>()) { - snapshots[pair.first.data()] = AerToPy::from_pershot_snap(pair.second); - } - - if ( py::len(snapshots) != 0 ) - pydata["snapshots"] = std::move(snapshots); - } - //for (auto item : pydatum) - // py::print(" {}:, {}"_s.format(item.first, item.second)); - return std::move(pydata); -} - -py::object AerToPy::from_experiment(AER::ExperimentResult &result) { - return AerToPy::from_experiment(std::move(result)); -} - -py::object AerToPy::from_experiment(AER::ExperimentResult &&result) { - py::dict pyexperiment; - - pyexperiment["shots"] = result.shots; - pyexperiment["seed_simulator"] = result.seed; - - pyexperiment["data"] = AerToPy::from_data(result.data); - - pyexperiment["success"] = (result.status == AER::ExperimentResult::Status::completed); - switch (result.status) { - case AER::ExperimentResult::Status::completed: - pyexperiment["status"] = "DONE"; - break; - case AER::ExperimentResult::Status::error: - pyexperiment["status"] = std::string("ERROR: ") + result.message; - break; - case AER::ExperimentResult::Status::empty: - pyexperiment["status"] = "EMPTY"; - } - pyexperiment["time_taken"] = result.time_taken; - if (result.header.empty() == false) { - py::object tmp; - from_json(result.header, tmp); - pyexperiment["header"] = std::move(tmp); - } - if (result.metadata.empty() == false) { - py::object tmp; - from_json(result.metadata, tmp); - pyexperiment["metadata"] = std::move(tmp); - } - return std::move(pyexperiment); -} - -py::object AerToPy::from_result(AER::Result &result) { - return AerToPy::from_result(std::move(result)); -} - -py::object AerToPy::from_result(AER::Result &&result) { - py::dict pyresult; - pyresult["qobj_id"] = result.qobj_id; - - pyresult["backend_name"] = result.backend_name; - pyresult["backend_version"] = result.backend_version; - pyresult["date"] = result.date; - pyresult["job_id"] = result.job_id; - - py::list exp_results; - for(AER::ExperimentResult& exp : result.results) - exp_results.append(AerToPy::from_experiment(std::move(exp))); - pyresult["results"] = std::move(exp_results); - - // For header and metadata we continue using the json->pyobject casting - // bc these are assumed to be small relative to the ExperimentResults - if (result.header.empty() == false) { - py::object tmp; - from_json(result.header, tmp); - pyresult["header"] = std::move(tmp); - } - if (result.metadata.empty() == false) { - py::object tmp; - from_json(result.metadata, tmp); - pyresult["metadata"] = std::move(tmp); - } - pyresult["success"] = (result.status == AER::Result::Status::completed); - switch (result.status) { - case AER::Result::Status::completed: - pyresult["status"] = "COMPLETED"; - break; - case AER::Result::Status::partial_completed: - pyresult["status"] = "PARTIAL COMPLETED"; - break; - case AER::Result::Status::error: - pyresult["status"] = std::string("ERROR: ") + result.message; - break; - case AER::Result::Status::empty: - pyresult["status"] = "EMPTY"; - } - return std::move(pyresult); - -} - #endif diff --git a/src/framework/results/data/data.hpp b/src/framework/results/data/data.hpp new file mode 100644 index 0000000000..f517a10de4 --- /dev/null +++ b/src/framework/results/data/data.hpp @@ -0,0 +1,249 @@ +/** + * 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_results_data_hpp_ +#define _aer_framework_results_data_hpp_ + +// Data primatives +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" + +// Data Containers +#include "framework/results/data/mixins/data_creg.hpp" +#include "framework/results/data/mixins/data_rvalue.hpp" +#include "framework/results/data/mixins/data_rvector.hpp" +#include "framework/results/data/mixins/data_rdict.hpp" +#include "framework/results/data/mixins/data_cmatrix.hpp" +#include "framework/results/data/mixins/data_cvector.hpp" +#include "framework/results/data/mixins/data_cdict.hpp" +#include "framework/results/data/mixins/data_json.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct Data : public DataCreg, + public DataRValue, + public DataRVector, + public DataRDict, + public DataCVector, + public DataCMatrix, + public DataCDict, + public DataJSON { + + //---------------------------------------------------------------- + // Measurement data + //---------------------------------------------------------------- + + // Add outcome to count dictionary + void add_count(const std::string &outcome); + + // Add outcome to memory list + void add_memory(const std::string &outcome); + void add_memory(std::string &&outcome); + + //---------------------------------------------------------------- + // Add single data + //---------------------------------------------------------------- + template + void add_single(const T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_single(T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_single(T &&data, const std::string &outer_key, + const Args &... inner_keys); + + //---------------------------------------------------------------- + // Add list data + //---------------------------------------------------------------- + template + void add_list(const T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_list(T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_list(T &&data, const std::string &outer_key, + const Args &... inner_keys); + + //---------------------------------------------------------------- + // Add accum data + //---------------------------------------------------------------- + template + void add_accum(const T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_accum(T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_accum(T &&data, const std::string &outer_key, + const Args &... inner_keys); + + //---------------------------------------------------------------- + // Add average data + //---------------------------------------------------------------- + template + void add_average(const T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_average(T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add_average(T &&data, const std::string &outer_key, + const Args &... inner_keys); + + //---------------------------------------------------------------- + // Utility and config + //---------------------------------------------------------------- + + // Serialize engine data to JSON + json_t to_json(); + + // Combine stored data + Data &combine(Data &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +Data &Data::combine(Data &&other) { + DataRValue::combine(std::move(other)); + DataRVector::combine(std::move(other)); + DataRDict::combine(std::move(other)); + DataCVector::combine(std::move(other)); + DataCMatrix::combine(std::move(other)); + DataCDict::combine(std::move(other)); + DataJSON::combine(std::move(other)); + DataCreg::combine(std::move(other)); + return *this; +} + +json_t Data::to_json() { + json_t result; + DataRValue::add_to_json(result); + DataRVector::add_to_json(result); + DataRDict::add_to_json(result); + DataCVector::add_to_json(result); + DataCMatrix::add_to_json(result); + DataCDict::add_to_json(result); + DataJSON::add_to_json(result); + DataCreg::add_to_json(result); + return result; +} + + +template +void Data::add_single(const T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_single(T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_single(T &&data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(std::move(data), outer_key, + inner_keys...); +} + +template +void Data::add_list(const T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_list(T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_list(T &&data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(std::move(data), outer_key, + inner_keys...); +} + +template +void Data::add_accum(const T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_accum(T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_accum(T &&data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(std::move(data), outer_key, + inner_keys...); +} + +template +void Data::add_average(const T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_average(T &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Data::add_average(T &&data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(std::move(data), outer_key, + inner_keys...); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/metadata.hpp b/src/framework/results/data/metadata.hpp new file mode 100644 index 0000000000..83820ea9f2 --- /dev/null +++ b/src/framework/results/data/metadata.hpp @@ -0,0 +1,136 @@ +/** + * 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_results_data_metadata_hpp_ +#define _aer_framework_results_data_metadata_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/single_data.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct Metadata : public DataMap, + public DataMap, + public DataMap { + + //---------------------------------------------------------------- + // Add JSON metadata + //---------------------------------------------------------------- + template + void add(const json_t &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add(json_t &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add(json_t &&data, const std::string &outer_key, + const Args &... inner_keys); + + //---------------------------------------------------------------- + // Add general metadata + // + // These functions allow adding general data types via conversion + // to json_t. + //---------------------------------------------------------------- + template + void add(const T &data, const std::string &outer_key, + const Args &... inner_keys); + + template + void add(T &data, const std::string &outer_key, const Args &... inner_keys); + + template + void add(T &&data, const std::string &outer_key, const Args &... inner_keys); + + // Serialize engine data to JSON + json_t to_json(); + + // Combine stored data + Metadata &combine(Metadata &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +Metadata &Metadata::combine(Metadata &&other) { + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + return *this; +} + +json_t Metadata::to_json() { + json_t result; + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); + return result; +} + +template +void Metadata::add(const T &data, const std::string &outer_key, + const Args &... inner_keys) { + json_t tmp = data; + DataMap::add( + std::move(tmp), outer_key, inner_keys...); +} + +template +void Metadata::add(T &data, const std::string &outer_key, + const Args &... inner_keys) { + json_t tmp = data; + DataMap::add( + std::move(tmp), outer_key, inner_keys...); +} + +template +void Metadata::add(T &&data, const std::string &outer_key, + const Args &... inner_keys) { + json_t tmp = data; + DataMap::add( + std::move(tmp), outer_key, inner_keys...); +} + +template +void Metadata::add(const json_t &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Metadata::add(json_t &data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add(data, outer_key, + inner_keys...); +} + +template +void Metadata::add(json_t &&data, const std::string &outer_key, + const Args &... inner_keys) { + DataMap::add( + std::move(data), outer_key, inner_keys...); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_cdict.hpp b/src/framework/results/data/mixins/data_cdict.hpp new file mode 100644 index 0000000000..16d1e3b8d7 --- /dev/null +++ b/src/framework/results/data/mixins/data_cdict.hpp @@ -0,0 +1,66 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_cdict_hpp_ +#define _aer_framework_results_data_cdict_hpp_ + +#include +#include + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataCDict : public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2> { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataCDict &combine(DataCDict &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataCDict &DataCDict::combine(DataCDict &&other) { + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + return *this; +} + +void DataCDict::add_to_json(json_t &result) { + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_cmatrix.hpp b/src/framework/results/data/mixins/data_cmatrix.hpp new file mode 100644 index 0000000000..0aa1920603 --- /dev/null +++ b/src/framework/results/data/mixins/data_cmatrix.hpp @@ -0,0 +1,103 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_cmatrix_hpp_ +#define _aer_framework_results_data_cmatrix_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataCMatrix : + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2> { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataCMatrix &combine(DataCMatrix &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataCMatrix &DataCMatrix::combine(DataCMatrix &&other) { + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + return *this; +} + +void DataCMatrix::add_to_json(json_t &result) { + + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_creg.hpp b/src/framework/results/data/mixins/data_creg.hpp new file mode 100644 index 0000000000..73a68acb11 --- /dev/null +++ b/src/framework/results/data/mixins/data_creg.hpp @@ -0,0 +1,57 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_creg_hpp_ +#define _aer_framework_results_data_creg_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataCreg : public DataMap, // Counts + public DataMap // Memory +{ + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataCreg &combine(DataCreg &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataCreg &DataCreg::combine(DataCreg &&other) { + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + return *this; +} + +void DataCreg::add_to_json(json_t &result) { + DataMap::add_to_json(result); + DataMap::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_cvector.hpp b/src/framework/results/data/mixins/data_cvector.hpp new file mode 100644 index 0000000000..cde3fd7636 --- /dev/null +++ b/src/framework/results/data/mixins/data_cvector.hpp @@ -0,0 +1,75 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_cvector_hpp_ +#define _aer_framework_results_data_cvector_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataCVector : public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 2> { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataCVector &combine(DataCVector &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataCVector &DataCVector::combine(DataCVector &&other) { + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + return *this; +} + +void DataCVector::add_to_json(json_t &result) { + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 2>::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_json.hpp b/src/framework/results/data/mixins/data_json.hpp new file mode 100644 index 0000000000..01104c3a8f --- /dev/null +++ b/src/framework/results/data/mixins/data_json.hpp @@ -0,0 +1,66 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_json_hpp_ +#define _aer_framework_results_data_json_hpp_ + +#include +#include + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataJSON : public DataMap, + public DataMap, + public DataMap, + public DataMap { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataJSON &combine(DataJSON &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataJSON &DataJSON::combine(DataJSON &&other) { + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + return *this; +} + +void DataJSON::add_to_json(json_t &result) { + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_rdict.hpp b/src/framework/results/data/mixins/data_rdict.hpp new file mode 100644 index 0000000000..8f9e0f55a2 --- /dev/null +++ b/src/framework/results/data/mixins/data_rdict.hpp @@ -0,0 +1,71 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_rdict_hpp_ +#define _aer_framework_results_data_rdict_hpp_ + +#include +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataRDict : public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2> { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataRDict &combine(DataRDict &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataRDict &DataRDict::combine(DataRDict &&other) { + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + return *this; +} + +void DataRDict::add_to_json(json_t &result) { + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_rvalue.hpp b/src/framework/results/data/mixins/data_rvalue.hpp new file mode 100644 index 0000000000..901c84eb30 --- /dev/null +++ b/src/framework/results/data/mixins/data_rvalue.hpp @@ -0,0 +1,72 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_rvalue_hpp_ +#define _aer_framework_results_data_rvalue_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataRValue : + public DataMap, + public DataMap, + public DataMap, + public DataMap, + public DataMap, + public DataMap { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataRValue &combine(DataRValue &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataRValue &DataRValue::combine(DataRValue &&other) { + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + DataMap::combine(std::move(other)); + return *this; +} + +void DataRValue::add_to_json(json_t &result) { + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); + DataMap::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/data_rvector.hpp b/src/framework/results/data/mixins/data_rvector.hpp new file mode 100644 index 0000000000..ab283f7ac1 --- /dev/null +++ b/src/framework/results/data/mixins/data_rvector.hpp @@ -0,0 +1,71 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_rvector_hpp_ +#define _aer_framework_results_data_rvector_hpp_ + +#include "framework/results/data/subtypes/data_map.hpp" +#include "framework/results/data/subtypes/list_data.hpp" +#include "framework/results/data/subtypes/single_data.hpp" +#include "framework/results/data/subtypes/accum_data.hpp" +#include "framework/results/data/subtypes/average_data.hpp" +#include "framework/types.hpp" + +namespace AER { + +//============================================================================ +// Result container for Qiskit-Aer +//============================================================================ + +struct DataRVector : public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2>, + public DataMap, 1>, + public DataMap, 2> { + + // Serialize engine data to JSON + void add_to_json(json_t &result); + + // Combine stored data + DataRVector &combine(DataRVector &&other); +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +DataRVector &DataRVector::combine(DataRVector &&other) { + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + DataMap, 1>::combine(std::move(other)); + DataMap, 2>::combine(std::move(other)); + return *this; +} + +void DataRVector::add_to_json(json_t &result) { + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); + DataMap, 1>::add_to_json(result); + DataMap, 2>::add_to_json(result); +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/mixins/pybind_data_cdict.hpp b/src/framework/results/data/mixins/pybind_data_cdict.hpp new file mode 100755 index 0000000000..7bcddf3cdc --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_cdict.hpp @@ -0,0 +1,53 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_cdict_hpp_ +#define _aer_framework_result_data_pybind_data_cdict_hpp_ + +#include "framework/results/data/mixins/data_cdict.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataCDict container object to a new Python dict +py::object to_python(AER::DataCDict &&data); + +// Move an DataCDict container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataCDict &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataCDict &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataCDict &&data) { + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_cmatrix.hpp b/src/framework/results/data/mixins/pybind_data_cmatrix.hpp new file mode 100755 index 0000000000..4b53de3c8a --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_cmatrix.hpp @@ -0,0 +1,65 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_cmatrix_hpp_ +#define _aer_framework_result_data_pybind_data_cmatrix_hpp_ + +#include "framework/results/data/mixins/data_cmatrix.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataCMatrix container object to a new Python dict +py::object to_python(AER::DataCMatrix &&data); + +// Move an DataCMatrix container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataCMatrix &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataCMatrix &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataCMatrix &&data) { + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_creg.hpp b/src/framework/results/data/mixins/pybind_data_creg.hpp new file mode 100755 index 0000000000..9585154c42 --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_creg.hpp @@ -0,0 +1,51 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_creg_hpp_ +#define _aer_framework_result_data_pybind_data_creg_hpp_ + +#include "framework/results/data/mixins/data_creg.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataCreg container object to a new Python dict +py::object to_python(AER::DataCreg &&data); + +// Move an DataCreg container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataCreg &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataCreg &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataCreg &&data) { + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_cvector.hpp b/src/framework/results/data/mixins/pybind_data_cvector.hpp new file mode 100755 index 0000000000..3115cc8b09 --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_cvector.hpp @@ -0,0 +1,57 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_cvector_hpp_ +#define _aer_framework_result_data_pybind_data_cvector_hpp_ + +#include "framework/results/data/mixins/data_cvector.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataCVector container object to a new Python dict +py::object to_python(AER::DataCVector &&data); + +// Move an DataCVector container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataCVector &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataCVector &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataCVector &&data) { + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_json.hpp b/src/framework/results/data/mixins/pybind_data_json.hpp new file mode 100755 index 0000000000..89759ed3ce --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_json.hpp @@ -0,0 +1,53 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_json_hpp_ +#define _aer_framework_result_data_pybind_data_json_hpp_ + +#include "framework/results/data/mixins/data_json.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataJSON container object to a new Python dict +py::object to_python(AER::DataJSON &&data); + +// Move an DataJSON container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataJSON &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataJSON &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataJSON &&data) { + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_rdict.hpp b/src/framework/results/data/mixins/pybind_data_rdict.hpp new file mode 100755 index 0000000000..03e2a0a30c --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_rdict.hpp @@ -0,0 +1,55 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_rdict_hpp_ +#define _aer_framework_result_data_pybind_data_rdict_hpp_ + +#include "framework/results/data/mixins/data_rdict.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataRDict container object to a new Python dict +py::object to_python(AER::DataRDict &&data); + +// Move an DataRDict container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataRDict &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataRDict &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataRDict &&data) { + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_rvalue.hpp b/src/framework/results/data/mixins/pybind_data_rvalue.hpp new file mode 100755 index 0000000000..15d9f18469 --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_rvalue.hpp @@ -0,0 +1,55 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_rvalue_hpp_ +#define _aer_framework_result_data_pybind_data_rvalue_hpp_ + +#include "framework/results/data/mixins/data_rvalue.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataRValue container object to a new Python dict +py::object to_python(AER::DataRValue &&data); + +// Move an DataRValue container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataRValue &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataRValue &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataRValue &&data) { + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); + AerToPy::add_to_python(pydata, static_cast&&>(data)); +} + +#endif diff --git a/src/framework/results/data/mixins/pybind_data_rvector.hpp b/src/framework/results/data/mixins/pybind_data_rvector.hpp new file mode 100755 index 0000000000..58ca61ec9d --- /dev/null +++ b/src/framework/results/data/mixins/pybind_data_rvector.hpp @@ -0,0 +1,55 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_result_data_pybind_data_rvector_hpp_ +#define _aer_framework_result_data_pybind_data_rvector_hpp_ + +#include "framework/results/data/mixins/data_rvector.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +//------------------------------------------------------------------------------ +// Aer C++ -> Python Conversion +//------------------------------------------------------------------------------ + +namespace AerToPy { + +// Move an DataRVector container object to a new Python dict +py::object to_python(AER::DataRVector &&data); + +// Move an DataRVector container object to an existing new Python dict +void add_to_python(py::dict &pydata, AER::DataRVector &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +py::object AerToPy::to_python(AER::DataRVector &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, std::move(data)); + return std::move(pydata); +} + +void AerToPy::add_to_python(py::dict &pydata, AER::DataRVector &&data) { + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 1>&&>(data)); + AerToPy::add_to_python(pydata, static_cast, 2>&&>(data)); +} + +#endif diff --git a/src/framework/results/data/pybind_data.hpp b/src/framework/results/data/pybind_data.hpp new file mode 100755 index 0000000000..f8b23f178b --- /dev/null +++ b/src/framework/results/data/pybind_data.hpp @@ -0,0 +1,55 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 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. + */ + +#ifndef _aer_framework_result_data_pybind_data_hpp_ +#define _aer_framework_result_data_pybind_data_hpp_ + +#include "framework/results/data/data.hpp" +#include "framework/results/data/mixins/pybind_data_creg.hpp" +#include "framework/results/data/mixins/pybind_data_rdict.hpp" +#include "framework/results/data/mixins/pybind_data_rvalue.hpp" +#include "framework/results/data/mixins/pybind_data_rvector.hpp" +#include "framework/results/data/mixins/pybind_data_cmatrix.hpp" +#include "framework/results/data/mixins/pybind_data_cvector.hpp" +#include "framework/results/data/mixins/pybind_data_cdict.hpp" +#include "framework/results/data/mixins/pybind_data_json.hpp" + + +namespace AerToPy { + +// Move an ExperimentResult data object to a Python dict +template <> py::object to_python(AER::Data &&data); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +template <> +py::object AerToPy::to_python(AER::Data &&data) { + py::dict pydata; + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + AerToPy::add_to_python(pydata, static_cast(data)); + return std::move(pydata); +} + +#endif diff --git a/src/framework/results/data/pybind_metadata.hpp b/src/framework/results/data/pybind_metadata.hpp new file mode 100755 index 0000000000..244083d931 --- /dev/null +++ b/src/framework/results/data/pybind_metadata.hpp @@ -0,0 +1,42 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 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. + */ + +#ifndef _aer_framework_result_data_pybind_metadata_hpp_ +#define _aer_framework_result_data_pybind_metadata_hpp_ + +#include "framework/results/data/metadata.hpp" +#include "framework/results/data/subtypes/pybind_data_map.hpp" + +namespace AerToPy { + +// Move an ExperimentResult metdata object to a Python dict +template <> py::object to_python(AER::Metadata &&metadata); + +} //end namespace AerToPy + + +//============================================================================ +// Implementations +//============================================================================ + +template <> +py::object AerToPy::to_python(AER::Metadata &&metadata) { + py::dict pydata; + add_to_python(pydata, static_cast&&>(metadata)); + add_to_python(pydata, static_cast&&>(metadata)); + add_to_python(pydata, static_cast&&>(metadata)); + return std::move(pydata); +} + +#endif diff --git a/src/framework/results/data/subtypes/accum_data.hpp b/src/framework/results/data/subtypes/accum_data.hpp new file mode 100755 index 0000000000..a424f8a107 --- /dev/null +++ b/src/framework/results/data/subtypes/accum_data.hpp @@ -0,0 +1,81 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_subtypes_accum_hpp_ +#define _aer_framework_results_data_subtypes_accum_hpp_ + +#include "framework/linalg/linalg.hpp" +#include "framework/results/data/subtypes/single_data.hpp" + +namespace AER { + +template +class AccumData : public SingleData { +using Base = SingleData; +public: + // Add data (copy) + void add(const T& data); + + // Add data (move) + void add(T&& data); + + // Combine data (move) + void combine(AccumData&& other); + + // Clear all stored data + void clear(); + +protected: + bool empty_ = true; +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +template +void AccumData::add(const T& data) { + if (empty_) { + Base::data_ = data; + empty_ = false; + } else { + Linalg::iadd(Base::data_, data); + } +} + +template +void AccumData::add(T&& data) { + if (empty_) { + Base::data_ = std::move(data); + empty_ = false; + } else { + Linalg::iadd(Base::data_, std::move(data)); + } +} + +template +void AccumData::combine(AccumData&& other) { + add(std::move(other.data_)); +} + +template +void AccumData::clear() { + Base::clear(); + empty_ = true; +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/subtypes/average_data.hpp b/src/framework/results/data/subtypes/average_data.hpp new file mode 100755 index 0000000000..fea9a7c858 --- /dev/null +++ b/src/framework/results/data/subtypes/average_data.hpp @@ -0,0 +1,114 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_subtypes_average_hpp_ +#define _aer_framework_results_data_subtypes_average_hpp_ + +#include "framework/results/data/subtypes/accum_data.hpp" + +namespace AER { + +template +class AverageData : public AccumData { +using Base = AccumData; +public: + // Access data + T& value(); + + // Add data (copy) + void add(const T& data); + + // Add data (move) + void add(T&& data); + + // Add data + void combine(AverageData&& other); + + // Clear all stored data + void clear(); + + // Divide accum by counts to convert to the normalized mean + void normalize(); + + // Multiply accum by counts to convert to the un-normalized mean + void denormalize(); + +protected: + // Number of datum that have been accumulated + size_t count_ = 0; + + // Flag for whether the accumulated data has been divided + // by the count + bool normalized_ = false; +}; + +//------------------------------------------------------------------------------ +// Implementation +//------------------------------------------------------------------------------ + +template +void AverageData::add(const T& data) { + denormalize(); + Base::add(data); + count_ += 1; +} + +template +void AverageData::add(T&& data) { + denormalize(); + Base::add(std::move(data)); + count_ += 1; +} + +template +void AverageData::combine(AverageData&& other) { + denormalize(); + other.denormalize(); + Base::combine(std::move(other)); + count_ += other.count_; +} + +template +void AverageData::clear() { + Base::clear(); + count_ = 0; + normalized_ = false; +} + +template +void AverageData::normalize() { + if (normalized_) + return; + Linalg::idiv(Base::data_, double(count_)); + normalized_ = true; +} + +template +void AverageData::denormalize() { + if (!normalized_) + return; + Linalg::imul(Base::data_, double(count_)); + normalized_ = false; +} + +template +T& AverageData::value() { + normalize(); + return Base::data_; +} + +//------------------------------------------------------------------------------ +} // end namespace AER +//------------------------------------------------------------------------------ +#endif diff --git a/src/framework/results/data/subtypes/data_map.hpp b/src/framework/results/data/subtypes/data_map.hpp new file mode 100755 index 0000000000..7f0d4fb0a6 --- /dev/null +++ b/src/framework/results/data/subtypes/data_map.hpp @@ -0,0 +1,220 @@ +/** + * This code is part of Qiskit. + * + * (C) Copyright IBM 2021. + * + * This code is licensed under the Apache License, Version 2.0. You may + * obtain a copy of this license in the LICENSE.txt file in the root directory + * of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. + * + * Any modifications or derivative works of this code must retain this + * copyright notice, and modified files need to carry a notice indicating + * that they have been altered from the originals. + */ + +#ifndef _aer_framework_results_data_map_hpp_ +#define _aer_framework_results_data_map_hpp_ + +#include "framework/json.hpp" +#include "framework/types.hpp" + +namespace AER { + +// Recursive nested data template class +template