├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── BUG_REPORT.yaml │ └── FEATURE_REQUEST.yaml ├── PULL_REQUEST_TEMPLATE.md ├── actions │ ├── install-main-dependencies │ │ └── action.yml │ ├── install-nature-pyscf │ │ └── action.yml │ └── run-tests │ │ └── action.yml └── workflows │ ├── deploy-code.yml │ ├── deploy-docs.yml │ └── main.yml ├── .gitignore ├── .mergify.yml ├── .pylintdict ├── .pylintrc ├── .stestr.conf ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.txt ├── MANIFEST.in ├── Makefile ├── README.md ├── constraints.txt ├── docs ├── Makefile ├── _templates │ └── autosummary │ │ ├── base.rst │ │ ├── class.rst │ │ ├── class_no_inherited_members.rst │ │ └── module.rst ├── apidocs │ └── qiskit_nature_pyscf.rst ├── conf.py ├── getting_started.rst ├── images │ └── logo.png ├── index.rst ├── lowercase_filter.py └── release_notes.rst ├── mypy.ini ├── pyproject.toml ├── qiskit_nature_pyscf ├── VERSION.txt ├── __init__.py ├── py.typed ├── pyscf_ground_state_solver.py ├── qiskit_solver.py └── version.py ├── releasenotes ├── config.yaml └── notes │ ├── 0.1 │ └── release-0.1.0-c2114e7d13fde810.yaml │ ├── 0.2 │ ├── compatibility-with-qiskit-nature-0.6-7f1189fe41833678.yaml │ ├── feat-pyscf-ground-state-solver-6a7866e29ca659f6.yaml │ └── support-python-3.11-dd380066953fafbb.yaml │ ├── 0.3 │ ├── drop-python-3.7-ee438003ebbd2645.yaml │ ├── proper-qiskit-density-0582f26cc9a669d9.yaml │ ├── support-multiple-roots-24f3119f247ab6f2.yaml │ └── use-qiskit-algorithms-89c7a3f72ca1ee9c.yaml │ └── 0.4 │ ├── compute-spin-square-18c5c4678551f7be.yaml │ └── python-3.12-951eab86dddf803c.yaml ├── requirements-dev.txt ├── requirements.txt ├── setup.py ├── test ├── __init__.py ├── nature_pyscf_test_case.py ├── test_pyscf_ground_state_solver.py ├── test_qiskit_solver.py └── test_readme_sample.py ├── tools ├── check_copyright.py ├── extract_deprecation.py ├── find_stray_release_notes.py ├── generate_spell_dict.py ├── ignore_untagged_notes.sh └── verify_headers.py └── tox.ini /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This file defines the persons who will be assigned as reviewers for PRs that 2 | # modify particular files in the repo. The PR can be merged when approved by at 3 | # least one codeowner. However, all Qiskit team members can (and should!) review the PRs. 4 | 5 | # Global rule, unless specialized by a later one 6 | * @mrossinek @woodsp-ibm 7 | 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG_REPORT.yaml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug report 2 | description: Create a report to help us improve 🤔. 3 | labels: ["bug"] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thank you for reporting! Please also use the search to see if there are any other relevant issues or pull requests. 9 | 10 | - type: textarea 11 | attributes: 12 | label: Environment 13 | description: For the version of Qiskit Nature PySCF, please give the actual version number (_e.g._ 0.18.3) if you are using a release version, or the first 7-8 characters of the commit hash if you have installed from `git`. If anything else is relevant, you can add it to the list. 14 | # The trailing spaces on the following lines are to make filling the form 15 | # in easier. The type is 'textarea' rather than three separate 'input's 16 | # to make the resulting issue body less noisy with headings. 17 | value: | 18 | - **Qiskit Nature PySCF version**: 19 | - **Python version**: 20 | - **Operating system**: 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | attributes: 26 | label: What is happening? 27 | description: A short description of what is going wrong, in words. 28 | validations: 29 | required: true 30 | 31 | - type: textarea 32 | attributes: 33 | label: How can we reproduce the issue? 34 | description: Give some steps that show the bug. A [minimal working example](https://stackoverflow.com/help/minimal-reproducible-example) of code with output is best. If you are copying in code, please remember to enclose it in triple backticks (` ``` [multiline code goes here] ``` `) so that it [displays correctly](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). 35 | validations: 36 | required: true 37 | 38 | - type: textarea 39 | attributes: 40 | label: What should happen? 41 | description: A short description, including about the expected output of any code in the previous section. 42 | validations: 43 | required: true 44 | 45 | - type: textarea 46 | attributes: 47 | label: Any suggestions? 48 | description: Not required, but if you have suggestions for how a contributor should fix this, or any problems we should be aware of, let us know. 49 | validations: 50 | required: false 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yaml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature request 2 | description: Suggest an idea for this project 💡! 3 | labels: ["type: feature request"] 4 | 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Please make sure to browse the opened and closed issues to make sure that this idea has not previously been discussed. 9 | 10 | - type: textarea 11 | attributes: 12 | label: What should we add? 13 | validations: 14 | required: true 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 11 | 12 | ### Summary 13 | 14 | 15 | 16 | ### Details and comments 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/actions/install-main-dependencies/action.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: 'Install Qiskit Nature PySCF Main Dependencies' 14 | description: 'Installs Python dependencies from Main' 15 | inputs: 16 | os: 17 | description: 'OS' 18 | required: true 19 | python-version: 20 | description: 'Python version' 21 | required: true 22 | qiskit-main: 23 | description: 'Use Qiskit main' 24 | required: true 25 | nature-main: 26 | description: 'Use Nature main' 27 | required: true 28 | runs: 29 | using: "composite" 30 | steps: 31 | - name: Get main last commit ids 32 | run: | 33 | echo "QISKIT_HASH=$(git ls-remote --heads https://github.com/Qiskit/qiskit.git refs/heads/main | awk '{print $1}')" >> $GITHUB_ENV 34 | echo "NATURE_HASH=$(git ls-remote --heads https://github.com/Qiskit/qiskit-nature.git refs/heads/main | awk '{print $1}')" >> $GITHUB_ENV 35 | shell: bash 36 | - name: Qiskit Cache 37 | env: 38 | CACHE_VERSION: v1 39 | id: qiskit-cache 40 | uses: actions/cache@v3 41 | with: 42 | path: qiskit-cache 43 | key: qiskit-cache-${{ inputs.os }}-${{ inputs.python-version }}-${{ env.QISKIT_HASH }}-${{ env.CACHE_VERSION }} 44 | - name: Nature Cache 45 | env: 46 | CACHE_VERSION: v1 47 | id: nature-cache 48 | uses: actions/cache@v3 49 | with: 50 | path: nature-cache 51 | key: nature-cache-${{ inputs.os }}-${{ inputs.python-version }}-${{ env.NATURE_HASH }}-${{ env.CACHE_VERSION }} 52 | - name: Install Qiskit 53 | env: 54 | MACOSX_DEPLOYMENT_TARGET: 10.15 55 | run: | 56 | if [ "${{ inputs.qiskit-main }}" == "true" ]; then 57 | echo 'Install Qiskit from Main' 58 | BASE_DIR=qiskit-cache 59 | build_from_main=true 60 | cache_hit=${{ steps.qiskit-cache.outputs.cache-hit }} 61 | echo "cache hit: ${cache_hit}" 62 | if [ "$cache_hit" == "true" ]; then 63 | pip_result=0 64 | pushd "${BASE_DIR}" 65 | python -m pip install *.whl && pip_result=$? || pip_result=$? 66 | popd 67 | if [ $pip_result == 0 ]; then 68 | build_from_main=false 69 | fi 70 | else 71 | mkdir -p ${BASE_DIR} 72 | fi 73 | if [ "$build_from_main" == "true" ]; then 74 | echo 'Create wheel file from main' 75 | pip install -U wheel setuptools_rust 76 | git clone --depth 1 --branch main https://github.com/Qiskit/qiskit.git /tmp/qiskit 77 | pushd /tmp/qiskit 78 | python setup.py bdist_wheel 79 | popd 80 | cp -rf /tmp/qiskit/dist/*.whl "${BASE_DIR}" 81 | pushd "${BASE_DIR}" 82 | python -m pip install *.whl 83 | popd 84 | pip uninstall -y setuptools_rust 85 | fi 86 | else 87 | echo 'Install Qiskit from Stable' 88 | pip install -U qiskit 89 | fi 90 | shell: bash 91 | - name: Install Nature from Main 92 | run: | 93 | if [ "${{ inputs.nature-main }}" == "true" ]; then 94 | echo 'Install Nature from Main' 95 | BASE_DIR=nature-cache 96 | build_from_main=true 97 | cache_hit=${{ steps.nature-cache.outputs.cache-hit }} 98 | echo "cache hit: ${cache_hit}" 99 | if [ "$cache_hit" == "true" ]; then 100 | pip_result=0 101 | pushd "${BASE_DIR}" 102 | python -m pip install *.whl && pip_result=$? || pip_result=$? 103 | popd 104 | if [ $pip_result == 0 ]; then 105 | build_from_main=false 106 | fi 107 | else 108 | mkdir -p ${BASE_DIR} 109 | fi 110 | if [ "$build_from_main" == "true" ]; then 111 | echo 'Create wheel file from main' 112 | pip install -U wheel 113 | git clone --depth 1 --branch main https://github.com/Qiskit/qiskit-nature.git /tmp/qiskit-nature 114 | pushd /tmp/qiskit-nature 115 | python setup.py bdist_wheel 116 | popd 117 | cp -rf /tmp/qiskit-nature/dist/*.whl "${BASE_DIR}" 118 | pushd "${BASE_DIR}" 119 | python -m pip install *.whl 120 | popd 121 | fi 122 | else 123 | echo 'Install Nature from Stable' 124 | pip install -U qiskit-nature 125 | fi 126 | shell: bash 127 | -------------------------------------------------------------------------------- /.github/actions/install-nature-pyscf/action.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: 'Install Qiskit Nature PySCF' 14 | description: 'Installs Qiskit Nature PySCF on developer mode' 15 | inputs: 16 | os: 17 | description: 'OS' 18 | required: true 19 | runs: 20 | using: "composite" 21 | steps: 22 | - run : | 23 | pip install -U -c constraints.txt -r requirements-dev.txt -r requirements.txt 24 | pip install -e . 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/actions/run-tests/action.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: 'Run Unit Tests' 14 | description: 'Run Unit Tests' 15 | inputs: 16 | os: 17 | description: 'OS' 18 | required: true 19 | event-name: 20 | description: 'Actions event' 21 | required: true 22 | run-slow: 23 | description: 'Run slow tests or not' 24 | required: true 25 | python-version: 26 | description: 'Python version' 27 | required: true 28 | runs: 29 | using: "composite" 30 | steps: 31 | - name: Run Unit Tests 32 | env: 33 | PYTHONWARNINGS: default 34 | run: | 35 | # run slow tests only on scheduled event or if input flag is set 36 | if [ "${{ inputs.event-name }}" == "schedule" ] || [ "${{ inputs.run-slow }}" == "true" ]; then 37 | export QISKIT_TESTS="run_slow" 38 | fi 39 | if [ "${{ inputs.os }}" == "ubuntu-latest" ] && [ "${{ inputs.python-version }}" == "3.8" ]; then 40 | export PYTHON="coverage3 run --source qiskit_nature_pyscf --parallel-mode" 41 | fi 42 | stestr --test-path test run 2> >(tee /dev/stderr out.txt > /dev/null) 43 | shell: bash 44 | -------------------------------------------------------------------------------- /.github/workflows/deploy-code.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: Deploy Code 14 | 15 | on: 16 | push: 17 | tags: 18 | - '*' 19 | 20 | jobs: 21 | code_publish: 22 | runs-on: ubuntu-latest 23 | environment: release 24 | permissions: 25 | id-token: write 26 | strategy: 27 | matrix: 28 | python-version: [3.8] 29 | steps: 30 | - uses: actions/checkout@v3 31 | - uses: actions/setup-python@v4 32 | with: 33 | python-version: ${{ matrix.python-version }} 34 | - name: Install deps 35 | run: pip install -U pip setuptools virtualenv wheel 36 | - name: Build sdist 37 | run: python3 setup.py sdist bdist_wheel 38 | - uses: actions/upload-artifact@v4 39 | with: 40 | path: ./dist/* 41 | - name: Deploy to Pypi 42 | uses: pypa/gh-action-pypi-publish@release/v1 43 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: Deploy Docs 14 | 15 | on: 16 | workflow_dispatch: 17 | 18 | concurrency: 19 | group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | docs_build: 24 | if: ${{ contains('["mrossinek", "woodsp-ibm"]', github.actor) }} 25 | runs-on: ubuntu-latest 26 | strategy: 27 | matrix: 28 | python-version: [3.8] 29 | steps: 30 | - uses: actions/checkout@v3 31 | with: 32 | fetch-depth: 0 33 | - uses: actions/setup-python@v4 34 | with: 35 | python-version: ${{ matrix.python-version }} 36 | cache: 'pip' 37 | cache-dependency-path: | 38 | requirements.txt 39 | requirements-dev.txt 40 | - uses: ./.github/actions/install-main-dependencies 41 | with: 42 | os: ${{ matrix.os }} 43 | python-version: ${{ matrix.python-version }} 44 | if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }} 45 | - name: Install Dependencies 46 | run: | 47 | pip install jupyter qiskit[visualization] 48 | sudo apt-get install -y pandoc graphviz 49 | shell: bash 50 | - uses: ./.github/actions/install-nature-pyscf 51 | with: 52 | os: ${{ matrix.os }} 53 | - name: Build and publish 54 | env: 55 | QISKIT_PARALLEL: False 56 | QISKIT_DOCS_BUILD_TUTORIALS: 'always' 57 | run: | 58 | echo "earliest_version: 0.1.0" >> releasenotes/config.yaml 59 | tools/ignore_untagged_notes.sh 60 | make html 61 | shell: bash 62 | - name: Run upload documentation artifacts 63 | uses: actions/upload-artifact@v4 64 | with: 65 | name: page 66 | path: docs/_build/html 67 | deploy: 68 | runs-on: ubuntu-latest 69 | needs: docs_build 70 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 71 | permissions: 72 | pages: write # to deploy to Pages 73 | id-token: write # to verify the deployment originates from an appropriate source 74 | environment: 75 | name: github-pages 76 | url: ${{steps.deployment.outputs.page_url}} 77 | steps: 78 | - uses: actions/download-artifact@v4 79 | with: 80 | name: page 81 | path: . 82 | - uses: actions/configure-pages@v1 83 | - uses: actions/upload-pages-artifact@v1 84 | with: 85 | path: . 86 | - name: Deploy to GitHub Pages 87 | id: deployment 88 | uses: actions/deploy-pages@v1 89 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | name: Qiskit Nature PySCF Unit Tests 14 | 15 | on: 16 | push: 17 | branches: 18 | - main 19 | - 'stable/**' 20 | pull_request: 21 | branches: 22 | - main 23 | - 'stable/**' 24 | schedule: 25 | # run every day at 2AM 26 | - cron: '0 2 * * *' 27 | 28 | concurrency: 29 | group: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }} 30 | cancel-in-progress: true 31 | 32 | jobs: 33 | Checks: 34 | if: github.repository_owner == 'qiskit-community' 35 | runs-on: ${{ matrix.os }} 36 | strategy: 37 | matrix: 38 | os: [ubuntu-latest] 39 | python-version: [3.8] 40 | steps: 41 | - name: Print Concurrency Group 42 | env: 43 | CONCURRENCY_GROUP: ${{ github.repository }}-${{ github.ref }}-${{ github.head_ref }}-${{ github.workflow }} 44 | run: | 45 | echo -e "\033[31;1;4mConcurrency Group\033[0m" 46 | echo -e "$CONCURRENCY_GROUP\n" 47 | shell: bash 48 | - uses: actions/checkout@v3 49 | with: 50 | fetch-depth: 0 51 | - uses: actions/setup-python@v4 52 | with: 53 | python-version: ${{ matrix.python-version }} 54 | cache: 'pip' 55 | cache-dependency-path: | 56 | requirements.txt 57 | requirements-dev.txt 58 | - uses: ./.github/actions/install-main-dependencies 59 | with: 60 | os: ${{ matrix.os }} 61 | python-version: ${{ matrix.python-version }} 62 | if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }} 63 | - uses: ./.github/actions/install-nature-pyscf 64 | with: 65 | os: ${{ matrix.os }} 66 | - name: Install Dependencies 67 | run: | 68 | pip install jupyter qiskit[visualization] 69 | sudo apt-get -y install pandoc graphviz 70 | sudo apt-get -y install python3-enchant 71 | sudo apt-get -y install hunspell-en-us 72 | pip install pyenchant 73 | # append to reno config 74 | echo "earliest_version: 0.1.0" >> releasenotes/config.yaml 75 | shell: bash 76 | - run: pip check 77 | if: ${{ !cancelled() }} 78 | shell: bash 79 | - name: Copyright Check 80 | run: | 81 | python tools/check_copyright.py -check 82 | if: ${{ !cancelled() }} 83 | shell: bash 84 | - run: make spell 85 | if: ${{ !cancelled() }} 86 | shell: bash 87 | - name: Style Check 88 | run: | 89 | make clean_sphinx 90 | make style 91 | if: ${{ !cancelled() }} 92 | shell: bash 93 | - name: Run make html 94 | run: | 95 | make clean_sphinx 96 | make html 97 | cd docs/_build/html 98 | mkdir artifacts 99 | tar -zcvf artifacts/documentation.tar.gz --exclude=./artifacts . 100 | if: ${{ !cancelled() }} 101 | shell: bash 102 | - name: Run upload documentation 103 | uses: actions/upload-artifact@v4 104 | with: 105 | name: documentation 106 | path: docs/_build/html/artifacts/documentation.tar.gz 107 | if: ${{ !cancelled() }} 108 | - run: make doctest 109 | if: ${{ !cancelled() }} 110 | shell: bash 111 | Nature_PySCF: 112 | if: github.repository_owner == 'qiskit-community' 113 | runs-on: ${{ matrix.os }} 114 | strategy: 115 | fail-fast: false 116 | matrix: 117 | os: [ubuntu-latest] 118 | python-version: [3.8, 3.9, '3.10', 3.11, 3.12] 119 | include: 120 | - os: macos-latest 121 | python-version: 3.8 122 | - os: macos-latest 123 | python-version: 3.12 124 | steps: 125 | - uses: actions/checkout@v3 126 | - uses: actions/setup-python@v4 127 | with: 128 | python-version: ${{ matrix.python-version }} 129 | cache: 'pip' 130 | cache-dependency-path: | 131 | requirements.txt 132 | requirements-dev.txt 133 | - uses: ./.github/actions/install-main-dependencies 134 | with: 135 | os: ${{ matrix.os }} 136 | python-version: ${{ matrix.python-version }} 137 | qiskit-main: "false" 138 | nature-main: "false" 139 | if: ${{ !startsWith(github.ref, 'refs/heads/stable') && !startsWith(github.base_ref, 'stable/') }} 140 | - uses: ./.github/actions/install-nature-pyscf 141 | with: 142 | os: ${{ matrix.os }} 143 | - name: Run lint 144 | run: | 145 | make lint 146 | shell: bash 147 | - name: Run mypy 148 | run: | 149 | make mypy 150 | if: ${{ !cancelled() }} 151 | shell: bash 152 | - name: Stestr Cache 153 | uses: actions/cache@v3 154 | with: 155 | path: | 156 | .stestr 157 | key: stestr-${{ matrix.os }}-${{ matrix.python-version }}-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }} 158 | restore-keys: | 159 | stestr-${{ matrix.os }}-${{ matrix.python-version }}- 160 | stestr-${{ matrix.os }}- 161 | if: ${{ !cancelled() }} 162 | - name: Qiskit Nature PySCF Unit Tests under Python ${{ matrix.python-version }} 163 | uses: ./.github/actions/run-tests 164 | with: 165 | os: ${{ matrix.os }} 166 | event-name: ${{ github.event_name }} 167 | run-slow: ${{ contains(github.event.pull_request.labels.*.name, 'run_slow') }} 168 | python-version: ${{ matrix.python-version }} 169 | if: ${{ !cancelled() }} 170 | - name: Deprecation Messages 171 | run: | 172 | mkdir ./ci-artifact-data 173 | python tools/extract_deprecation.py -file out.txt -output ./ci-artifact-data/fin.dep 174 | shell: bash 175 | - name: Coverage combine 176 | run: | 177 | coverage3 combine 178 | mv .coverage ./ci-artifact-data/fin.dat 179 | if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version == 3.8 }} 180 | shell: bash 181 | - uses: actions/upload-artifact@v4 182 | with: 183 | name: ${{ matrix.os }}-${{ matrix.python-version }} 184 | path: ./ci-artifact-data/* 185 | Deprecation_Messages_and_Coverage: 186 | if: github.repository_owner == 'qiskit-community' 187 | needs: [Checks, Nature_PySCF] 188 | runs-on: ubuntu-latest 189 | strategy: 190 | matrix: 191 | python-version: [3.8] 192 | steps: 193 | - uses: actions/checkout@v3 194 | - uses: actions/setup-python@v4 195 | with: 196 | python-version: ${{ matrix.python-version }} 197 | - uses: actions/download-artifact@v4 198 | with: 199 | name: ubuntu-latest-3.8 200 | path: /tmp/f38 201 | - uses: actions/download-artifact@v4 202 | with: 203 | name: ubuntu-latest-3.9 204 | path: /tmp/f39 205 | - uses: actions/download-artifact@v4 206 | with: 207 | name: ubuntu-latest-3.10 208 | path: /tmp/f310 209 | - uses: actions/download-artifact@v4 210 | with: 211 | name: ubuntu-latest-3.11 212 | path: /tmp/f311 213 | - uses: actions/download-artifact@v4 214 | with: 215 | name: ubuntu-latest-3.12 216 | path: /tmp/f312 217 | - uses: actions/download-artifact@v4 218 | with: 219 | name: macos-latest-3.8 220 | path: /tmp/m38 221 | - uses: actions/download-artifact@v4 222 | with: 223 | name: macos-latest-3.12 224 | path: /tmp/m312 225 | - name: Install Dependencies 226 | run: pip install -U coverage coveralls diff-cover 227 | shell: bash 228 | - name: Combined Deprecation Messages 229 | run: | 230 | sort -f -u /tmp/f38/fin.dep /tmp/f39/fin.dep /tmp/f310/fin.dep /tmp/f311/fin.dep /tmp/f312/fin.dep /tmp/m38/fin.dep /tmp/m312/fin.dep || true 231 | shell: bash 232 | - name: Coverage combine 233 | run: coverage3 combine /tmp/f38/fin.dat 234 | shell: bash 235 | - name: Upload to Coveralls 236 | env: 237 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 238 | run: coveralls --service=github 239 | shell: bash 240 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # MacOSX 2 | .DS_Store 3 | 4 | # Dolphin KDE 5 | .directory 6 | 7 | # editor files 8 | .vscode/ 9 | .idea/ 10 | 11 | # Standard python ignores follow 12 | 13 | # Byte-compiled / optimized / DLL files 14 | __pycache__/ 15 | *.py[cod] 16 | *$py.class 17 | 18 | # C extensions 19 | *.so 20 | *.so.dSYM 21 | *.dll 22 | 23 | # Distribution / packaging 24 | .Python 25 | env/ 26 | build/ 27 | develop-eggs/ 28 | dist/ 29 | downloads/ 30 | eggs/ 31 | .eggs/ 32 | parts/ 33 | sdist/ 34 | var/ 35 | wheels/ 36 | *.egg-info/ 37 | .installed.cfg 38 | *.egg 39 | MANIFEST 40 | install/ 41 | 42 | # PyInstaller 43 | # Usually these files are written by a python script from a template 44 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 45 | *.manifest 46 | *.spec 47 | 48 | # Installer logs 49 | pip-log.txt 50 | pip-delete-this-directory.txt 51 | 52 | # Unit test / coverage reports 53 | htmlcov/ 54 | .tox/ 55 | .coverage 56 | .coverage.* 57 | .cache 58 | nosetests.xml 59 | coverage.xml 60 | *,cover 61 | .hypothesis/ 62 | .pytest_cache/ 63 | test/**/*.log 64 | test/**/*.pdf 65 | test/**/*.prof 66 | test/**/*.npz 67 | 68 | # Translations 69 | *.mo 70 | *.pot 71 | 72 | # Django stuff: 73 | *.log 74 | .static_storage/ 75 | .media/ 76 | local_settings.py 77 | 78 | # Flask stuff: 79 | instance/ 80 | .webassets-cache 81 | 82 | # Scrapy stuff: 83 | .scrapy 84 | 85 | # Sphinx documentation 86 | */_build/ 87 | docs/api/* 88 | docs/stubs/* 89 | 90 | # PyBuilder 91 | target/ 92 | 93 | # Jupyter Notebook 94 | .ipynb_checkpoints 95 | 96 | # pyenv 97 | .python-version 98 | 99 | # celery beat schedule file 100 | celerybeat-schedule 101 | 102 | # SageMath parsed files 103 | *.sage.py 104 | 105 | # Environments 106 | .env 107 | .venv 108 | env/ 109 | venv/ 110 | ENV/ 111 | env.bak/ 112 | venv.bak/ 113 | 114 | # Spyder project settings 115 | .spyderproject 116 | .spyproject 117 | 118 | # Rope project settings 119 | .ropeproject 120 | 121 | # mkdocs documentation 122 | /site 123 | 124 | # mypy 125 | .mypy_cache/ 126 | 127 | # stestr repostiory 128 | .stestr/ 129 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | queue_rules: 2 | - name: automerge 3 | conditions: 4 | - check-success=Deprecation_Messages_and_Coverage (3.8) 5 | 6 | pull_request_rules: 7 | - name: automatic merge on CI success and review 8 | conditions: 9 | - check-success=Deprecation_Messages_and_Coverage (3.8) 10 | - "#approved-reviews-by>=1" 11 | - label=automerge 12 | - label!=on hold 13 | actions: 14 | queue: 15 | name: automerge 16 | method: squash 17 | - name: backport 18 | conditions: 19 | - label=stable backport potential 20 | actions: 21 | backport: 22 | branches: 23 | - stable/0.4 24 | -------------------------------------------------------------------------------- /.pylintdict: -------------------------------------------------------------------------------- 1 | ansatz 2 | args 3 | autosummary 4 | bool 5 | cas 6 | ci 7 | currentmodule 8 | dicts 9 | ecore 10 | eigensolver 11 | fci 12 | fcisolver 13 | filename 14 | formatter 15 | github 16 | gto 17 | hamiltonian 18 | https 19 | kwargs 20 | makefile 21 | methodtype 22 | mcscf 23 | mol 24 | nalpha 25 | nbeta 26 | ndarray 27 | nelec 28 | norb 29 | nosignatures 30 | np 31 | optimizers 32 | pxd 33 | py 34 | pyscf 35 | qiskit 36 | qiskit's 37 | qubit 38 | readme 39 | reinstall 40 | rhf 41 | scf 42 | scipy 43 | stdout 44 | str 45 | toctree 46 | tuple 47 | tuples 48 | vec 49 | vqe 50 | wavefunction 51 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # Specify a configuration file. 4 | #rcfile= 5 | 6 | # Python code to execute, usually for sys.path manipulation such as 7 | # pygtk.require(). 8 | # Changed to fix recursion crash since pandas 1.1.5 9 | init-hook='import sys; sys.setrecursionlimit(8 * sys.getrecursionlimit())' 10 | 11 | # Add files or directories to the blacklist. They should be base names, not 12 | # paths. 13 | ignore=CVS 14 | 15 | # Add files or directories matching the regex patterns to the blacklist. The 16 | # regex matches against base names, not paths. 17 | ignore-patterns= 18 | 19 | # Pickle collected data for later comparisons. 20 | persistent=yes 21 | 22 | # List of plugins (as comma separated values of python modules names) to load, 23 | # usually to register additional checkers. 24 | load-plugins=pylint.extensions.docparams, # enable checking of docstring args 25 | pylint.extensions.docstyle, # basic docstring style checks 26 | 27 | # Use multiple processes to speed up Pylint. 28 | jobs=1 29 | 30 | # Allow loading of arbitrary C extensions. Extensions are imported into the 31 | # active Python interpreter and may run arbitrary code. 32 | unsafe-load-any-extension=no 33 | 34 | # A comma-separated list of package or module names from where C extensions may 35 | # be loaded. Extensions are loading into the active Python interpreter and may 36 | # run arbitrary code 37 | extension-pkg-whitelist= 38 | 39 | 40 | [MESSAGES CONTROL] 41 | 42 | # Only show warnings with the listed confidence levels. Leave empty to show 43 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED 44 | confidence= 45 | 46 | # Enable the message, report, category or checker with the given id(s). You can 47 | # either give multiple identifier separated by comma (,) or put this option 48 | # multiple time (only on the command line, not in the configuration file where 49 | # it should appear only once). See also the "--disable" option for examples. 50 | #enable= 51 | 52 | # Disable the message, report, category or checker with the given id(s). You 53 | # can either give multiple identifiers separated by comma (,) or put this 54 | # option multiple times (only on the command line, not in the configuration 55 | # file where it should appear only once).You can also use "--disable=all" to 56 | # disable everything first and then reenable specific checks. For example, if 57 | # you want to run only the similarities checker, you can use "--disable=all 58 | # --enable=similarities". If you want to run only the classes checker, but have 59 | # no Warning level messages displayed, use"--disable=all --enable=classes 60 | # --disable=W" 61 | disable=fixme, # disabled as TODOs would show up as warnings 62 | protected-access, # disabled as we don't follow the public vs private 63 | # convention strictly 64 | duplicate-code, # disabled as it is too verbose 65 | redundant-returns-doc, # for @abstractmethod, it cannot interpret "pass" 66 | # disable the "too-many/few-..." refactoring hints 67 | too-many-lines, too-many-branches, too-many-locals, too-many-nested-blocks, 68 | too-many-statements, too-many-instance-attributes, too-many-arguments, 69 | too-many-public-methods, too-few-public-methods, too-many-ancestors, 70 | unnecessary-pass, # allow for methods with just "pass", for clarity 71 | no-else-return, # relax "elif" after a clause with a return 72 | docstring-first-line-empty, # relax docstring style 73 | import-outside-toplevel, 74 | 75 | 76 | [REPORTS] 77 | 78 | # Set the output format. Available formats are text, parseable, colorized, msvs 79 | # (visual studio) and html. You can also give a reporter class, eg 80 | # mypackage.mymodule.MyReporterClass. 81 | output-format=text 82 | 83 | # Tells whether to display a full report or only the messages 84 | reports=yes 85 | 86 | # Python expression which should return a note less than 10 (10 is the highest 87 | # note). You have access to the variables errors warning, statement which 88 | # respectively contain the number of errors / warnings messages and the total 89 | # number of statements analyzed. This is used by the global evaluation report 90 | # (RP0004). 91 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 92 | 93 | # Template used to display messages. This is a python new-style format string 94 | # used to format the message information. See doc for all details 95 | #msg-template= 96 | 97 | 98 | [BASIC] 99 | 100 | # Good variable names which should always be accepted, separated by a comma 101 | # i,j,k = typical indices 102 | # n,m = typical numbers 103 | # ex = for exceptions and errors 104 | # v,w = typical vectors 105 | # x,y,z = typical axes 106 | # _ = placeholder name 107 | # q,r,qr,cr,qc = quantum and classical registers, and quantum circuit 108 | # pi = the PI constant 109 | # op = operation iterator 110 | # b = basis iterator 111 | good-names=i,j,k,n,m,ex,v,w,x,y,z,Run,_,logger,q,c,r,qr,cr,qc,nd,pi,op,b,ar,br,a,mu, 112 | __unittest,iSwapGate 113 | 114 | # Bad variable names which should always be refused, separated by a comma 115 | bad-names=foo,bar,toto,tutu,tata 116 | 117 | # Colon-delimited sets of names that determine each other's naming style when 118 | # the name regexes allow several styles. 119 | name-group= 120 | 121 | # Include a hint for the correct naming format with invalid-name 122 | include-naming-hint=no 123 | 124 | # List of decorators that produce properties, such as abc.abstractproperty. Add 125 | # to this list to register other decorators that produce valid properties. 126 | property-classes=abc.abstractproperty 127 | 128 | # Regular expression matching correct module names 129 | module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ 130 | 131 | # Regular expression matching correct constant names 132 | const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ 133 | 134 | # Regular expression matching correct class names 135 | class-rgx=[A-Z_][a-zA-Z0-9]+$ 136 | 137 | # Regular expression matching correct function names 138 | function-rgx=[a-z_][a-z0-9_]{2,30}$ 139 | 140 | # Regular expression matching correct method names 141 | method-rgx=(([a-z_][a-z0-9_]{2,49})|(assert[A-Z][a-zA-Z0-9]{2,43})|(test_[_a-zA-Z0-9]{2,}))$ 142 | 143 | # Regular expression matching correct attribute names 144 | attr-rgx=[a-z_][a-z0-9_]{2,30}$ 145 | 146 | # Regular expression matching correct argument names 147 | argument-rgx=[a-z_][a-z0-9_]{2,30}|ax|dt$ 148 | 149 | # Regular expression matching correct variable names 150 | variable-rgx=[a-z_][a-z0-9_]{2,30}$ 151 | 152 | # Regular expression matching correct class attribute names 153 | class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ 154 | 155 | # Regular expression matching correct inline iteration names 156 | inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ 157 | 158 | # Regular expression which should only match function or class names that do 159 | # not require a docstring. 160 | no-docstring-rgx=^_ 161 | 162 | # Minimum line length for functions/classes that require docstrings, shorter 163 | # ones are exempt. 164 | docstring-min-length=-1 165 | 166 | 167 | [ELIF] 168 | 169 | # Maximum number of nested blocks for function / method body 170 | max-nested-blocks=5 171 | 172 | 173 | [FORMAT] 174 | 175 | # Maximum number of characters on a single line. 176 | max-line-length=105 177 | 178 | # Regexp for a line that is allowed to be longer than the limit. 179 | ignore-long-lines=^\s*(# )??$ 180 | 181 | # Allow the body of an if to be on the same line as the test if there is no 182 | # else. 183 | single-line-if-stmt=no 184 | 185 | # Maximum number of lines in a module 186 | max-module-lines=1000 187 | 188 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 189 | # tab). 190 | indent-string=' ' 191 | 192 | # Number of spaces of indent required inside a hanging or continued line. 193 | indent-after-paren=4 194 | 195 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 196 | expected-line-ending-format= 197 | 198 | 199 | [LOGGING] 200 | 201 | # Logging modules to check that the string format arguments are in logging 202 | # function parameter format 203 | logging-modules=logging 204 | 205 | 206 | [MISCELLANEOUS] 207 | 208 | # List of note tags to take in consideration, separated by a comma. 209 | notes=FIXME,XXX,TODO 210 | 211 | 212 | [SIMILARITIES] 213 | 214 | # Minimum lines number of a similarity. 215 | min-similarity-lines=4 216 | 217 | # Ignore comments when computing similarities. 218 | ignore-comments=yes 219 | 220 | # Ignore docstrings when computing similarities. 221 | ignore-docstrings=yes 222 | 223 | # Ignore imports when computing similarities. 224 | ignore-imports=no 225 | 226 | 227 | [SPELLING] 228 | 229 | # Spelling dictionary name. Available dictionaries: none. To make it working 230 | # install python-enchant package. 231 | spelling-dict= 232 | 233 | # List of comma separated words that should not be checked. 234 | spelling-ignore-words= 235 | 236 | # A path to a file that contains private dictionary; one word per line. 237 | spelling-private-dict-file= 238 | 239 | # Tells whether to store unknown words to indicated private dictionary in 240 | # --spelling-private-dict-file option instead of raising a message. 241 | spelling-store-unknown-words=no 242 | 243 | 244 | [TYPECHECK] 245 | 246 | # Tells whether missing members accessed in mixin class should be ignored. A 247 | # mixin class is detected if its name ends with "mixin" (case insensitive). 248 | ignore-mixin-members=yes 249 | 250 | # List of module names for which member attributes should not be checked 251 | # (useful for modules/projects where namespaces are manipulated during runtime 252 | # and thus existing member attributes cannot be deduced by static analysis. It 253 | # supports qualified module names, as well as Unix pattern matching. 254 | ignored-modules=matplotlib.cm,numpy.random 255 | 256 | # List of class names for which member attributes should not be checked (useful 257 | # for classes with dynamically set attributes). This supports the use of 258 | # qualified names. 259 | ignored-classes=optparse.Values,thread._local,_thread._local,QuantumCircuit 260 | 261 | # List of members which are set dynamically and missed by pylint inference 262 | # system, and so shouldn't trigger E1101 when accessed. Python regular 263 | # expressions are accepted. 264 | generated-members= 265 | 266 | # List of decorators that produce context managers, such as 267 | # contextlib.contextmanager. Add to this list to register other decorators that 268 | # produce valid context managers. 269 | contextmanager-decorators=contextlib.contextmanager 270 | 271 | 272 | [VARIABLES] 273 | 274 | # Tells whether we should check for unused import in __init__ files. 275 | init-import=no 276 | 277 | # A regular expression matching the name of dummy variables (i.e. expectedly 278 | # not used). 279 | dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy 280 | 281 | # List of additional names supposed to be defined in builtins. Remember that 282 | # you should avoid to define new builtins when possible. 283 | additional-builtins= 284 | 285 | # List of strings which can identify a callback function by name. A callback 286 | # name must start or end with one of those strings. 287 | callbacks=cb_,_cb 288 | 289 | # List of qualified module names which can have objects that can redefine 290 | # builtins. 291 | redefining-builtins-modules=six.moves,future.builtins 292 | 293 | 294 | [CLASSES] 295 | 296 | # List of method names used to declare (i.e. assign) instance attributes. 297 | defining-attr-methods=__init__,__new__,setUp 298 | 299 | # List of valid names for the first argument in a class method. 300 | valid-classmethod-first-arg=cls 301 | 302 | # List of valid names for the first argument in a metaclass class method. 303 | valid-metaclass-classmethod-first-arg=mcs 304 | 305 | # List of member names, which should be excluded from the protected access 306 | # warning. 307 | exclude-protected=_asdict,_fields,_replace,_source,_make 308 | 309 | 310 | [DESIGN] 311 | 312 | # Maximum number of arguments for function / method 313 | max-args=8 314 | 315 | # Argument names that match this expression will be ignored. Default to name 316 | # with leading underscore 317 | ignored-argument-names=_.* 318 | 319 | # Maximum number of locals for function / method body 320 | max-locals=15 321 | 322 | # Maximum number of return / yield for function / method body 323 | max-returns=6 324 | 325 | # Maximum number of branch for function / method body 326 | max-branches=12 327 | 328 | # Maximum number of statements in function / method body 329 | max-statements=50 330 | 331 | # Maximum number of parents for a class (see R0901). 332 | max-parents=7 333 | 334 | # Maximum number of attributes for a class (see R0902). 335 | max-attributes=10 336 | 337 | # Minimum number of public methods for a class (see R0903). 338 | min-public-methods=2 339 | 340 | # Maximum number of public methods for a class (see R0904). 341 | max-public-methods=35 342 | 343 | # Maximum number of boolean expressions in a if statement 344 | max-bool-expr=5 345 | 346 | 347 | [IMPORTS] 348 | 349 | # Deprecated modules which should not be used, separated by a comma 350 | deprecated-modules=optparse 351 | 352 | # Create a graph of every (i.e. internal and external) dependencies in the 353 | # given file (report RP0402 must not be disabled) 354 | import-graph= 355 | 356 | # Create a graph of external dependencies in the given file (report RP0402 must 357 | # not be disabled) 358 | ext-import-graph= 359 | 360 | # Create a graph of internal dependencies in the given file (report RP0402 must 361 | # not be disabled) 362 | int-import-graph= 363 | 364 | # Force import order to recognize a module as part of the standard 365 | # compatibility libraries. 366 | known-standard-library= 367 | 368 | # Force import order to recognize a module as part of a third party library. 369 | known-third-party=enchant 370 | 371 | # Analyse import fallback blocks. This can be used to support both Python 2 and 372 | # 3 compatible code, which means that the block might have code that exists 373 | # only in one or another interpreter, leading to false positives when analysed. 374 | analyse-fallback-blocks=no 375 | 376 | 377 | [EXCEPTIONS] 378 | 379 | # Exceptions that will emit a warning when being caught. Defaults to 380 | # "Exception" 381 | overgeneral-exceptions=builtins.Exception 382 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./test -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Code of Conduct 4 | 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) 5 | 6 | ---- 7 | 8 | License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), 9 | Copyright Contributors to Qiskit. 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | **We appreciate all kinds of help, so thank you!** 4 | 5 | First please read the overall project contributing guidelines. These are 6 | included in the Qiskit documentation here: 7 | 8 | https://github.com/Qiskit/qiskit/blob/main/CONTRIBUTING.md 9 | 10 | ## Contributing to Qiskit Nature PySCF 11 | 12 | In addition to the general guidelines above there are specific details for 13 | contributing to Qiskit Nature PySCF. 14 | 15 | You should first install the python development libraries by running 16 | `pip install -r requirements-dev.txt` from the root of the Qiskit Nature PySCF 17 | repository clone and then follow the guidelines below. 18 | 19 | ### Project Code Style. 20 | 21 | Code in Qiskit Nature PySCF should conform to PEP8 and style/lint checks are run to validate 22 | this. Line length must be limited to no more than 100 characters. Docstrings 23 | should be written using the Google docstring format. 24 | 25 | ### Pull request checklist 26 | 27 | When submitting a pull request and you feel it is ready for review, 28 | please ensure that: 29 | 30 | 1. The code follows the _code style_ of this project and successfully 31 | passes the _unit tests_. Qiskit Nature PySCF uses [Pylint](https://www.pylint.org) and 32 | [PEP8](https://www.python.org/dev/peps/pep-0008) style guidelines. 33 | 34 | You can run 35 | ```shell script 36 | make lint 37 | make style 38 | ``` 39 | from the root of the Qiskit Nature PySCF repository clone for lint and style conformance checks. 40 | 41 | If your code fails the local style checks (specifically the black 42 | code formatting check) you can use `make black` to automatically 43 | fix update the code formatting. 44 | 45 | For unit testing please see [Testing](#testing) section below. 46 | 47 | 2. The documentation has been updated accordingly. In particular, if a 48 | function or class has been modified during the PR, please update the 49 | *docstring* accordingly. 50 | 51 | The documentation will be built/tested using Sphinx and should be free 52 | from errors and warnings. 53 | You will need to [install pandoc](https://pandoc.org/installing.html) first. 54 | 55 | Then you can run 56 | ```shell script 57 | make html 58 | ``` 59 | from the root of the Qiskit Nature PySCF repository clone. You might also like to check the html output 60 | to see the changes formatted output is as expected. You will find an index.html 61 | file in docs\_build\html and you can navigate from there. 62 | 63 | Please note that a spell check is run in CI on the docstrings. 64 | 65 | You can run `make spell` locally to check spelling though you would need to 66 | [install pyenchant](https://pyenchant.github.io/pyenchant/install.html) and be using 67 | hunspell-en-us as is used by the CI. 68 | 69 | For some words, such as names, technical terms, referring to parameters of the method etc., 70 | that are not in the en-us dictionary and get flagged as being misspelled, despite being correct, 71 | there is a [.pylintdict](./.pylintdict) custom word list file, in the root of the Qiskit Nature PySCF repo, 72 | where such words can be added, in alphabetic order, as needed. 73 | 74 | 3. If it makes sense for your change that you have added new tests that 75 | cover the changes and any new function. 76 | 77 | 4. Ensure that if your change has an end user facing impact (new feature, 78 | deprecation, removal etc) that you have added a reno release note for 79 | that change and that the PR is tagged for the changelog. 80 | 81 | 5. Ensure all code, including unit tests, has the copyright header. The copyright 82 | date will be checked by CI build. The format of the date(s) is _year of creation, 83 | last year changed_. So for example: 84 | 85 | > \# (C) Copyright IBM 2018, 2021. 86 | 87 | If the _year of creation_ is the same as _last year changed_ then only 88 | one date is needed, for example: 89 | 90 | > \# (C) Copyright IBM 2021. 91 | 92 | If code is changed in a file make sure the copyright includes the current year. 93 | If there is just one date and it's a prior year then add the current year as the 2nd date, 94 | otherwise simply change the 2nd date to the current year. The _year of creation_ date is 95 | never changed. 96 | 97 | ### Changelog generation 98 | 99 | The changelog is automatically generated as part of the release process 100 | automation. This works through a combination of the git log and the pull 101 | request. When a release is tagged and pushed to github the release automation 102 | bot looks at all commit messages from the git log for the release. It takes the 103 | PR numbers from the git log (assuming a squash merge) and checks if that PR had 104 | a `Changelog:` label on it. If there is a label it will add the git commit 105 | message summary line from the git log for the release to the changelog. 106 | 107 | If there are multiple `Changelog:` tags on a PR the git commit message summary 108 | line from the git log will be used for each changelog category tagged. 109 | 110 | The current categories for each label are as follows: 111 | 112 | | PR Label | Changelog Category | 113 | | -----------------------|--------------------| 114 | | Changelog: Deprecation | Deprecated | 115 | | Changelog: New Feature | Added | 116 | | Changelog: API Change | Changed | 117 | | Changelog: Removal | Removed | 118 | | Changelog: Bugfix | Fixed | 119 | 120 | ### Release Notes 121 | 122 | When making any end user facing changes in a contribution we have to make sure 123 | we document that when we release a new version of qiskit-nature-pyscf. The expectation 124 | is that if your code contribution has user facing changes that you will write 125 | the release documentation for these changes. This documentation must explain 126 | what was changed, why it was changed, and how users can either use or adapt 127 | to the change. The idea behind release documentation is that when a naive 128 | user with limited internal knowledge of the project is upgrading from the 129 | previous release to the new one, they should be able to read the release notes, 130 | understand if they need to update their program which uses qiskit, and how they 131 | would go about doing that. It ideally should explain why they need to make 132 | this change too, to provide the necessary context. 133 | 134 | To make sure we don't forget a release note or if the details of user facing 135 | changes over a release cycle we require that all user facing changes include 136 | documentation at the same time as the code. To accomplish this we use the 137 | [reno](https://docs.openstack.org/reno/latest/) tool which enables a git based 138 | workflow for writing and compiling release notes. 139 | 140 | #### Adding a new release note 141 | 142 | Making a new release note is quite straightforward. Ensure that you have reno 143 | installed with:: 144 | 145 | pip install -U reno 146 | 147 | Once you have reno installed you can make a new release note by running in 148 | your local repository checkout's root:: 149 | 150 | reno new short-description-string 151 | 152 | where short-description-string is a brief string (with no spaces) that describes 153 | what's in the release note. This will become the prefix for the release note 154 | file. Once that is run it will create a new yaml file in releasenotes/notes. 155 | Then open that yaml file in a text editor and write the release note. The basic 156 | structure of a release note is restructured text in yaml lists under category 157 | keys. You add individual items under each category and they will be grouped 158 | automatically by release when the release notes are compiled. A single file 159 | can have as many entries in it as needed, but to avoid potential conflicts 160 | you'll want to create a new file for each pull request that has user facing 161 | changes. When you open the newly created file it will be a full template of 162 | the different categories with a description of a category as a single entry 163 | in each category. You'll want to delete all the sections you aren't using and 164 | update the contents for those you are. For example, the end result should 165 | look something like:: 166 | 167 | ```yaml 168 | features: 169 | - | 170 | Introduced a new feature foo, that adds support for doing something to 171 | ``QuantumCircuit`` objects. It can be used by using the foo function, 172 | for example:: 173 | 174 | from qiskit import foo 175 | from qiskit import QuantumCircuit 176 | foo(QuantumCircuit()) 177 | 178 | - | 179 | The ``qiskit.QuantumCircuit`` module has a new method ``foo()``. This is 180 | the equivalent of calling the ``qiskit.foo()`` to do something to your 181 | QuantumCircuit. This is the equivalent of running ``qiskit.foo()`` on 182 | your circuit, but provides the convenience of running it natively on 183 | an object. For example:: 184 | 185 | from qiskit import QuantumCircuit 186 | 187 | circ = QuantumCircuit() 188 | circ.foo() 189 | 190 | deprecations: 191 | - | 192 | The ``qiskit.bar`` module has been deprecated and will be removed in a 193 | future release. Its sole function, ``foobar()`` has been superseded by the 194 | ``qiskit.foo()`` function which provides similar functionality but with 195 | more accurate results and better performance. You should update your calls 196 | ``qiskit.bar.foobar()`` calls to ``qiskit.foo()``. 197 | ``` 198 | 199 | You can also look at other release notes for other examples. 200 | 201 | You can use any restructured text feature in them (code sections, tables, 202 | enumerated lists, bulleted list, etc) to express what is being changed as 203 | needed. In general you want the release notes to include as much detail as 204 | needed so that users will understand what has changed, why it changed, and how 205 | they'll have to update their code. 206 | 207 | After you've finished writing your release notes you'll want to add the note 208 | file to your commit with `git add` and commit them to your PR branch to make 209 | sure they're included with the code in your PR. 210 | 211 | #### Generating the release notes 212 | 213 | After release notes have been added if you want to see what the full output of 214 | the release notes. In general the output from reno that we'll get is a rst 215 | (ReStructuredText) file that can be compiled by 216 | [sphinx](https://www.sphinx-doc.org/en/master/). To generate the rst file you 217 | use the ``reno report`` command. If you want to generate the full Qiskit Nature PySCF release 218 | notes for all releases (since we started using reno during 0.9) you just run:: 219 | 220 | reno report 221 | 222 | but you can also use the ``--version`` argument to view a single release (after 223 | it has been tagged:: 224 | 225 | reno report --version 0.5.0 226 | 227 | At release time ``reno report`` is used to generate the release notes for the 228 | release and the output will be submitted as a pull request to the documentation 229 | repository's [release notes file]( 230 | https://github.com/Qiskit/qiskit/blob/main/docs/release_notes.rst) 231 | 232 | #### Building release notes locally 233 | 234 | Building The release notes are part of the standard qiskit-nature-pyscf documentation 235 | builds. To check what the rendered html output of the release notes will look 236 | like for the current state of the repo you can run: `tox -edocs` which will 237 | build all the documentation into `docs/_build/html` and the release notes in 238 | particular will be located at `docs/_build/html/release_notes.html` 239 | 240 | ## Installing Qiskit Nature PySCF from source 241 | 242 | Please see the [Installing Qiskit Nature PySCF from 243 | Source](https://qiskit-community.github.io/qiskit-nature-pyscf/getting_started.html) 244 | section of the documentation. 245 | 246 | Note: Qiskit Nature PySCF depends on Qiskit Nature and PySCF so you may want to 247 | install those from source, too. 248 | 249 | ### Testing 250 | 251 | Once you've made a code change, it is important to verify that your change 252 | does not break any existing tests and that any new tests that you've added 253 | also run successfully. Before you open a new pull request for your change, 254 | you'll want to run the test suite locally. 255 | 256 | The test suite can be run from a command line or via your IDE. You can run `make test` which will 257 | run all unit tests. Another way to run the test suite is to use 258 | [**tox**](https://tox.readthedocs.io/en/latest/#). For more information about using tox please 259 | refer to 260 | [Terra CONTRIBUTING](https://github.com/Qiskit/qiskit-terra/blob/main/CONTRIBUTING.md#test) 261 | Test section. However please note Qiskit Nature PySCF does not have any 262 | [online tests](https://github.com/Qiskit/qiskit-terra/blob/main/CONTRIBUTING.md#online-tests) 263 | nor does it have 264 | [test skip 265 | options](https://github.com/Qiskit/qiskit-terra/blob/main/CONTRIBUTING.md#test-skip-options). 266 | 267 | ### Branches 268 | 269 | * `main`: 270 | 271 | The main branch is used for development of the next version of qiskit-nature-pyscf. 272 | It will be updated frequently and should not be considered stable. The API 273 | can and will change on main as we introduce and refine new features. 274 | 275 | * `stable/*`: 276 | The stable branches are used to maintain the most recent released versions of 277 | qiskit-nature-pyscf. It contains the versions of the code corresponding to the minor 278 | version release in the branch name release for The API on these branches are 279 | stable and the only changes merged to it are bugfixes. 280 | 281 | ### Release Cycle 282 | 283 | From time to time, we will release brand new versions of Qiskit Nature PySCF. These 284 | are well-tested versions of the software. 285 | 286 | When the time for a new release has come, we will: 287 | 288 | 1. Create a new tag with the version number and push it to github 289 | 2. Change the `main` version to the next release version. 290 | 291 | The release automation processes will be triggered by the new tag and perform 292 | the following steps: 293 | 294 | 1. Create a stable branch for the new minor version from the release tag 295 | on the `main` branch 296 | 2. Build and upload binary wheels to pypi 297 | 3. Create a github release page with a generated changelog 298 | 4. Generate a PR on the meta-repository to bump the terra version and 299 | meta-package version. 300 | 301 | The `stable/*` branches should only receive changes in the form of bug 302 | fixes. 303 | 304 | ## Dealing with the git blame ignore list 305 | 306 | In the qiskit-nature-pyscf repository we maintain a list of commits for git blame 307 | to ignore. This is mostly commits that are code style changes that don't 308 | change the functionality but just change the code formatting (for example, 309 | when we migrated to use black for code formatting). This file, 310 | `.git-blame-ignore-revs` just contains a list of commit SHA1s you can tell git 311 | to ignore when using the `git blame` command. This can be done one time 312 | with something like 313 | 314 | ``` 315 | git blame --ignore-revs-file .git-blame-ignore-revs qiskit_nature_pyscf/version.py 316 | ``` 317 | 318 | from the root of the repository. If you'd like to enable this by default you 319 | can update your local repository's configuration with: 320 | 321 | ``` 322 | git config blame.ignoreRevsFile .git-blame-ignore-revs 323 | ``` 324 | 325 | which will update your local repositories configuration to use the ignore list 326 | by default. 327 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2017 IBM and its contributors 2 | 3 | Apache License 4 | Version 2.0, January 2004 5 | http://www.apache.org/licenses/ 6 | 7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 8 | 9 | 1. Definitions. 10 | 11 | "License" shall mean the terms and conditions for use, reproduction, 12 | and distribution as defined by Sections 1 through 9 of this document. 13 | 14 | "Licensor" shall mean the copyright owner or entity authorized by 15 | the copyright owner that is granting the License. 16 | 17 | "Legal Entity" shall mean the union of the acting entity and all 18 | other entities that control, are controlled by, or are under common 19 | control with that entity. For the purposes of this definition, 20 | "control" means (i) the power, direct or indirect, to cause the 21 | direction or management of such entity, whether by contract or 22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 23 | outstanding shares, or (iii) beneficial ownership of such entity. 24 | 25 | "You" (or "Your") shall mean an individual or Legal Entity 26 | exercising permissions granted by this License. 27 | 28 | "Source" form shall mean the preferred form for making modifications, 29 | including but not limited to software source code, documentation 30 | source, and configuration files. 31 | 32 | "Object" form shall mean any form resulting from mechanical 33 | transformation or translation of a Source form, including but 34 | not limited to compiled object code, generated documentation, 35 | and conversions to other media types. 36 | 37 | "Work" shall mean the work of authorship, whether in Source or 38 | Object form, made available under the License, as indicated by a 39 | copyright notice that is included in or attached to the work 40 | (an example is provided in the Appendix below). 41 | 42 | "Derivative Works" shall mean any work, whether in Source or Object 43 | form, that is based on (or derived from) the Work and for which the 44 | editorial revisions, annotations, elaborations, or other modifications 45 | represent, as a whole, an original work of authorship. For the purposes 46 | of this License, Derivative Works shall not include works that remain 47 | separable from, or merely link (or bind by name) to the interfaces of, 48 | the Work and Derivative Works thereof. 49 | 50 | "Contribution" shall mean any work of authorship, including 51 | the original version of the Work and any modifications or additions 52 | to that Work or Derivative Works thereof, that is intentionally 53 | submitted to Licensor for inclusion in the Work by the copyright owner 54 | or by an individual or Legal Entity authorized to submit on behalf of 55 | the copyright owner. For the purposes of this definition, "submitted" 56 | means any form of electronic, verbal, or written communication sent 57 | to the Licensor or its representatives, including but not limited to 58 | communication on electronic mailing lists, source code control systems, 59 | and issue tracking systems that are managed by, or on behalf of, the 60 | Licensor for the purpose of discussing and improving the Work, but 61 | excluding communication that is conspicuously marked or otherwise 62 | designated in writing by the copyright owner as "Not a Contribution." 63 | 64 | "Contributor" shall mean Licensor and any individual or Legal Entity 65 | on behalf of whom a Contribution has been received by Licensor and 66 | subsequently incorporated within the Work. 67 | 68 | 2. Grant of Copyright License. Subject to the terms and conditions of 69 | this License, each Contributor hereby grants to You a perpetual, 70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 71 | copyright license to reproduce, prepare Derivative Works of, 72 | publicly display, publicly perform, sublicense, and distribute the 73 | Work and such Derivative Works in Source or Object form. 74 | 75 | 3. Grant of Patent License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | (except as stated in this section) patent license to make, have made, 79 | use, offer to sell, sell, import, and otherwise transfer the Work, 80 | where such license applies only to those patent claims licensable 81 | by such Contributor that are necessarily infringed by their 82 | Contribution(s) alone or by combination of their Contribution(s) 83 | with the Work to which such Contribution(s) was submitted. If You 84 | institute patent litigation against any entity (including a 85 | cross-claim or counterclaim in a lawsuit) alleging that the Work 86 | or a Contribution incorporated within the Work constitutes direct 87 | or contributory patent infringement, then any patent licenses 88 | granted to You under this License for that Work shall terminate 89 | as of the date such litigation is filed. 90 | 91 | 4. Redistribution. You may reproduce and distribute copies of the 92 | Work or Derivative Works thereof in any medium, with or without 93 | modifications, and in Source or Object form, provided that You 94 | meet the following conditions: 95 | 96 | (a) You must give any other recipients of the Work or 97 | Derivative Works a copy of this License; and 98 | 99 | (b) You must cause any modified files to carry prominent notices 100 | stating that You changed the files; and 101 | 102 | (c) You must retain, in the Source form of any Derivative Works 103 | that You distribute, all copyright, patent, trademark, and 104 | attribution notices from the Source form of the Work, 105 | excluding those notices that do not pertain to any part of 106 | the Derivative Works; and 107 | 108 | (d) If the Work includes a "NOTICE" text file as part of its 109 | distribution, then any Derivative Works that You distribute must 110 | include a readable copy of the attribution notices contained 111 | within such NOTICE file, excluding those notices that do not 112 | pertain to any part of the Derivative Works, in at least one 113 | of the following places: within a NOTICE text file distributed 114 | as part of the Derivative Works; within the Source form or 115 | documentation, if provided along with the Derivative Works; or, 116 | within a display generated by the Derivative Works, if and 117 | wherever such third-party notices normally appear. The contents 118 | of the NOTICE file are for informational purposes only and 119 | do not modify the License. You may add Your own attribution 120 | notices within Derivative Works that You distribute, alongside 121 | or as an addendum to the NOTICE text from the Work, provided 122 | that such additional attribution notices cannot be construed 123 | as modifying the License. 124 | 125 | You may add Your own copyright statement to Your modifications and 126 | may provide additional or different license terms and conditions 127 | for use, reproduction, or distribution of Your modifications, or 128 | for any such Derivative Works as a whole, provided Your use, 129 | reproduction, and distribution of the Work otherwise complies with 130 | the conditions stated in this License. 131 | 132 | 5. Submission of Contributions. Unless You explicitly state otherwise, 133 | any Contribution intentionally submitted for inclusion in the Work 134 | by You to the Licensor shall be under the terms and conditions of 135 | this License, without any additional terms or conditions. 136 | Notwithstanding the above, nothing herein shall supersede or modify 137 | the terms of any separate license agreement you may have executed 138 | with Licensor regarding such Contributions. 139 | 140 | 6. Trademarks. This License does not grant permission to use the trade 141 | names, trademarks, service marks, or product names of the Licensor, 142 | except as required for reasonable and customary use in describing the 143 | origin of the Work and reproducing the content of the NOTICE file. 144 | 145 | 7. Disclaimer of Warranty. Unless required by applicable law or 146 | agreed to in writing, Licensor provides the Work (and each 147 | Contributor provides its Contributions) on an "AS IS" BASIS, 148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 149 | implied, including, without limitation, any warranties or conditions 150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 151 | PARTICULAR PURPOSE. You are solely responsible for determining the 152 | appropriateness of using or redistributing the Work and assume any 153 | risks associated with Your exercise of permissions under this License. 154 | 155 | 8. Limitation of Liability. In no event and under no legal theory, 156 | whether in tort (including negligence), contract, or otherwise, 157 | unless required by applicable law (such as deliberate and grossly 158 | negligent acts) or agreed to in writing, shall any Contributor be 159 | liable to You for damages, including any direct, indirect, special, 160 | incidental, or consequential damages of any character arising as a 161 | result of this License or out of the use or inability to use the 162 | Work (including but not limited to damages for loss of goodwill, 163 | work stoppage, computer failure or malfunction, or any and all 164 | other commercial damages or losses), even if such Contributor 165 | has been advised of the possibility of such damages. 166 | 167 | 9. Accepting Warranty or Additional Liability. While redistributing 168 | the Work or Derivative Works thereof, You may choose to offer, 169 | and charge a fee for, acceptance of support, warranty, indemnity, 170 | or other liability obligations and/or rights consistent with this 171 | License. However, in accepting such obligations, You may act only 172 | on Your own behalf and on Your sole responsibility, not on behalf 173 | of any other Contributor, and only if You agree to indemnify, 174 | defend, and hold each Contributor harmless for any liability 175 | incurred by, or claims asserted against, such Contributor by reason 176 | of your accepting any such warranty or additional liability. 177 | 178 | END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following 183 | boilerplate notice, with the fields enclosed by brackets "[]" 184 | replaced with your own identifying information. (Don't include 185 | the brackets!) The text should be enclosed in the appropriate 186 | comment syntax for the file format. We also recommend that a 187 | file or class name and description of purpose be included on the 188 | same "printed page" as the copyright notice for easier 189 | identification within third-party archives. 190 | 191 | Copyright 2017 IBM and its contributors. 192 | 193 | Licensed under the Apache License, Version 2.0 (the "License"); 194 | you may not use this file except in compliance with the License. 195 | You may obtain a copy of the License at 196 | 197 | http://www.apache.org/licenses/LICENSE-2.0 198 | 199 | Unless required by applicable law or agreed to in writing, software 200 | distributed under the License is distributed on an "AS IS" BASIS, 201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 202 | See the License for the specific language governing permissions and 203 | limitations under the License. 204 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include requirements.txt 3 | include qiskit_nature_pyscf/py.typed 4 | recursive-include qiskit_nature_pyscf *.txt 5 | graft test 6 | prune docs 7 | prune tools 8 | prune tutorials 9 | global-exclude *.py[co] .DS_Store 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | OS := $(shell uname -s) 14 | 15 | ifeq ($(OS), Linux) 16 | NPROCS := $(shell grep -c ^processor /proc/cpuinfo) 17 | else ifeq ($(OS), Darwin) 18 | NPROCS := 2 19 | else 20 | NPROCS := 0 21 | endif # $(OS) 22 | 23 | ifeq ($(NPROCS), 2) 24 | CONCURRENCY := 2 25 | else ifeq ($(NPROCS), 1) 26 | CONCURRENCY := 1 27 | else ifeq ($(NPROCS), 3) 28 | CONCURRENCY := 3 29 | else ifeq ($(NPROCS), 0) 30 | CONCURRENCY := 0 31 | else 32 | CONCURRENCY := $(shell echo "$(NPROCS) 2" | awk '{printf "%.0f", $$1 / $$2}') 33 | endif 34 | 35 | # You can set this variable from the command line. 36 | SPHINXOPTS = 37 | 38 | .PHONY: lint mypy style black test test_ci spell copyright html doctest clean_sphinx coverage coverage_erase clean 39 | 40 | all_check: spell style lint copyright mypy clean_sphinx html doctest 41 | 42 | lint: 43 | python -m pylint -rn qiskit_nature_pyscf test tools 44 | python tools/verify_headers.py qiskit_nature_pyscf test tools 45 | python tools/find_stray_release_notes.py 46 | 47 | mypy: 48 | python -m mypy qiskit_nature_pyscf test tools 49 | 50 | style: 51 | python -m black --check qiskit_nature_pyscf test tools docs 52 | 53 | black: 54 | python -m black qiskit_nature_pyscf test tools docs 55 | 56 | test: 57 | python -m unittest discover -v test 58 | 59 | test_ci: 60 | echo "Detected $(NPROCS) CPUs running with $(CONCURRENCY) workers" 61 | python -m stestr run --concurrency $(CONCURRENCY) 62 | 63 | spell: 64 | python -m pylint -rn --disable=all --enable=spelling --spelling-dict=en_US --spelling-private-dict-file=.pylintdict qiskit_nature_pyscf test tools 65 | sphinx-build -M spelling docs docs/_build -W -T --keep-going $(SPHINXOPTS) 66 | 67 | copyright: 68 | python tools/check_copyright.py 69 | 70 | html: 71 | sphinx-build -M html docs docs/_build -W -T --keep-going $(SPHINXOPTS) 72 | 73 | doctest: 74 | sphinx-build -M doctest docs docs/_build -W -T --keep-going $(SPHINXOPTS) 75 | 76 | clean_sphinx: 77 | make -C docs clean 78 | 79 | coverage: 80 | python -m coverage3 run --source qiskit_nature_pyscf -m unittest discover -s test -q 81 | python -m coverage3 report 82 | 83 | coverage_erase: 84 | python -m coverage erase 85 | 86 | clean: clean_sphinx coverage_erase; 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qiskit Nature PySCF 2 | 3 | [![License](https://img.shields.io/github/license/qiskit-community/qiskit-nature-pyscf.svg?style=popout-square)](https://opensource.org/licenses/Apache-2.0)[![Build Status](https://github.com/qiskit-community/qiskit-nature-pyscf/workflows/Nature%20PySCF%20Unit%20Tests/badge.svg?branch=main)](https://github.com/qiskit-community/qiskit-nature-pyscf/actions?query=workflow%3A"Nature%20PySCF%20Unit%20Tests"+branch%3Amain+event%3Apush)[![](https://img.shields.io/github/release/qiskit-community/qiskit-nature-pyscf.svg?style=popout-square)](https://github.com/qiskit-community/qiskit-nature-pyscf/releases)[![](https://img.shields.io/pypi/dm/qiskit-nature-pyscf.svg?style=popout-square)](https://pypi.org/project/qiskit-nature-pyscf/)[![Coverage Status](https://coveralls.io/repos/github/qiskit-community/qiskit-nature-pyscf/badge.svg?branch=main)](https://coveralls.io/github/qiskit-community/qiskit-nature-pyscf?branch=main) 4 | 5 | **Qiskit Nature PySCF** is a third-party integration plugin of Qiskit Nature + PySCF. 6 | 7 | ## Installation 8 | 9 | We encourage installing Qiskit Nature PySCF via the pip tool (a python package manager). 10 | 11 | ```bash 12 | pip install qiskit-nature-pyscf 13 | ``` 14 | 15 | **pip** will handle all dependencies automatically and you will always install the latest 16 | (and well-tested) version. It will also install Qiskit Nature if needed. 17 | 18 | If you want to work on the very latest work-in-progress versions, either to try features ahead of 19 | their official release or if you want to contribute to Qiskit Nature PySCF, then you can install from source. 20 | 21 | 22 | ## Usage 23 | 24 | This plugin couples the APIs of PySCF and Qiskit Nature, enabling a user of PySCF to leverage 25 | Quantum-based algorithms implemented in Qiskit to be used in-place of their classical counterparts. 26 | 27 | ### Active Space Calculations 28 | 29 | One very common approach is to use a Quantum algorithm to find the ground state in an active space 30 | calculation. To this extent, this plugin provides the `QiskitSolver` class, which you can inject 31 | directly into your `CASCI` or `CASSCF` simulation objects of PySCF. 32 | 33 | Below we show a simple example of how to do this. 34 | 35 | ```python 36 | from pyscf import gto, scf, mcscf 37 | 38 | import numpy as np 39 | 40 | from qiskit.primitives import Estimator 41 | from qiskit_algorithms import VQE 42 | from qiskit_algorithms.optimizers import SLSQP 43 | from qiskit_nature.second_q.algorithms import GroundStateEigensolver 44 | from qiskit_nature.second_q.circuit.library import HartreeFock, UCCSD 45 | from qiskit_nature.second_q.mappers import ParityMapper 46 | 47 | from qiskit_nature_pyscf import QiskitSolver 48 | 49 | mol = gto.M(atom="Li 0 0 0; H 0 0 1.6", basis="sto-3g") 50 | 51 | h_f = scf.RHF(mol).run() 52 | 53 | norb = 2 54 | nalpha, nbeta = 1, 1 55 | nelec = nalpha + nbeta 56 | 57 | cas = mcscf.CASCI(h_f, norb, nelec) 58 | 59 | mapper = ParityMapper(num_particles=(nalpha, nbeta)) 60 | 61 | ansatz = UCCSD( 62 | norb, 63 | (nalpha, nbeta), 64 | mapper, 65 | initial_state=HartreeFock( 66 | norb, 67 | (nalpha, nbeta), 68 | mapper, 69 | ), 70 | ) 71 | 72 | vqe = VQE(Estimator(), ansatz, SLSQP()) 73 | vqe.initial_point = np.zeros(ansatz.num_parameters) 74 | 75 | algorithm = GroundStateEigensolver(mapper, vqe) 76 | 77 | cas.fcisolver = QiskitSolver(algorithm) 78 | 79 | cas.run() 80 | ``` 81 | 82 | More detailed information for this plugin can be found in its 83 | [Documentation](https://qiskit-community.github.io/qiskit-nature-pyscf/). For further 84 | information and explanations we recommend to check out the documentation of 85 | [PySCF](https://pyscf.org/) and [Qiskit Nature](https://qiskit-community.github.io/qiskit-nature/). 86 | 87 | 88 | ## Citation 89 | 90 | If you use this plugin, please cite the following references: 91 | 92 | - PySCF, as per [these instructions](https://github.com/pyscf/pyscf#citing-pyscf). 93 | - Qiskit, as per the provided [BibTeX file](https://github.com/Qiskit/qiskit/blob/master/Qiskit.bib). 94 | - Qiskit Nature, as per https://doi.org/10.5281/zenodo.7828767 95 | -------------------------------------------------------------------------------- /constraints.txt: -------------------------------------------------------------------------------- 1 | numpy>=1.20.0 2 | scipy<1.14 3 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2018, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | # You can set these variables from the command line. 14 | SPHINXOPTS = 15 | SPHINXBUILD = sphinx-build 16 | SOURCEDIR = . 17 | BUILDDIR = _build 18 | STUBSDIR = stubs 19 | 20 | # Put it first so that "make" without argument is like "make help". 21 | help: 22 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 23 | 24 | spell: 25 | @$(SPHINXBUILD) -M spelling "$(SOURCEDIR)" "$(BUILDDIR)" -W -T --keep-going $(SPHINXOPTS) $(O) 26 | 27 | clean: 28 | rm -rf $(BUILDDIR) 29 | rm -rf $(STUBSDIR) 30 | 31 | .PHONY: help spell clean Makefile 32 | 33 | # Catch-all target: route all unknown targets to Sphinx using the new 34 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 35 | %: Makefile 36 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" -W -T --keep-going $(SPHINXOPTS) $(O) 37 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/base.rst: -------------------------------------------------------------------------------- 1 | {% if referencefile %} 2 | .. include:: {{ referencefile }} 3 | {% endif %} 4 | 5 | {{ objname }} 6 | {{ underline }} 7 | 8 | .. currentmodule:: {{ module }} 9 | 10 | .. auto{{ objtype }}:: {{ objname }} 11 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/class.rst: -------------------------------------------------------------------------------- 1 | {% if referencefile %} 2 | .. include:: {{ referencefile }} 3 | {% endif %} 4 | 5 | {{ objname }} 6 | {{ underline }} 7 | 8 | .. currentmodule:: {{ module }} 9 | 10 | .. autoclass:: {{ objname }} 11 | :show-inheritance: 12 | :no-members: 13 | :no-inherited-members: 14 | :no-special-members: 15 | 16 | {% block attributes_summary %} 17 | {% if attributes %} 18 | 19 | .. rubric:: Attributes 20 | 21 | .. autosummary:: 22 | :toctree: ../stubs/ 23 | {% for item in all_attributes %} 24 | {%- if not item.startswith('_') %} 25 | ~{{ name }}.{{ item }} 26 | {%- endif -%} 27 | {%- endfor %} 28 | {% endif %} 29 | {% endblock %} 30 | 31 | {% block methods_summary %} 32 | {% if methods %} 33 | 34 | .. rubric:: Methods 35 | 36 | .. autosummary:: 37 | :toctree: ../stubs/ 38 | {% for item in all_methods %} 39 | {%- if not item.startswith('_') %} 40 | ~{{ name }}.{{ item }} 41 | {%- endif -%} 42 | {%- endfor %} 43 | 44 | {% endif %} 45 | {% endblock %} 46 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/class_no_inherited_members.rst: -------------------------------------------------------------------------------- 1 | {% if referencefile %} 2 | .. include:: {{ referencefile }} 3 | {% endif %} 4 | 5 | {{ objname }} 6 | {{ underline }} 7 | 8 | .. currentmodule:: {{ module }} 9 | 10 | .. autoclass:: {{ objname }} 11 | :show-inheritance: 12 | :no-members: 13 | :no-inherited-members: 14 | :no-special-members: 15 | 16 | {% block attributes_summary %} 17 | {% if attributes %} 18 | 19 | .. rubric:: Attributes 20 | 21 | .. autosummary:: 22 | :toctree: ../stubs/ 23 | {% for item in all_attributes %} 24 | {%- if item not in inherited_members %} 25 | {%- if not item.startswith('_') %} 26 | ~{{ name }}.{{ item }} 27 | {%- endif -%} 28 | {%- endif %} 29 | {%- endfor %} 30 | {% endif %} 31 | {% endblock %} 32 | 33 | {% block methods_summary %} 34 | {% if methods %} 35 | 36 | .. rubric:: Methods 37 | 38 | .. autosummary:: 39 | :toctree: ../stubs/ 40 | {% for item in all_methods %} 41 | {%- if item not in inherited_members %} 42 | {%- if not item.startswith('_') %} 43 | ~{{ name }}.{{ item }} 44 | {%- endif -%} 45 | {%- endif %} 46 | {%- endfor %} 47 | 48 | {% endif %} 49 | {% endblock %} 50 | -------------------------------------------------------------------------------- /docs/_templates/autosummary/module.rst: -------------------------------------------------------------------------------- 1 | {% if referencefile %} 2 | .. include:: {{ referencefile }} 3 | {% endif %} 4 | 5 | {{ objname }} 6 | {{ underline }} 7 | 8 | .. automodule:: {{ fullname }} 9 | 10 | {% block functions %} 11 | {% if functions %} 12 | .. rubric:: Functions 13 | 14 | .. autosummary:: 15 | {% for item in functions %} 16 | {{ item }} 17 | {%- endfor %} 18 | {% endif %} 19 | {% endblock %} 20 | 21 | {% block classes %} 22 | {% if classes %} 23 | .. rubric:: Classes 24 | 25 | .. autosummary:: 26 | {% for item in classes %} 27 | {{ item }} 28 | {%- endfor %} 29 | {% endif %} 30 | {% endblock %} 31 | 32 | {% block exceptions %} 33 | {% if exceptions %} 34 | .. rubric:: Exceptions 35 | 36 | .. autosummary:: 37 | {% for item in exceptions %} 38 | {{ item }} 39 | {%- endfor %} 40 | {% endif %} 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /docs/apidocs/qiskit_nature_pyscf.rst: -------------------------------------------------------------------------------- 1 | ================================= 2 | Qiskit Nature PySCF API Reference 3 | ================================= 4 | 5 | .. _qiskit_nature_pyscf: 6 | 7 | .. automodule:: qiskit_nature_pyscf 8 | :no-members: 9 | :no-inherited-members: 10 | :no-special-members: 11 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | # pylint: disable=invalid-name 14 | # Configuration file for the Sphinx documentation builder. 15 | # 16 | # This file does only contain a selection of the most common options. For a 17 | # full list see the documentation: 18 | # http://www.sphinx-doc.org/en/master/config 19 | 20 | # -- Path setup -------------------------------------------------------------- 21 | 22 | # If extensions (or modules to document with autodoc) are in another directory, 23 | # add these directories to sys.path here. If the directory is relative to the 24 | # documentation root, use os.path.abspath to make it absolute, like shown here. 25 | # 26 | import os 27 | import sys 28 | from datetime import date 29 | 30 | sys.path.insert(0, os.path.abspath("..")) 31 | sys.path.append(os.path.abspath(".")) 32 | 33 | import qiskit_nature_pyscf 34 | 35 | 36 | # Set env flag so that we can doc functions that may otherwise not be loaded 37 | # see for example interactive visualizations in qiskit.visualization. 38 | os.environ["QISKIT_DOCS"] = "TRUE" 39 | 40 | # -- Project information ----------------------------------------------------- 41 | project = "Qiskit Nature PySCF" 42 | copyright = f"2022, {date.today().year}, Qiskit Nature PySCF Development Team" # pylint: disable=redefined-builtin 43 | author = "Qiskit Nature PySCF Development Team" 44 | 45 | # The short X.Y version 46 | version = qiskit_nature_pyscf.__version__ 47 | # The full version, including alpha/beta/rc tags 48 | release = qiskit_nature_pyscf.__version__ 49 | 50 | rst_prolog = """ 51 | .. raw:: html 52 | 53 |


54 | 55 | .. |version| replace:: {0} 56 | """.format( 57 | release 58 | ) 59 | 60 | nbsphinx_prolog = """ 61 | {% set docname = env.doc2path(env.docname, base=None) %} 62 | .. only:: html 63 | 64 | .. role:: raw-html(raw) 65 | :format: html 66 | 67 | .. note:: 68 | This page was generated from `docs/{{ docname }}`__. 69 | 70 | __""" 71 | 72 | vers = version.split(".") 73 | link_str = f" https://github.com/qiskit-community/qiskit_nature_pyscf/blob/stable/{vers[0]}.{vers[1]}/docs/" 74 | nbsphinx_prolog += link_str + "{{ docname }}" 75 | 76 | # -- General configuration --------------------------------------------------- 77 | 78 | extensions = [ 79 | "sphinx.ext.napoleon", 80 | "sphinx.ext.autodoc", 81 | "sphinx.ext.autosummary", 82 | "sphinx.ext.mathjax", 83 | "sphinx.ext.viewcode", 84 | "sphinx.ext.extlinks", 85 | "sphinx_design", 86 | "jupyter_sphinx", 87 | "reno.sphinxext", 88 | "sphinx.ext.doctest", 89 | "nbsphinx", 90 | "sphinx.ext.intersphinx", 91 | "qiskit_sphinx_theme", 92 | ] 93 | templates_path = ["_templates"] 94 | 95 | nbsphinx_timeout = 360 96 | nbsphinx_execute = os.getenv("QISKIT_DOCS_BUILD_TUTORIALS", "never") 97 | nbsphinx_widgets_path = "" 98 | nbsphinx_thumbnails = {} 99 | 100 | spelling_word_list_filename = "../.pylintdict" 101 | spelling_filters = ["lowercase_filter.LowercaseFilter"] 102 | 103 | # ----------------------------------------------------------------------------- 104 | # Autosummary 105 | # ----------------------------------------------------------------------------- 106 | 107 | autosummary_generate = True 108 | autosummary_generate_overwrite = False 109 | 110 | # ----------------------------------------------------------------------------- 111 | # Autodoc 112 | # ----------------------------------------------------------------------------- 113 | # Move type hints from signatures to the parameter descriptions (except in overload cases, where 114 | # that's not possible). 115 | autodoc_typehints = "description" 116 | # Only add type hints from signature to description body if the parameter has documentation. The 117 | # return type is always added to the description (if in the signature). 118 | autodoc_typehints_description_target = "documented_params" 119 | 120 | autodoc_default_options = { 121 | "inherited-members": None, 122 | } 123 | 124 | autoclass_content = "both" 125 | 126 | # If true, figures, tables and code-blocks are automatically numbered if they 127 | # have a caption. 128 | numfig = True 129 | 130 | # A dictionary mapping 'figure', 'table', 'code-block' and 'section' to 131 | # strings that are used for format of figure numbers. As a special character, 132 | # %s will be replaced to figure number. 133 | numfig_format = {"table": "Table %s"} 134 | # The language for content autogenerated by Sphinx. Refer to documentation 135 | # for a list of supported languages. 136 | # 137 | # This is also used if you do content translation via gettext catalogs. 138 | # Usually you set "language" from the command line for these cases. 139 | language = "en" 140 | 141 | # For Adding Locale 142 | locale_dirs = ["locale/"] # path is example but recommended. 143 | gettext_compact = False # optional. 144 | 145 | # List of patterns, relative to source directory, that match files and 146 | # directories to ignore when looking for source files. 147 | # This pattern also affects html_static_path and html_extra_path. 148 | exclude_patterns = ["**site-packages", "_build", "**.ipynb_checkpoints"] 149 | 150 | # The name of the Pygments (syntax highlighting) style to use. 151 | pygments_style = "colorful" 152 | 153 | # A boolean that decides whether module names are prepended to all object names 154 | # (for object types where a “module” of some kind is defined), e.g. for 155 | # py:function directives. 156 | add_module_names = False 157 | 158 | # A list of prefixes that are ignored for sorting the Python module index 159 | # (e.g., if this is set to ['foo.'], then foo.bar is shown under B, not F). 160 | # This can be handy if you document a project that consists of a single 161 | # package. Works only for the HTML builder currently. 162 | modindex_common_prefix = ["qiskit_nature_pyscf."] 163 | 164 | # -- Configuration for extlinks extension ------------------------------------ 165 | # Refer to https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html 166 | 167 | 168 | # -- Options for HTML output ------------------------------------------------- 169 | 170 | html_theme = "qiskit-ecosystem" 171 | html_title = f"{project} {release}" 172 | 173 | intersphinx_mapping = { 174 | "python": ("https://docs.python.org/3", None), 175 | "numpy": ("https://numpy.org/doc/stable", None), 176 | "scipy": ("https://docs.scipy.org/doc/scipy", None), 177 | "qiskit_algorithms": ("https://qiskit-community.github.io/qiskit-algorithms", None), 178 | "qiskit_nature": ("https://qiskit-community.github.io/qiskit-nature", None), 179 | "qiskit": ("https://quantum.cloud.ibm.com/docs/api/qiskit", None), 180 | } 181 | 182 | html_context = {"analytics_enabled": True} 183 | -------------------------------------------------------------------------------- /docs/getting_started.rst: -------------------------------------------------------------------------------- 1 | :orphan: 2 | 3 | ############### 4 | Getting started 5 | ############### 6 | 7 | Installation 8 | ============ 9 | 10 | Qiskit Nature PySCF depends on the main Qiskit package which has its own 11 | `installation instructions `__ detailing the 12 | installation options for Qiskit and its supported environments/platforms. You should refer to 13 | that first. Then the information here can be followed which focuses on the additional installation 14 | specific to Qiskit Nature PySCF. 15 | 16 | .. tab-set:: 17 | 18 | .. tab-item:: Start locally 19 | 20 | .. code:: sh 21 | 22 | pip install qiskit-nature-pyscf 23 | 24 | 25 | .. tab-item:: Install from source 26 | 27 | Installing Qiskit Nature PySCF from source allows you to access the most recently 28 | updated version under development instead of using the version in the Python Package 29 | Index (PyPI) repository. This will give you the ability to inspect and extend 30 | the latest version of the Qiskit Nature PySCF code more efficiently. 31 | 32 | Since Qiskit Nature PySCF depends on Qiskit, and its latest changes may require new or changed 33 | features of Qiskit, you should first follow Qiskit's `"Install from source"` instructions 34 | `here `__ 35 | 36 | .. raw:: html 37 | 38 |

Installing Qiskit Nature PySCF from Source

39 | 40 | Using the same development environment that you installed Qiskit in you are ready to install 41 | Qiskit Nature PySCF. 42 | 43 | 1. Clone the Qiskit Nature PySCF repository. 44 | 45 | .. code:: sh 46 | 47 | git clone https://github.com/qiskit-community/qiskit-nature-pyscf.git 48 | 49 | 2. Cloning the repository creates a local folder called ``qiskit-nature-pyscf``. 50 | 51 | .. code:: sh 52 | 53 | cd qiskit-nature-pyscf 54 | 55 | 3. If you want to run tests or linting checks, install the developer requirements. 56 | 57 | .. code:: sh 58 | 59 | pip install -r requirements-dev.txt 60 | 61 | 4. Install ``qiskit-nature-pyscf``. 62 | 63 | .. code:: sh 64 | 65 | pip install . 66 | 67 | If you want to install it in editable mode, meaning that code changes to the 68 | project don't require a reinstall to be applied, you can do this with: 69 | 70 | .. code:: sh 71 | 72 | pip install -e . 73 | 74 | ---- 75 | 76 | 77 | .. Hiding - Indices and tables 78 | :ref:`genindex` 79 | :ref:`modindex` 80 | :ref:`search` 81 | -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiskit-community/qiskit-nature-pyscf/94a6543a916e3089b846fcf91307324ae3dfdf21/docs/images/logo.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | ############################ 2 | Qiskit Nature PySCF overview 3 | ############################ 4 | 5 | Overview 6 | ============== 7 | 8 | **Qiskit Nature PySCF** is a third-party plugin of Qiskit Nature + PySCF. 9 | To get started we suggest looking at the README on Github ``_. 10 | 11 | 12 | Citation 13 | ======== 14 | 15 | If you use this plugin, please cite the following references: 16 | 17 | - PySCF, as per `these instructions `_. 18 | - Qiskit, as per the provided `BibTeX file `_. 19 | - Qiskit Nature, as per https://doi.org/10.5281/zenodo.7828767 20 | 21 | 22 | Next Steps 23 | ================================= 24 | 25 | `Getting started `_ 26 | 27 | .. toctree:: 28 | :hidden: 29 | 30 | Overview 31 | Getting Started 32 | API Reference 33 | Release Notes 34 | GitHub 35 | 36 | 37 | .. Hiding - Indices and tables 38 | :ref:`genindex` 39 | :ref:`modindex` 40 | :ref:`search` 41 | -------------------------------------------------------------------------------- /docs/lowercase_filter.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2021, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ Implements a Lower Case Filter for Sphinx spelling """ 14 | 15 | from enchant import tokenize 16 | 17 | 18 | class LowercaseFilter(tokenize.Filter): 19 | """Lower Case Filter""" 20 | 21 | def _split(self, word): 22 | """Filter method for sub-tokenization of tokens. 23 | 24 | This method must be a tokenization function that will split the 25 | given word into sub-tokens according to the needs of the filter. 26 | The default behavior is not to split any words. 27 | """ 28 | # Don't split, just lower case to test against lowercase dict 29 | return super()._split(word.lower()) 30 | -------------------------------------------------------------------------------- /docs/release_notes.rst: -------------------------------------------------------------------------------- 1 | .. release-notes:: Release Notes 2 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | warn_unused_configs = True 3 | ignore_missing_imports = True 4 | strict_optional = False 5 | no_implicit_optional = True 6 | warn_redundant_casts = True 7 | warn_unused_ignores = True 8 | 9 | ### Output 10 | show_error_codes = True 11 | show_error_context = True 12 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools"] 3 | build-backend = "setuptools.build_meta" 4 | 5 | [tool.black] 6 | line-length = 100 7 | target-version = ['py38', 'py39', 'py310', 'py311'] 8 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.5.0 2 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | """ 13 | ========================================================= 14 | Qiskit's Nature PySCF module (:mod:`qiskit_nature_pyscf`) 15 | ========================================================= 16 | 17 | .. currentmodule:: qiskit_nature_pyscf 18 | 19 | .. autosummary:: 20 | :toctree: ../stubs/ 21 | :nosignatures: 22 | 23 | QiskitSolver 24 | PySCFGroundStateSolver 25 | """ 26 | 27 | from .qiskit_solver import QiskitSolver 28 | from .pyscf_ground_state_solver import PySCFGroundStateSolver 29 | from .version import __version__ 30 | 31 | __all__ = [ 32 | "QiskitSolver", 33 | "PySCFGroundStateSolver", 34 | "__version__", 35 | ] 36 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/py.typed: -------------------------------------------------------------------------------- 1 | # Marker file for PEP 561. 2 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/pyscf_ground_state_solver.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2023, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """A PySCF-based GroundStateSolver for Qiskit Nature.""" 14 | 15 | from __future__ import annotations 16 | 17 | import logging 18 | 19 | import numpy as np 20 | from pyscf import fci 21 | from qiskit.quantum_info import SparsePauliOp 22 | from qiskit_nature.second_q.algorithms import GroundStateSolver 23 | from qiskit_nature.second_q.operators import SparseLabelOp 24 | from qiskit_nature.second_q.operators.tensor_ordering import find_index_order, to_chemist_ordering 25 | from qiskit_nature.second_q.problems import ( 26 | BaseProblem, 27 | ElectronicStructureProblem, 28 | ElectronicStructureResult, 29 | ) 30 | from qiskit_nature.second_q.properties import ElectronicDensity 31 | 32 | LOGGER = logging.getLogger(__name__) 33 | 34 | 35 | class PySCFGroundStateSolver(GroundStateSolver): 36 | """A PySCF-based GroundStateSolver for Qiskit Nature. 37 | 38 | This class provides a :class:`~qiskit_nature.second_q.algorithms.GroundStateSolver` for Qiskit 39 | Nature, leveraging the ``fci`` module of PySCF. This is utility does not enable any Quantum 40 | algorithms to be used (since it replaces them in the Qiskit workflow) but instead provides a 41 | useful utility for debugging classical computational workflows based on Qiskit Nature. 42 | More importantly, it provides a more efficient implementation of what Qiskit otherwise achieves 43 | using a :class:`~qiskit_algorithms.NumPyMinimumEigensolver` in combination with a 44 | ``filter_criterion``. For non-singlet spin ground states the setup using Qiskit components is a 45 | lot more involved, whereas this class provides an easy-to-use alternative. 46 | 47 | Here is an example use cas: 48 | 49 | .. code-block:: python 50 | 51 | from pyscf import fci 52 | 53 | from qiskit_nature.second_q.drivers import MethodType, PySCFDriver 54 | from qiskit_nature.second_q.transformers import ActiveSpaceTransformer 55 | 56 | from qiskit_nature_pyscf import PySCFGroundStateSolver 57 | 58 | driver = PySCFDriver( 59 | atom="O 0.0 0.0 0.0; O 0.0 0.0 1.5", 60 | basis="sto3g", 61 | spin=2, 62 | method=MethodType.UHF, 63 | ) 64 | problem = driver.run() 65 | 66 | transformer = ActiveSpaceTransformer(4, 4) 67 | 68 | problem = transformer.transform(problem) 69 | 70 | solver = PySCFGroundStateSolver(fci.direct_uhf.FCI()) 71 | 72 | result = solver.solve(problem) 73 | print(result) 74 | 75 | For more details please to the documentation of `PySCF `_ and 76 | `Qiskit Nature `_. 77 | """ 78 | 79 | def __init__(self, solver: fci.direct_spin1.FCISolver) -> None: 80 | """ 81 | Args: 82 | solver: a FCI solver provided by PySCF. 83 | """ 84 | super().__init__(None) 85 | self._solver = solver 86 | 87 | def solve( 88 | self, 89 | problem: BaseProblem, 90 | aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, 91 | ) -> ElectronicStructureResult: 92 | """Finds the ground-state of the provided problem. 93 | 94 | This method is written to match the 95 | :class:`~qiskit_nature.second_q.algorithms.GroundStateSolver` API of Qiskit Nature but it 96 | does not support the evaluation of ``aux_operators`` and will ignore them. 97 | It is also only capable of solving an 98 | :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem` and will raise an error 99 | if another problem object is provided. 100 | 101 | Args: 102 | problem: the problem instance to be solved. 103 | aux_operators: **ignored**. 104 | 105 | Raises: 106 | TypeError: if a problem other than an 107 | :class:`~qiskit_nature.second_q.problems.ElectronicStructureProblem` is provided. 108 | 109 | Returns: 110 | The interpreted result object in Qiskit Nature format. 111 | """ 112 | if aux_operators is not None: 113 | LOGGER.warning( 114 | "This solver does not support auxiliary operators. They will be ignored." 115 | ) 116 | 117 | if not isinstance(problem, ElectronicStructureProblem): 118 | raise TypeError( 119 | "This solver only supports an ElectronicStructureProblem but you provided a " 120 | f"problem of type '{type(problem)}'." 121 | ) 122 | 123 | e_ints = problem.hamiltonian.electronic_integrals 124 | 125 | restricted_spin = e_ints.beta_alpha.is_empty() 126 | 127 | one_body: np.ndarray 128 | two_body: np.ndarray 129 | 130 | if restricted_spin: 131 | one_body = np.asarray(problem.hamiltonian.electronic_integrals.alpha["+-"]) 132 | two_body = np.asarray( 133 | to_chemist_ordering( 134 | problem.hamiltonian.electronic_integrals.alpha["++--"], 135 | ) 136 | ) 137 | else: 138 | one_body = np.asarray( 139 | [ 140 | problem.hamiltonian.electronic_integrals.alpha["+-"], 141 | problem.hamiltonian.electronic_integrals.beta["+-"], 142 | ] 143 | ) 144 | index_order = find_index_order(problem.hamiltonian.electronic_integrals.alpha["++--"]) 145 | two_body = np.asarray( 146 | [ 147 | to_chemist_ordering( 148 | problem.hamiltonian.electronic_integrals.alpha["++--"], 149 | index_order=index_order, 150 | ), 151 | to_chemist_ordering( 152 | problem.hamiltonian.electronic_integrals.alpha_beta["++--"], 153 | index_order=index_order, 154 | ), 155 | to_chemist_ordering( 156 | problem.hamiltonian.electronic_integrals.beta["++--"], 157 | index_order=index_order, 158 | ), 159 | ] 160 | ) 161 | 162 | energy, ci_vec = self.solver.kernel( 163 | h1e=one_body, 164 | eri=two_body, 165 | norb=problem.num_spatial_orbitals, 166 | nelec=problem.num_particles, 167 | ) 168 | 169 | if not isinstance(energy, np.ndarray): 170 | # computed a single root, but in the following we pretend to always have multiple ones 171 | energy = [energy] 172 | ci_vec = [ci_vec] 173 | 174 | raw_density = self.solver.make_rdm1s( 175 | ci_vec[0], norb=problem.num_spatial_orbitals, nelec=problem.num_particles 176 | ) 177 | density = ElectronicDensity.from_raw_integrals(raw_density[0], h1_b=raw_density[1]) 178 | 179 | overlap_ab: np.ndarray | None = None 180 | if problem.properties.angular_momentum is not None: 181 | overlap_ab = problem.properties.angular_momentum.overlap 182 | 183 | magnetization: list[float] = [] 184 | angular_momentum: list[float] = [] 185 | for vec in ci_vec: 186 | densities = self.solver.make_rdm12s( 187 | vec, norb=problem.num_spatial_orbitals, nelec=problem.num_particles 188 | ) 189 | spin_square, spin_z = self._compute_spin(densities, overlap_ab) 190 | magnetization.append(spin_z) 191 | angular_momentum.append(spin_square) 192 | 193 | result = ElectronicStructureResult() 194 | result.computed_energies = np.asarray(energy) 195 | result.hartree_fock_energy = problem.reference_energy 196 | result.extracted_transformer_energies = dict(problem.hamiltonian.constants.items()) 197 | result.nuclear_repulsion_energy = result.extracted_transformer_energies.pop( 198 | "nuclear_repulsion_energy", None 199 | ) 200 | result.num_particles = [sum(problem.num_particles)] * len(energy) 201 | result.magnetization = magnetization 202 | result.total_angular_momentum = angular_momentum 203 | # NOTE: the ElectronicStructureResult does not yet support multiple densities. Thus, we only 204 | # have the ground-state density here, regardless of how many roots were computed. 205 | result.electronic_density = density 206 | 207 | return result 208 | 209 | def get_qubit_operators( 210 | self, 211 | problem: BaseProblem, 212 | aux_operators: dict[str, SparseLabelOp | SparsePauliOp] | None = None, 213 | ) -> tuple[SparsePauliOp, dict[str, SparsePauliOp] | None]: 214 | """This solver does not deal with qubit operators and this method raises a RuntimeError.""" 215 | raise RuntimeError( 216 | "This solver is a purely classical one and as such solves an ElectronicStructureProblem" 217 | " on the second-quantization level. Thus, it has no concept of qubit operators and does" 218 | " not support this method." 219 | ) 220 | 221 | def supports_aux_operators(self) -> bool: 222 | """Returns whether the eigensolver supports auxiliary operators.""" 223 | return False 224 | 225 | @property 226 | def solver(self) -> fci.direct_spin1.FCISolver: 227 | """Returns the solver.""" 228 | return self._solver 229 | 230 | @staticmethod 231 | def _compute_spin( 232 | densities: tuple[tuple[np.ndarray, np.ndarray], tuple[np.ndarray, np.ndarray, np.ndarray]], 233 | overlap_ab: np.ndarray | None, 234 | ): 235 | (dm1a, dm1b), (dm2aa, dm2ab, dm2bb) = densities 236 | 237 | identity = np.eye(dm1a.shape[0]) 238 | if overlap_ab is None: 239 | overlap_ab = identity 240 | 241 | overlap_ba = overlap_ab.T 242 | 243 | # NOTE: we know for a fact, that overlap_aa and overlap_bb will always equal the identity 244 | # when dealing with a single wavefunction 245 | ssz = ( 246 | np.einsum("ijkl,ij,kl->", dm2aa, identity, identity) 247 | - np.einsum("ijkl,ij,kl->", dm2ab, identity, identity) 248 | + np.einsum("ijkl,ij,kl->", dm2bb, identity, identity) 249 | - np.einsum("ijkl,ij,kl->", dm2ab, identity, identity) 250 | + np.einsum("ji,ij->", dm1a, identity) 251 | + np.einsum("ji,ij->", dm1b, identity) 252 | ) * 0.25 253 | 254 | dm2abba = -dm2ab.transpose(0, 3, 2, 1) # alpha^+ beta^+ alpha beta 255 | dm2baab = -dm2ab.transpose(2, 1, 0, 3) # beta^+ alpha^+ beta alpha 256 | ssxy = ( 257 | np.einsum("ijkl,ij,kl->", dm2abba, overlap_ab, overlap_ba) 258 | + np.einsum("ijkl,ij,kl->", dm2baab, overlap_ba, overlap_ab) 259 | # NOTE: the following two lines are different from PySCF because we may deal with 260 | # non-unitary overlap_ab matrices 261 | + np.einsum("ji,ij->", dm1a, overlap_ab @ overlap_ba) 262 | + np.einsum("ji,ij->", dm1b, overlap_ba @ overlap_ab) 263 | ) * 0.5 264 | 265 | spin_square = ssxy + ssz 266 | 267 | return spin_square, np.sqrt(ssz) 268 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/qiskit_solver.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """A Qiskit-based FCI solver for PySCF.""" 14 | 15 | from __future__ import annotations 16 | 17 | import numpy as np 18 | 19 | from pyscf import ao2mo 20 | 21 | from qiskit_nature.second_q.algorithms import GroundStateSolver 22 | from qiskit_nature.second_q.hamiltonians import ElectronicEnergy 23 | from qiskit_nature.second_q.operators.tensor_ordering import IndexType, to_chemist_ordering 24 | from qiskit_nature.second_q.problems import ( 25 | ElectronicBasis, 26 | ElectronicStructureProblem, 27 | ElectronicStructureResult, 28 | ) 29 | from qiskit_nature.second_q.properties import ( 30 | AngularMomentum, 31 | ElectronicDensity, 32 | Magnetization, 33 | ParticleNumber, 34 | ) 35 | 36 | 37 | class QiskitSolver: 38 | """A Qiskit-based FCI solver for PySCF. 39 | 40 | This class bridges the APIs of PySCF and Qiskit Nature. You can use an instance of it to 41 | overwrite the ``fcisolver`` attribute of a :class:`pyscf.mcscf.casci.CASCI` object, in order to 42 | use a Quantum algorithm as provided by Qiskit for the CI solution step. 43 | Depending on the algorithm and its configuration, this may **not** result in the true FCI 44 | result, so care must be taken when using this class. 45 | 46 | Here is an example use case: 47 | 48 | .. code-block:: python 49 | 50 | from pyscf import gto, scf, mcscf 51 | 52 | from qiskit_algorithms import VQE 53 | from qiskit_algorithms.optimizers import SLSQP 54 | from qiskit.primitives import Estimator 55 | from qiskit_nature.second_q.algorithms import GroundStateEigensolver 56 | from qiskit_nature.second_q.circuit.library import HartreeFock, UCCSD 57 | from qiskit_nature.second_q.mappers import ParityMapper 58 | 59 | from qiskit_nature_pyscf import QiskitSolver 60 | 61 | mol = gto.M(atom="Li 0 0 0; H 0 0 1.51", basis="631g*") 62 | 63 | h_f = scf.RHF(mol).run() 64 | 65 | norb = 2 66 | nalpha, nbeta = 1, 1 67 | nelec = nalpha + nbeta 68 | 69 | cas = mcscf.CASCI(h_f, norb, nelec) 70 | 71 | mapper = ParityMapper(num_particles=(nalpha, nbeta)) 72 | 73 | ansatz = UCCSD( 74 | norb, 75 | (nalpha, nbeta), 76 | mapper, 77 | initial_state=HartreeFock( 78 | norb, 79 | (nalpha, nbeta), 80 | mapper, 81 | ), 82 | ) 83 | 84 | vqe = VQE(Estimator(), ansatz, SLSQP()) 85 | vqe.initial_point = np.zeros(ansatz.num_parameters) 86 | 87 | algorithm = GroundStateEigensolver(mapper, vqe) 88 | 89 | cas.fcisolver = QiskitSolver(algorithm) 90 | 91 | cas.run() 92 | 93 | For more details please to the documentation of `PySCF `_ and 94 | `Qiskit Nature `_. 95 | 96 | An instance of this class has the following attributes: 97 | 98 | Attributes: 99 | solver: a ground-state solver provided by Qiskit Nature. This must be supplied by the user 100 | prior to running the algorithm. 101 | density: an ElectronicDensity provided by Qiskit Nature. This is only available after the 102 | first iteration of the algorithm. 103 | result: an ElectronicStructureResult provided by Qiskit Nature. This is only available after 104 | the first iteration of the algorithm. 105 | """ 106 | 107 | def __init__(self, solver: GroundStateSolver): 108 | """ 109 | Args: 110 | solver: a ground-state solver provided by Qiskit Nature. 111 | """ 112 | self.solver = solver 113 | self.density: ElectronicDensity = None 114 | self.result: ElectronicStructureResult = None 115 | 116 | # pylint: disable=invalid-name,missing-type-doc 117 | def kernel( 118 | self, 119 | h1: np.ndarray | tuple[np.ndarray, np.ndarray], 120 | h2: np.ndarray | tuple[np.ndarray, np.ndarray, np.ndarray], 121 | norb: int, 122 | nelec: int | tuple[int, int], 123 | ci0=None, 124 | ecore: float = 0, 125 | **kwargs, 126 | ) -> tuple[float, QiskitSolver]: 127 | # pylint: disable=unused-argument 128 | """Finds the ground-state of the provided Hamiltonian. 129 | 130 | Args: 131 | h1: the one-body integrals. If this is a tuple, it indicates the alpha- and beta-spin 132 | coefficients, respectively. 133 | h2: the two-body integrals in chemists index ordering. If this is a tuple, it indicates the 134 | alpha-alpha-, alpha-beta-, and beta-beta-spin integrals, respectively. 135 | norb: the number of (active) orbitals. 136 | nelec: the number of (active) electrons. If this is an integer, it is the total number of 137 | electrons; if it is a tuple, it indicates the alpha- and beta-spin particles, 138 | respectively. 139 | ci0: the initial CI vector. (Currently ignored!) 140 | ecore: the core (inactive) energy. 141 | kwargs: any additional keyword arguments. (Currently ignored!) 142 | 143 | Returns: 144 | The pair of total evaluated energy (including ``ecore``) and a reference to this 145 | ``QiskitSolver`` itself. 146 | """ 147 | if isinstance(h1, tuple): 148 | h1_a, h1_b = h1 149 | else: 150 | h1_a, h1_b = h1, None 151 | if isinstance(h2, tuple): 152 | h2_aa, h2_ab, h2_bb = h2 153 | else: 154 | h2_aa, h2_ab, h2_bb = h2, None, None 155 | 156 | hamiltonian = ElectronicEnergy.from_raw_integrals( 157 | h1_a, 158 | ao2mo.restore(1, h2_aa, norb), 159 | h1_b=h1_b, 160 | h2_bb=h2_bb, 161 | h2_ba=h2_ab.T if h2_ab is not None else None, 162 | ) 163 | hamiltonian.constants["inactive energy"] = ecore 164 | 165 | problem = ElectronicStructureProblem(hamiltonian) 166 | problem.basis = ElectronicBasis.MO 167 | problem.num_spatial_orbitals = norb 168 | if not isinstance(nelec, tuple): 169 | nbeta = nelec // 2 170 | nalpha = nelec - nbeta 171 | nelec = (nalpha, nbeta) 172 | problem.num_particles = nelec 173 | 174 | if self.density is None: 175 | self.density = ElectronicDensity.from_orbital_occupation( 176 | problem.orbital_occupations, 177 | problem.orbital_occupations_b, 178 | ) 179 | 180 | problem.properties.particle_number = ParticleNumber(norb) 181 | problem.properties.magnetization = Magnetization(norb) 182 | problem.properties.angular_momentum = AngularMomentum(norb) 183 | problem.properties.electronic_density = self.density 184 | 185 | self.result = self.solver.solve(problem) 186 | self.density = self.result.electronic_density 187 | 188 | e_tot = self.result.total_energies[0] 189 | return e_tot, self 190 | 191 | approx_kernel = kernel 192 | 193 | def make_rdm1( 194 | self, fake_ci_vec: QiskitSolver, norb: int, nelec: int | tuple[int, int] 195 | ) -> np.ndarray: 196 | # pylint: disable=unused-argument 197 | """Constructs the spin-traced 1-RDM. 198 | 199 | Args: 200 | fake_ci_vec: the reference to the ``QiskitSolver``. 201 | norb: the number of (active) orbitals. (currently ignored!) 202 | nelec: the number of (active) electrons. (currently ignored!) 203 | 204 | Returns: 205 | The spin-traced 1-RDM. 206 | """ 207 | return np.asarray(fake_ci_vec.density.trace_spin()["+-"]) 208 | 209 | def make_rdm1s( 210 | self, fake_ci_vec: QiskitSolver, norb: int, nelec: int | tuple[int, int] 211 | ) -> tuple[np.ndarray, np.ndarray]: 212 | # pylint: disable=unused-argument 213 | """Constructs the alpha- and beta-spin 1-RDMs. 214 | 215 | Args: 216 | fake_ci_vec: the reference to the ``QiskitSolver``. 217 | norb: the number of (active) orbitals. (currently ignored!) 218 | nelec: the number of (active) electrons. (currently ignored!) 219 | 220 | Returns: 221 | The alpha- and beta-spin 1-RDMs. 222 | """ 223 | return np.asarray(fake_ci_vec.density.alpha["+-"]), np.asarray( 224 | fake_ci_vec.density.beta["+-"] 225 | ) 226 | 227 | def make_rdm12( 228 | self, fake_ci_vec: QiskitSolver, norb: int, nelec: int | tuple[int, int] 229 | ) -> tuple[np.ndarray, np.ndarray]: 230 | # pylint: disable=unused-argument 231 | """Constructs the spin-traced 1- and 2-RDMs. 232 | 233 | Args: 234 | fake_ci_vec: the reference to the ``QiskitSolver``. 235 | norb: the number of (active) orbitals. (currently ignored!) 236 | nelec: the number of (active) electrons. (currently ignored!) 237 | 238 | Returns: 239 | The spin-traced 1- and 2-RDMs. The 2-RDM is returned in chemist index ordering. 240 | """ 241 | traced = fake_ci_vec.density.trace_spin() 242 | return ( 243 | np.asarray(traced["+-"]), 244 | to_chemist_ordering(traced["++--"], index_order=IndexType.PHYSICIST), 245 | ) 246 | 247 | def make_rdm12s( 248 | self, fake_ci_vec: QiskitSolver, norb: int, nelec: int | tuple[int, int] 249 | ) -> tuple[tuple[np.ndarray, np.ndarray], tuple[np.ndarray, np.ndarray, np.ndarray]]: 250 | # pylint: disable=unused-argument 251 | """Constructs the alpha- and beta-spin 1- and 2-RDMs. 252 | 253 | Args: 254 | fake_ci_vec: the reference to the ``QiskitSolver``. 255 | norb: the number of (active) orbitals. (currently ignored!) 256 | nelec: the number of (active) electrons. (currently ignored!) 257 | 258 | Returns: 259 | A pair of tuples; the first tuple being the alpha- and beta-spin 1-RDMs; the second 260 | tuple being the alpha-alpha-, alpha-beta- and beta-beta-spin 2-RDMs in chemist index 261 | ordering. 262 | """ 263 | dm2_aa = to_chemist_ordering( 264 | fake_ci_vec.density.alpha["++--"], index_order=IndexType.PHYSICIST 265 | ) 266 | dm2_bb = to_chemist_ordering( 267 | fake_ci_vec.density.beta["++--"], index_order=IndexType.PHYSICIST 268 | ) 269 | dm2_ba = to_chemist_ordering( 270 | fake_ci_vec.density.beta_alpha["++--"], index_order=IndexType.PHYSICIST 271 | ) 272 | return (self.make_rdm1s(fake_ci_vec, norb, nelec), (dm2_aa, dm2_ba.T, dm2_bb)) 273 | -------------------------------------------------------------------------------- /qiskit_nature_pyscf/version.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Contains the Qiskit Nature PySCF version.""" 14 | 15 | import os 16 | import subprocess 17 | 18 | ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 19 | QISKIT_DIR = os.path.dirname(ROOT_DIR) 20 | 21 | 22 | def _minimal_ext_cmd(cmd): 23 | # construct minimal environment 24 | env = {} 25 | for k in ["SYSTEMROOT", "PATH"]: 26 | v = os.environ.get(k) 27 | if v is not None: 28 | env[k] = v 29 | # LANGUAGE is used on win32 30 | env["LANGUAGE"] = "C" 31 | env["LANG"] = "C" 32 | env["LC_ALL"] = "C" 33 | with subprocess.Popen( 34 | cmd, 35 | stdout=subprocess.PIPE, 36 | stderr=subprocess.PIPE, 37 | env=env, 38 | cwd=os.path.join(os.path.dirname(QISKIT_DIR)), 39 | ) as proc: 40 | stdout, stderr = proc.communicate() 41 | if proc.returncode > 0: 42 | raise OSError( 43 | f"Command {cmd} exited with code {proc.returncode}: {stderr.strip().decode('ascii')}" 44 | ) 45 | return stdout 46 | 47 | 48 | def git_version(): 49 | """Get the current git head sha1.""" 50 | # Determine if we're at main 51 | try: 52 | out = _minimal_ext_cmd(["git", "rev-parse", "HEAD"]) 53 | git_revision = out.strip().decode("ascii") 54 | except OSError: 55 | git_revision = "Unknown" 56 | 57 | return git_revision 58 | 59 | 60 | with open(os.path.join(ROOT_DIR, "VERSION.txt"), "r", encoding="utf8") as version_file: 61 | VERSION = version_file.read().strip() 62 | 63 | 64 | def get_version_info(): 65 | """Get the full version string.""" 66 | # Adding the git rev number needs to be done inside 67 | # write_version_py(), otherwise the import of scipy.version messes 68 | # up the build under Python 3. 69 | full_version = VERSION 70 | 71 | if not os.path.exists(os.path.join(os.path.dirname(QISKIT_DIR), ".git")): 72 | return full_version 73 | try: 74 | release = _minimal_ext_cmd(["git", "tag", "-l", "--points-at", "HEAD"]) 75 | except Exception: # pylint: disable=broad-except 76 | return full_version 77 | git_revision = git_version() 78 | if not release: 79 | full_version += ".dev0+" + git_revision[:7] 80 | 81 | return full_version 82 | 83 | 84 | __version__ = get_version_info() 85 | -------------------------------------------------------------------------------- /releasenotes/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | encoding: utf8 3 | default_branch: main 4 | -------------------------------------------------------------------------------- /releasenotes/notes/0.1/release-0.1.0-c2114e7d13fde810.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | First release of Qiskit Nature PySCF with the class 5 | :class:`qiskit_nature_pyscf.runtime.QiskitSolver` 6 | 7 | .. code-block:: python 8 | 9 | from pyscf import gto, scf, mcscf 10 | 11 | from qiskit.algorithms.optimizers import SLSQP 12 | from qiskit.primitives import Estimator 13 | from qiskit_nature.second_q.algorithms import GroundStateEigensolver, VQEUCCFactory 14 | from qiskit_nature.second_q.circuit.library import UCCSD 15 | from qiskit_nature.second_q.mappers import ParityMapper, QubitConverter 16 | 17 | from qiskit_nature_pyscf import QiskitSolver 18 | 19 | mol = gto.M(atom="Li 0 0 0; H 0 0 1.6", basis="sto-3g") 20 | 21 | h_f = scf.RHF(mol).run() 22 | 23 | norb, nelec = 2, 2 24 | 25 | cas = mcscf.CASCI(h_f, norb, nelec) 26 | 27 | converter = QubitConverter(ParityMapper(), two_qubit_reduction=True) 28 | 29 | vqe = VQEUCCFactory(Estimator(), UCCSD(), SLSQP()) 30 | 31 | algorithm = GroundStateEigensolver(converter, vqe) 32 | 33 | cas.fcisolver = QiskitSolver(algorithm) 34 | 35 | cas.run() 36 | -------------------------------------------------------------------------------- /releasenotes/notes/0.2/compatibility-with-qiskit-nature-0.6-7f1189fe41833678.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Ensures compatibility of this plugin with Qiskit Nature 0.6. 5 | The examples are updated in order to avoid triggering deprecation warnings. 6 | Using Qiskit Nature 0.5 is still supported. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/0.2/feat-pyscf-ground-state-solver-6a7866e29ca659f6.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adds the :class:`.PySCFGroundStateSolver` to provide simpler means for testing and debugging 5 | classical computational workflows in Qiskit Nature. Below is an example of how to use it: 6 | 7 | .. code-block:: python 8 | 9 | from pyscf import fci 10 | 11 | from qiskit_nature.second_q.drivers import MethodType, PySCFDriver 12 | from qiskit_nature.second_q.transformers import ActiveSpaceTransformer 13 | 14 | from qiskit_nature_pyscf import PySCFGroundStateSolver 15 | 16 | driver = PySCFDriver( 17 | atom="O 0.0 0.0 0.0; O 0.0 0.0 1.5", 18 | basis="sto3g", 19 | spin=2, 20 | method=MethodType.UHF, 21 | ) 22 | problem = driver.run() 23 | 24 | transformer = ActiveSpaceTransformer(4, 4) 25 | 26 | problem = transformer.transform(problem) 27 | 28 | solver = PySCFGroundStateSolver(fci.direct_uhf.FCI()) 29 | 30 | result = solver.solve(problem) 31 | print(result) 32 | -------------------------------------------------------------------------------- /releasenotes/notes/0.2/support-python-3.11-dd380066953fafbb.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added support for running with Python 3.11. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/0.3/drop-python-3.7-ee438003ebbd2645.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for running with Python 3.7 has been removed. You now need to use 5 | Python 3.8 as the minimum version. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/0.3/proper-qiskit-density-0582f26cc9a669d9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | The :class:`~qiskit_nature.second_q.properties.ElectronicDensity` object that was returned as part of the 5 | :class:`~qiskit_nature.second_q.problems.ElectronicStructureResult` from the :class:`.PySCFGroundStateSolver` 6 | was faultily storing restricted-spin information within the alpha-spin register even though the object is 7 | documented as storing unrestricted spin-data. This has been fixed and the 8 | :class:`~qiskit_nature.second_q.properties.ElectronicDensity` object is now guaranteed to store 9 | unrestricted-spin information. If a restricted-spin density is required it may be obtained from the 10 | :meth:`~qiskit_nature.second_q.properties.ElectronicDensity.trace_spin` method. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/0.3/support-multiple-roots-24f3119f247ab6f2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Support was added for ``FCISolver`` instances which have their ``nroots`` 5 | attribute set to a value higher than 1, indicating that they also compute 6 | excited states. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/0.3/use-qiskit-algorithms-89c7a3f72ca1ee9c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | The use of the deprecated ``qiskit.algorithms`` module has been replaced by 4 | the new ``qiskit-algorithms`` package which provide a drop-in replacement in 5 | the form of the ``qiskit_algorithms`` module. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/0.4/compute-spin-square-18c5c4678551f7be.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The :class:`.PySCFGroundStateSolver` now also computes the magnetization ($S^z$) 5 | and total angular momentum ($S^2$) values for all computes roots. In doing so, 6 | it properly takes the alpha-beta overlap matrix from the 7 | ``qiskit_nature.second_q.properties.AngularMomentum.overlap`` property into account. 8 | When this overlap is non-unitary, the spin-contamination within the active subspace 9 | is computed correctly. For more details, see also: 10 | 11 | * _ 12 | * _ 13 | * _ 14 | * _ 15 | -------------------------------------------------------------------------------- /releasenotes/notes/0.4/python-3.12-951eab86dddf803c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added support for Python 3.12. 5 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | coverage>=4.4.0 2 | matplotlib>=3.3 3 | black[jupyter]~=24.1 4 | pylint>=2.15.0 5 | pylatexenc>=1.4 6 | stestr>=2.0.0 7 | ddt>=1.2.0,!=1.4.0,!=1.4.3 8 | reno>=3.4.0 9 | Sphinx>=5.0 10 | sphinx-design>=0.4.1 11 | sphinxcontrib-spelling!=7.3.1 12 | jupyter-sphinx 13 | discover 14 | mypy>=0.991 15 | mypy-extensions>=0.4.3 16 | nbsphinx 17 | qiskit_sphinx_theme~=1.16.0 18 | types-certifi 19 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | qiskit-nature>=0.7.1 2 | pyscf>=2.0 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2024. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | import inspect 14 | import os 15 | import re 16 | import sys 17 | 18 | import setuptools 19 | 20 | with open("requirements.txt", "r", encoding="utf-8") as f: 21 | REQUIREMENTS = f.read().splitlines() 22 | 23 | VERSION_PATH = os.path.join(os.path.dirname(__file__), "qiskit_nature_pyscf", "VERSION.txt") 24 | with open(VERSION_PATH, "r", encoding="utf-8") as version_file: 25 | VERSION = version_file.read().strip() 26 | 27 | # Read long description from README. 28 | README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "README.md") 29 | with open(README_PATH, "r", encoding="utf-8") as readme_file: 30 | README = re.sub( 31 | ".*", 32 | "", 33 | readme_file.read(), 34 | flags=re.S | re.M, 35 | ) 36 | 37 | setuptools.setup( 38 | name="qiskit-nature-pyscf", 39 | version=VERSION, 40 | description="Qiskit Nature PySCF: Third-party integration plugin of Qiskit Nature + PySCF.", 41 | long_description=README, 42 | long_description_content_type="text/markdown", 43 | url="https://github.com/qiskit-community/qiskit-nature-pyscf", 44 | author="Qiskit Nature PySCF Development Team", 45 | author_email="qiskit@us.ibm.com", 46 | license="Apache-2.0", 47 | classifiers=[ 48 | "Environment :: Console", 49 | "License :: OSI Approved :: Apache Software License", 50 | "Intended Audience :: Developers", 51 | "Intended Audience :: Science/Research", 52 | "Operating System :: MacOS", 53 | "Operating System :: POSIX :: Linux", 54 | "Programming Language :: Python :: 3 :: Only", 55 | "Programming Language :: Python :: 3.8", 56 | "Programming Language :: Python :: 3.9", 57 | "Programming Language :: Python :: 3.10", 58 | "Programming Language :: Python :: 3.11", 59 | "Programming Language :: Python :: 3.12", 60 | "Topic :: Scientific/Engineering", 61 | ], 62 | keywords="qiskit sdk quantum nature chemistry physics pyscf", 63 | packages=setuptools.find_packages(include=["qiskit_nature_pyscf", "qiskit_nature_pyscf.*"]), 64 | install_requires=REQUIREMENTS, 65 | include_package_data=True, 66 | python_requires=">=3.8", 67 | project_urls={ 68 | "Bug Tracker": "https://github.com/qiskit-community/qiskit-nature-pyscf/issues", 69 | "Documentation": "https://qiskit-community.github.io/qiskit-nature-pyscf/", 70 | "Source Code": "https://github.com/qiskit-community/qiskit-nature-pyscf", 71 | }, 72 | zip_safe=False, 73 | ) 74 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2018, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ Qiskit Nature PySCF test packages """ 14 | 15 | from .nature_pyscf_test_case import QiskitNaturePySCFTestCase 16 | 17 | __all__ = ["QiskitNaturePySCFTestCase"] 18 | -------------------------------------------------------------------------------- /test/nature_pyscf_test_case.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Qiskit Nature PySCF Test Case""" 14 | 15 | from typing import Optional 16 | from abc import ABC 17 | import warnings 18 | import inspect 19 | import logging 20 | import os 21 | import unittest 22 | import time 23 | 24 | # disable deprecation warnings that can cause log output overflow 25 | # pylint: disable=unused-argument 26 | 27 | 28 | def _noop(*args, **kargs): 29 | pass 30 | 31 | 32 | # disable warning messages 33 | # warnings.warn = _noop 34 | 35 | 36 | class QiskitNaturePySCFTestCase(unittest.TestCase, ABC): 37 | """Qiskit Nature PySCF Test Case""" 38 | 39 | moduleName = None 40 | log = None 41 | 42 | def setUp(self) -> None: 43 | warnings.filterwarnings("default", category=DeprecationWarning) 44 | self._started_at = time.time() 45 | self._class_location = __file__ 46 | 47 | def tearDown(self) -> None: 48 | elapsed = time.time() - self._started_at 49 | if elapsed > 5.0: 50 | print(f"({round(elapsed, 2):.2f}s)", flush=True) 51 | 52 | @classmethod 53 | def setUpClass(cls) -> None: 54 | cls.moduleName = os.path.splitext(inspect.getfile(cls))[0] 55 | cls.log = logging.getLogger(cls.__name__) 56 | 57 | # Set logging to file and stdout if the LOG_LEVEL environment variable 58 | # is set. 59 | if os.getenv("LOG_LEVEL"): 60 | # Set up formatter. 61 | log_fmt = f"{cls.__name__}.%(funcName)s:%(levelname)s:%(asctime)s:" " %(message)s" 62 | formatter = logging.Formatter(log_fmt) 63 | 64 | # Set up the file handler. 65 | log_file_name = f"{cls.moduleName}.log" 66 | file_handler = logging.FileHandler(log_file_name) 67 | file_handler.setFormatter(formatter) 68 | cls.log.addHandler(file_handler) 69 | 70 | # Set the logging level from the environment variable, defaulting 71 | # to INFO if it is not a valid level. 72 | level = logging._nameToLevel.get(os.getenv("LOG_LEVEL"), logging.INFO) 73 | cls.log.setLevel(level) 74 | logger = logging.getLogger("qiskit_nature_pyscf") 75 | logger.addHandler(file_handler) 76 | logger.setLevel(level) 77 | 78 | def get_resource_path(self, filename: str, path: Optional[str] = None) -> str: 79 | """Get the absolute path to a resource. 80 | Args: 81 | filename: filename or relative path to the resource. 82 | path: path used as relative to the filename. 83 | Returns: 84 | str: the absolute path to the resource. 85 | """ 86 | root = os.path.dirname(self._class_location) 87 | path = root if path is None else os.path.join(root, path) 88 | return os.path.normpath(os.path.join(path, filename)) 89 | -------------------------------------------------------------------------------- /test/test_pyscf_ground_state_solver.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Tests for the PySCFGroundStateSolver.""" 14 | 15 | from test import QiskitNaturePySCFTestCase 16 | 17 | from pyscf import mcscf, fci 18 | from qiskit_nature.second_q.drivers import MethodType, PySCFDriver 19 | from qiskit_nature.second_q.transformers import ActiveSpaceTransformer 20 | 21 | from qiskit_nature_pyscf import PySCFGroundStateSolver 22 | 23 | 24 | class TestPySCFGroundStateSolver(QiskitNaturePySCFTestCase): 25 | """Tests for the PySCFGroundStateSolver.""" 26 | 27 | def test_direct_spin1_fci(self): 28 | """Test with the ``fci.direct_spin1.FCISolver``.""" 29 | driver = PySCFDriver( 30 | atom="H 0.0 0.0 0.0; H 0.0 0.0 1.5", 31 | basis="sto3g", 32 | ) 33 | problem = driver.run() 34 | 35 | solver = PySCFGroundStateSolver(fci.direct_spin1.FCI()) 36 | 37 | result = solver.solve(problem) 38 | 39 | casci = mcscf.CASCI(driver._calc, 2, 2) 40 | casci.run() 41 | 42 | self.assertAlmostEqual(casci.e_tot, result.total_energies[0]) 43 | self.assertAlmostEqual(result.magnetization[0], 0.0) 44 | self.assertAlmostEqual(result.total_angular_momentum[0], 0.0) 45 | 46 | def test_direct_uhf_fci(self): 47 | """Test with the ``fci.direct_uhf.FCISolver``.""" 48 | driver = PySCFDriver( 49 | atom="O 0.0 0.0 0.0; O 0.0 0.0 1.5", 50 | basis="sto3g", 51 | spin=2, 52 | method=MethodType.UHF, 53 | ) 54 | problem = driver.run() 55 | 56 | transformer = ActiveSpaceTransformer(4, 4) 57 | 58 | problem = transformer.transform(problem) 59 | 60 | solver = PySCFGroundStateSolver(fci.direct_uhf.FCI()) 61 | 62 | result = solver.solve(problem) 63 | 64 | casci = mcscf.UCASCI(driver._calc, 4, 4) 65 | casci.run() 66 | 67 | self.assertAlmostEqual(casci.e_tot, result.total_energies[0]) 68 | self.assertAlmostEqual(result.magnetization[0], 1.0) 69 | self.assertAlmostEqual(result.total_angular_momentum[0], 1.99988454) 70 | 71 | def test_nroots_support(self): 72 | """Test support for more than ground-state calculations.""" 73 | driver = PySCFDriver( 74 | atom="H 0.0 0.0 0.0; H 0.0 0.0 1.5", 75 | basis="sto3g", 76 | ) 77 | problem = driver.run() 78 | 79 | fci_solver = fci.direct_spin1.FCI() 80 | fci_solver.nroots = 2 81 | solver = PySCFGroundStateSolver(fci_solver) 82 | 83 | result = solver.solve(problem) 84 | 85 | casci = mcscf.CASCI(driver._calc, 2, 2) 86 | casci.fcisolver.nroots = 2 87 | casci.run() 88 | 89 | self.assertAlmostEqual(casci.e_tot[0], result.total_energies[0]) 90 | self.assertAlmostEqual(casci.e_tot[1], result.total_energies[1]) 91 | self.assertAlmostEqual(result.num_particles[0], 2.0) 92 | self.assertAlmostEqual(result.num_particles[1], 2.0) 93 | self.assertAlmostEqual(result.magnetization[0], 0.0) 94 | self.assertAlmostEqual(result.magnetization[1], 0.0) 95 | self.assertAlmostEqual(result.total_angular_momentum[0], 0.0) 96 | self.assertAlmostEqual(result.total_angular_momentum[1], 2.0) 97 | -------------------------------------------------------------------------------- /test/test_qiskit_solver.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """Tests for the QiskitSolver.""" 14 | 15 | from test import QiskitNaturePySCFTestCase 16 | 17 | from pyscf import gto, scf, mcscf 18 | 19 | from qiskit_algorithms import NumPyMinimumEigensolver 20 | from qiskit_nature.second_q.algorithms import GroundStateEigensolver 21 | from qiskit_nature.second_q.mappers import JordanWignerMapper 22 | from qiskit_nature_pyscf.qiskit_solver import QiskitSolver 23 | 24 | 25 | class TestQiskitSolver(QiskitNaturePySCFTestCase): 26 | """Tests for the QiskitSolver.""" 27 | 28 | def test_rhf_casci_h2(self): 29 | """Test the RHF and CASCI method on H2.""" 30 | mol = gto.M(atom="H 0 0 0; H 0 0 0.735", basis="631g*") 31 | 32 | h_f = scf.RHF(mol).run() 33 | 34 | norb, nelec = 2, 2 35 | 36 | cas_ref = mcscf.CASCI(h_f, norb, nelec).run() 37 | 38 | qcas = mcscf.CASCI(h_f, norb, nelec) 39 | 40 | ground_state_solver = GroundStateEigensolver( 41 | JordanWignerMapper(), 42 | NumPyMinimumEigensolver(), 43 | ) 44 | 45 | qcas.fcisolver = QiskitSolver(ground_state_solver) 46 | 47 | qcas.run() 48 | 49 | self.assertAlmostEqual(qcas.e_tot, cas_ref.e_tot) 50 | 51 | def test_uhf_ucasci_h2(self): 52 | """Test the UHF and UCASCI method on H2.""" 53 | mol = gto.M(atom="H 0 0 0; H 0 0 0.735", basis="631g*") 54 | 55 | h_f = scf.UHF(mol).run() 56 | 57 | norb, nelec = 2, 2 58 | 59 | cas_ref = mcscf.UCASCI(h_f, norb, nelec).run() 60 | 61 | qcas = mcscf.UCASCI(h_f, norb, nelec) 62 | 63 | ground_state_solver = GroundStateEigensolver( 64 | JordanWignerMapper(), 65 | NumPyMinimumEigensolver(), 66 | ) 67 | 68 | qcas.fcisolver = QiskitSolver(ground_state_solver) 69 | 70 | qcas.run() 71 | 72 | self.assertAlmostEqual(qcas.e_tot, cas_ref.e_tot) 73 | 74 | def test_rhf_casscf_h2(self): 75 | """Test the RHF and CASSCF method on H2.""" 76 | mol = gto.M(atom="H 0 0 0; H 0 0 0.735", basis="631g*") 77 | 78 | h_f = scf.RHF(mol).run() 79 | 80 | norb, nelec = 2, 2 81 | 82 | cas_ref = mcscf.CASSCF(h_f, norb, nelec).run() 83 | 84 | qcas = mcscf.CASSCF(h_f, norb, nelec) 85 | 86 | ground_state_solver = GroundStateEigensolver( 87 | JordanWignerMapper(), 88 | NumPyMinimumEigensolver(), 89 | ) 90 | 91 | qcas.fcisolver = QiskitSolver(ground_state_solver) 92 | 93 | qcas.run() 94 | 95 | self.assertAlmostEqual(qcas.e_tot, cas_ref.e_tot) 96 | 97 | def test_uhf_ucasscf_h2(self): 98 | """Test the UHF and UCASSCF method on H2.""" 99 | mol = gto.M(atom="H 0 0 0; H 0 0 0.735", basis="631g*") 100 | 101 | h_f = scf.UHF(mol).run() 102 | 103 | norb, nelec = 2, 2 104 | 105 | cas_ref = mcscf.UCASSCF(h_f, norb, nelec).run() 106 | 107 | qcas = mcscf.UCASSCF(h_f, norb, nelec) 108 | 109 | ground_state_solver = GroundStateEigensolver( 110 | JordanWignerMapper(), 111 | NumPyMinimumEigensolver(), 112 | ) 113 | 114 | qcas.fcisolver = QiskitSolver(ground_state_solver) 115 | 116 | qcas.run() 117 | 118 | self.assertAlmostEqual(qcas.e_tot, cas_ref.e_tot) 119 | -------------------------------------------------------------------------------- /test/test_readme_sample.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ 14 | Code inside the test is the Qiskit Nature PySCF sample from the readme. 15 | If this test fails and code changes are needed here to resolve 16 | the issue then ensure changes are made to readme too. 17 | """ 18 | 19 | import unittest 20 | 21 | import contextlib 22 | import io 23 | from pathlib import Path 24 | import re 25 | from test import QiskitNaturePySCFTestCase 26 | 27 | 28 | class TestReadmeSample(QiskitNaturePySCFTestCase): 29 | """Test sample code from readme""" 30 | 31 | def test_readme_sample(self): 32 | """readme sample test""" 33 | # pylint: disable=exec-used 34 | 35 | readme_name = "README.md" 36 | readme_path = Path(__file__).parent.parent.joinpath(readme_name) 37 | if not readme_path.exists() or not readme_path.is_file(): 38 | self.fail(msg=f"{readme_name} not found at {readme_path}") 39 | return 40 | 41 | # gets the first matched code sample 42 | # assumes one code sample to test per readme 43 | readme_sample = None 44 | with open(readme_path, encoding="UTF-8") as readme_file: 45 | match_sample = re.search( 46 | "```python.*```", 47 | readme_file.read(), 48 | flags=re.S, 49 | ) 50 | if match_sample: 51 | # gets the matched string stripping the markdown code block 52 | readme_sample = match_sample.group(0)[9:-3] 53 | 54 | if readme_sample is None: 55 | self.skipTest(f"No sample found inside {readme_name}.") 56 | return 57 | 58 | with contextlib.redirect_stdout(io.StringIO()) as out: 59 | try: 60 | exec(readme_sample) 61 | except Exception as ex: # pylint: disable=broad-except 62 | self.fail(str(ex)) 63 | return 64 | 65 | texts = out.getvalue().split("\n") 66 | energy = float(texts[0][len("converged SCF energy = ") :]) 67 | self.assertAlmostEqual(energy, -7.86186476980865, places=6) 68 | 69 | 70 | if __name__ == "__main__": 71 | unittest.main() 72 | -------------------------------------------------------------------------------- /tools/check_copyright.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ Fix copyright year in header """ 14 | 15 | from typing import Tuple, Union, List 16 | import builtins 17 | import sys 18 | import os 19 | import datetime 20 | import argparse 21 | import subprocess 22 | import traceback 23 | 24 | 25 | class CopyrightChecker: 26 | """Check copyright""" 27 | 28 | _UTF_STRING = "# -*- coding: utf-8 -*-" 29 | _COPYRIGHT_STRING = "# (C) Copyright IBM " 30 | 31 | def __init__(self, root_dir: str, check: bool) -> None: 32 | self._root_dir = root_dir 33 | self._check = check 34 | self._current_year = datetime.datetime.now().year 35 | self._changed_files = self._get_changed_files() 36 | 37 | @staticmethod 38 | def _exception_to_string(excp: Exception) -> str: 39 | stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__) 40 | pretty = traceback.format_list(stack) 41 | return "".join(pretty) + f"\n {excp.__class__} {excp}" 42 | 43 | @staticmethod 44 | def _get_year_from_date(date) -> int: 45 | if not date or len(date) < 4: 46 | return None 47 | 48 | return int(date[:4]) 49 | 50 | def _cmd_execute(self, args: List[str]) -> Tuple[str, Union[None, str]]: 51 | # execute command 52 | env = {} 53 | for k in ["SYSTEMROOT", "PATH"]: 54 | v = os.environ.get(k) 55 | if v is not None: 56 | env[k] = v 57 | # LANGUAGE is used on win32 58 | env["LANGUAGE"] = "C" 59 | env["LANG"] = "C" 60 | env["LC_ALL"] = "C" 61 | with subprocess.Popen( 62 | args, 63 | cwd=self._root_dir, 64 | env=env, 65 | stdin=subprocess.DEVNULL, 66 | stdout=subprocess.PIPE, 67 | stderr=subprocess.PIPE, 68 | ) as popen: 69 | out, err = popen.communicate() 70 | popen.wait() 71 | out_str = out.decode("utf-8").strip() 72 | err_str = err.decode("utf-8").strip() 73 | err_str = err_str if err_str else None 74 | return out_str, err_str 75 | 76 | def _get_changed_files(self) -> List[str]: 77 | out_str, err_str = self._cmd_execute(["git", "diff", "--name-only", "HEAD"]) 78 | if err_str: 79 | raise builtins.Exception(err_str) 80 | 81 | return out_str.splitlines() 82 | 83 | def _get_file_last_year(self, relative_path: str) -> int: 84 | last_year = None 85 | errors = [] 86 | try: 87 | out_str, err_str = self._cmd_execute( 88 | ["git", "log", "-1", "--format=%cI", relative_path] 89 | ) 90 | last_year = CopyrightChecker._get_year_from_date(out_str) 91 | if err_str: 92 | errors.append(err_str) 93 | except Exception as ex: # pylint: disable=broad-except 94 | errors.append(f"'{relative_path}' Last year: {str(ex)}") 95 | 96 | if errors: 97 | raise ValueError(" - ".join(errors)) 98 | 99 | return last_year 100 | 101 | def check_copyright(self, file_path) -> Tuple[bool, bool, bool]: 102 | """check copyright for a file""" 103 | file_with_utf8 = False 104 | file_with_invalid_year = False 105 | file_has_header = False 106 | try: 107 | new_line = "# (C) Copyright IBM " 108 | idx_utf8 = -1 109 | idx_new_line = -1 110 | file_lines = None 111 | with open(file_path, "rt", encoding="utf8") as file: 112 | file_lines = file.readlines() 113 | for idx, line in enumerate(file_lines): 114 | relative_path = os.path.relpath(file_path, self._root_dir) 115 | if line.startswith(CopyrightChecker._UTF_STRING): 116 | if self._check: 117 | print(f"File contains utf-8 header: '{relative_path}'") 118 | file_with_utf8 = True 119 | idx_utf8 = idx 120 | 121 | if not line.startswith(CopyrightChecker._COPYRIGHT_STRING): 122 | continue 123 | 124 | file_has_header = True 125 | curr_years = [] 126 | for word in line.strip().split(): 127 | for year in word.strip().split(","): 128 | if year.startswith("20") and len(year) >= 4: 129 | try: 130 | curr_years.append(int(year[0:4])) 131 | except ValueError: 132 | pass 133 | 134 | header_start_year = None 135 | header_last_year = None 136 | if len(curr_years) > 1: 137 | header_start_year = curr_years[0] 138 | header_last_year = curr_years[1] 139 | elif len(curr_years) == 1: 140 | header_start_year = header_last_year = curr_years[0] 141 | 142 | if relative_path in self._changed_files: 143 | self._changed_files.remove(relative_path) 144 | last_year = self._current_year 145 | else: 146 | last_year = self._get_file_last_year(relative_path) 147 | if last_year and header_last_year != last_year: 148 | if header_start_year and header_start_year != last_year: 149 | new_line += f"{header_start_year}, " 150 | 151 | new_line += f"{self._current_year}.\n" 152 | if self._check: 153 | print( 154 | f"Wrong Copyright Year:'{relative_path}': ", 155 | f"Current:'{line[:-1]}' Correct:'{new_line[:-1]}'", 156 | ) 157 | file_with_invalid_year = True 158 | idx_new_line = idx 159 | 160 | break 161 | if not self._check and (idx_utf8 >= 0 or idx_new_line >= 0): 162 | if idx_new_line >= 0: 163 | file_lines[idx_new_line] = new_line 164 | if idx_utf8 >= 0: 165 | del file_lines[idx_utf8] 166 | with open(file_path, "w", encoding="utf8") as file: 167 | file.writelines(file_lines) 168 | if idx_new_line >= 0: 169 | file_with_invalid_year = False 170 | print(f"Fixed copyright year for {relative_path}.") 171 | if idx_utf8 >= 0: 172 | file_with_utf8 = False 173 | print(f"Removed utf-8 header for {relative_path}.") 174 | 175 | except UnicodeDecodeError: 176 | return file_with_utf8, file_with_invalid_year, file_has_header 177 | 178 | return file_with_utf8, file_with_invalid_year, file_has_header 179 | 180 | def check(self) -> Tuple[int, int, int]: 181 | """check copyright""" 182 | return self._check_copyright(self._root_dir) 183 | 184 | def _check_copyright(self, path: str) -> Tuple[int, int, int]: 185 | files_with_utf8 = 0 186 | files_with_invalid_year = 0 187 | files_with_header = 0 188 | for item in os.listdir(path): 189 | fullpath = os.path.join(path, item) 190 | if os.path.isdir(fullpath): 191 | if not item.startswith("."): 192 | files = self._check_copyright(fullpath) 193 | files_with_utf8 += files[0] 194 | files_with_invalid_year += files[1] 195 | files_with_header += files[2] 196 | continue 197 | 198 | if os.path.isfile(fullpath): 199 | # check copyright year 200 | ( 201 | file_with_utf8, 202 | file_with_invalid_year, 203 | file_has_header, 204 | ) = self.check_copyright(fullpath) 205 | if file_with_utf8: 206 | files_with_utf8 += 1 207 | if file_with_invalid_year: 208 | files_with_invalid_year += 1 209 | if file_has_header: 210 | files_with_header += 1 211 | 212 | return files_with_utf8, files_with_invalid_year, files_with_header 213 | 214 | 215 | def check_path(path): 216 | """valid path argument""" 217 | if not path or os.path.isdir(path): 218 | return path 219 | 220 | raise argparse.ArgumentTypeError(f"readable_dir:{path} is not a valid path") 221 | 222 | 223 | if __name__ == "__main__": 224 | PARSER = argparse.ArgumentParser(description="Check Copyright Tool") 225 | PARSER.add_argument("-path", type=check_path, metavar="path", help="Root path of project.") 226 | PARSER.add_argument( 227 | "-check", 228 | required=False, 229 | action="store_true", 230 | help="Just check copyright, without fixing it.", 231 | ) 232 | 233 | ARGS = PARSER.parse_args() 234 | if not ARGS.path: 235 | ARGS.path = os.getcwd() 236 | 237 | ARGS.path = os.path.abspath(os.path.realpath(os.path.expanduser(ARGS.path))) 238 | INVALID_UTF8, INVALID_YEAR, HAS_HEADER = CopyrightChecker(ARGS.path, ARGS.check).check() 239 | print(f"{INVALID_UTF8} files have utf8 headers.") 240 | print(f"{INVALID_YEAR} of {HAS_HEADER} files with copyright header have wrong years.") 241 | 242 | sys.exit(0 if INVALID_UTF8 == 0 and INVALID_YEAR == 0 else 1) 243 | -------------------------------------------------------------------------------- /tools/extract_deprecation.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ Extract deprecation messages from input """ 14 | 15 | from typing import List 16 | import sys 17 | import os 18 | import argparse 19 | 20 | 21 | class DeprecationExtractor: 22 | """Extract deprecation messages""" 23 | 24 | def __init__(self, in_file: str, out_file: str) -> None: 25 | self._input_filename = in_file 26 | self._output_filename = out_file 27 | self._messages = None # type: List[str] 28 | 29 | def extract_messages(self) -> bool: 30 | """ 31 | extract deprecation 32 | Returns: 33 | bool: if messages were found 34 | """ 35 | 36 | self._messages = None 37 | messages = set() 38 | with open(self._input_filename, "rt", encoding="utf8", errors="ignore") as file: 39 | for line in file: 40 | if line.find("DeprecationWarning:") > 0: 41 | messages.add(line.strip()) 42 | 43 | if messages: 44 | self._messages = sorted(messages) 45 | return True 46 | 47 | return False 48 | 49 | def save_to_output(self, force_create: bool) -> bool: 50 | """ 51 | save messages to file if they exist 52 | Args: 53 | force_create: create file even if it is empty 54 | Returns: 55 | bool: if messages were saved 56 | """ 57 | if self._output_filename: 58 | # create file even if it is empty 59 | if self._messages or force_create: 60 | with open(self._output_filename, "w", encoding="utf8") as file: 61 | if self._messages: 62 | file.write("\n".join(self._messages)) 63 | return True 64 | 65 | return False 66 | 67 | def print_messages(self) -> None: 68 | """print messages""" 69 | if self._messages: 70 | print("---------------------") 71 | print("Deprecation Messages:") 72 | print("---------------------") 73 | for line in self._messages: 74 | print(line) 75 | 76 | 77 | def _check_file(path) -> str: 78 | if not os.path.isfile(path): 79 | raise argparse.ArgumentTypeError(f"file: '{path}' doesn't exist.") 80 | 81 | return path 82 | 83 | 84 | if __name__ == "__main__": 85 | PARSER = argparse.ArgumentParser(description="Qiskit Extract Deprecation Messages Tool") 86 | PARSER.add_argument( 87 | "-file", type=_check_file, required=True, metavar="file", help="Input file." 88 | ) 89 | PARSER.add_argument("-output", metavar="output", help="Output file.") 90 | 91 | ARGS = PARSER.parse_args() 92 | 93 | OBJ = DeprecationExtractor(ARGS.file, ARGS.output) 94 | OBJ.extract_messages() 95 | OBJ.save_to_output(True) 96 | OBJ.print_messages() 97 | 98 | sys.exit(0) 99 | -------------------------------------------------------------------------------- /tools/find_stray_release_notes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This code is part of a Qiskit project. 3 | # 4 | # (C) Copyright IBM 2022, 2023. 5 | # 6 | # This code is licensed under the Apache License, Version 2.0. You may 7 | # obtain a copy of this license in the LICENSE.txt file in the root directory 8 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 9 | # 10 | # Any modifications or derivative works of this code must retain this 11 | # copyright notice, and modified files need to carry a notice indicating 12 | # that they have been altered from the originals. 13 | 14 | """Utility script to verify qiskit copyright file headers""" 15 | 16 | import argparse 17 | import multiprocessing 18 | import subprocess 19 | import sys 20 | import re 21 | 22 | # release notes regex 23 | reno = re.compile(r"releasenotes\/notes") 24 | # exact release note regex 25 | exact_reno = re.compile(r"^releasenotes\/notes") 26 | 27 | 28 | def discover_files(): 29 | """Find all .py, .pyx, .pxd files in a list of trees""" 30 | cmd = ["git", "ls-tree", "-r", "--name-only", "HEAD"] 31 | res = subprocess.run(cmd, capture_output=True, check=True, encoding="UTF8") 32 | files = res.stdout.split("\n") 33 | return files 34 | 35 | 36 | def validate_path(file_path): 37 | """Validate a path in the git tree.""" 38 | if reno.search(file_path) and not exact_reno.search(file_path): 39 | return file_path 40 | return None 41 | 42 | 43 | def _main(): 44 | parser = argparse.ArgumentParser(description="Find any stray release notes.") 45 | _args = parser.parse_args() 46 | files = discover_files() 47 | with multiprocessing.Pool() as pool: 48 | res = pool.map(validate_path, files) 49 | failed_files = [x for x in res if x is not None] 50 | if len(failed_files) > 0: 51 | for failed_file in failed_files: 52 | sys.stderr.write(f"{failed_file} is not in the correct location.\n") 53 | sys.exit(1) 54 | sys.exit(0) 55 | 56 | 57 | if __name__ == "__main__": 58 | _main() 59 | -------------------------------------------------------------------------------- /tools/generate_spell_dict.py: -------------------------------------------------------------------------------- 1 | # This code is part of a Qiskit project. 2 | # 3 | # (C) Copyright IBM 2022, 2023. 4 | # 5 | # This code is licensed under the Apache License, Version 2.0. You may 6 | # obtain a copy of this license in the LICENSE.txt file in the root directory 7 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 8 | # 9 | # Any modifications or derivative works of this code must retain this 10 | # copyright notice, and modified files need to carry a notice indicating 11 | # that they have been altered from the originals. 12 | 13 | """ Generates spelling dictionaries for Sphinx and Pylint and combine them. """ 14 | 15 | from typing import Set, List 16 | import sys 17 | import os 18 | import argparse 19 | import shutil 20 | import errno 21 | import tempfile 22 | from pathlib import Path 23 | from sphinx.cmd.build import build_main as sphinx_build 24 | from pylint import lint 25 | 26 | 27 | class SpellDictGenerator: 28 | """Generates spelling dictionaries for Sphinx and Pylint""" 29 | 30 | _DOCS_DIR = "docs" 31 | _BUILD_DIR = "_build" 32 | _STUBS_DIR = "stubs" 33 | _JUPYTER_EXECUTE_DIR = "jupyter_execute" 34 | _SPHINX_DICT_FILE = "dummy_spelling_wordlist.txt" 35 | _SPELLING_SUFFIX = ".spelling" 36 | _MAKE_FILE = "Makefile" 37 | 38 | def __init__(self, root_dir: str, out_file: str) -> None: 39 | self._root_dir = root_dir 40 | self._output_file = out_file 41 | self._docs_dir = os.path.join(self._root_dir, SpellDictGenerator._DOCS_DIR) 42 | if not os.path.isdir(self._docs_dir): 43 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), self._docs_dir) 44 | self._build_dir = os.path.join(self._docs_dir, SpellDictGenerator._BUILD_DIR) 45 | self._stubs_dir = os.path.join(self._docs_dir, SpellDictGenerator._STUBS_DIR) 46 | self._jupyter_execute_dir = os.path.join( 47 | self._docs_dir, SpellDictGenerator._JUPYTER_EXECUTE_DIR 48 | ) 49 | self._sphinx_words: Set[str] = set() 50 | self._pylint_words: Set[str] = set() 51 | 52 | def generate_sphinx_spell_words(self) -> Set[str]: 53 | """ 54 | Generates Sphinx spelling dictionary 55 | 56 | Returns: 57 | spell words 58 | """ 59 | if os.path.isdir(self._build_dir): 60 | shutil.rmtree(self._build_dir) 61 | if os.path.isdir(self._stubs_dir): 62 | shutil.rmtree(self._stubs_dir) 63 | if os.path.isdir(self._jupyter_execute_dir): 64 | shutil.rmtree(self._jupyter_execute_dir) 65 | try: 66 | os.mkdir(self._build_dir) 67 | sphinx_dict_file = os.path.join(self._build_dir, SpellDictGenerator._SPHINX_DICT_FILE) 68 | # create empty dictionary file 69 | with open(sphinx_dict_file, "w", encoding="utf8"): 70 | pass 71 | sphinx_build( 72 | [ 73 | "-b", 74 | "spelling", 75 | "-D", 76 | f"spelling_word_list_filename={sphinx_dict_file}", 77 | self._docs_dir, 78 | self._build_dir, 79 | ] 80 | ) 81 | self._sphinx_words = SpellDictGenerator._get_sphinx_spell_words(self._build_dir) 82 | return self._sphinx_words 83 | finally: 84 | if os.path.isdir(self._build_dir): 85 | shutil.rmtree(self._build_dir) 86 | if os.path.isdir(self._stubs_dir): 87 | shutil.rmtree(self._stubs_dir) 88 | if os.path.isdir(self._jupyter_execute_dir): 89 | shutil.rmtree(self._jupyter_execute_dir) 90 | 91 | @staticmethod 92 | def _get_sphinx_spell_words(path: str) -> Set[str]: 93 | words = set() 94 | for item in os.listdir(path): 95 | fullpath = os.path.join(path, item) 96 | file_path = Path(fullpath) 97 | if file_path.is_dir() and not item.startswith("."): 98 | word_list = SpellDictGenerator._get_sphinx_spell_words(fullpath) 99 | words.update(word_list) 100 | elif file_path.is_file() and file_path.suffix == SpellDictGenerator._SPELLING_SUFFIX: 101 | word_list = SpellDictGenerator._extract_sphinx_spell_words(fullpath) 102 | words.update(word_list) 103 | 104 | return words 105 | 106 | @staticmethod 107 | def _extract_sphinx_spell_words(file_path: str) -> Set[str]: 108 | words = set() 109 | with open(file_path, "rt", encoding="utf8") as file: 110 | for line in file: 111 | start_idx = line.find("(") 112 | end_idx = -1 113 | if start_idx > 0: 114 | end_idx = line.find(")", start_idx + 1) 115 | if start_idx > 0 and end_idx > 0: 116 | word = line[start_idx + 1 : end_idx] 117 | words.add(word) 118 | return words 119 | 120 | def generate_pylint_spell_words(self) -> Set[str]: 121 | """ 122 | Generates Pylint spelling dictionary 123 | 124 | Returns: 125 | spell words 126 | Raises: 127 | FileNotFoundError: makefile not found 128 | ValueError: Pylint spell not found 129 | """ 130 | # First read make file to extract pylint options 131 | make_file = os.path.join(self._root_dir, SpellDictGenerator._MAKE_FILE) 132 | if not os.path.isfile(make_file): 133 | raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), make_file) 134 | options = None 135 | with open(make_file, "rt", encoding="utf8") as file: 136 | pylint_spell = False 137 | for line in file: 138 | if line.startswith("spell:"): 139 | pylint_spell = True 140 | elif pylint_spell and line.find("pylint ") > 0: 141 | options = line.split() 142 | options = options[1:] 143 | break 144 | 145 | if options is None: 146 | raise ValueError(f"Pylint spell command not found in makefile {make_file}") 147 | idx = options.index("--spelling-private-dict-file=.pylintdict") 148 | if idx < 0: 149 | raise ValueError(f"Pylint spell dict option not found in makefile {make_file}") 150 | 151 | with tempfile.TemporaryDirectory() as temp_dir: 152 | temp_dict_path = os.path.join(temp_dir, ".pylintdict") 153 | options[idx] = f"--spelling-private-dict-file={temp_dict_path}" 154 | options.insert(idx, "--spelling-store-unknown-words=y") 155 | lint.Run(options, exit=False) 156 | with open(temp_dict_path, "rt", encoding="utf8") as temp_dict_file: 157 | words = temp_dict_file.read().splitlines() 158 | self._pylint_words.update(words) 159 | return self._pylint_words 160 | 161 | def merge_sort_dict_to_output(self) -> List[str]: 162 | """Merge and sort Sphinx and Pylint dicts""" 163 | word_set = set(w.lower() for w in self._sphinx_words) 164 | word_set.update(w.lower() for w in self._pylint_words) 165 | words = sorted(word_set) 166 | with open(self._output_file, "w", encoding="utf8") as out_file: 167 | out_file.writelines([f"{word}\n" for word in words]) 168 | return words 169 | 170 | 171 | def check_path(path): 172 | """valid path argument""" 173 | if not path or os.path.isdir(path): 174 | return path 175 | 176 | raise argparse.ArgumentTypeError(f"readable_dir:{path} is not a valid path") 177 | 178 | 179 | if __name__ == "__main__": 180 | PARSER = argparse.ArgumentParser(description="Qiskit Spelling Dictionary Generation Tool") 181 | PARSER.add_argument( 182 | "-path", type=check_path, metavar="path", required=False, help="Root path of project." 183 | ) 184 | PARSER.add_argument( 185 | "-output", metavar="output", required=False, default=".pylintdict", help="Output file." 186 | ) 187 | 188 | ARGS = PARSER.parse_args() 189 | if not ARGS.path: 190 | ARGS.path = os.getcwd() 191 | 192 | ARGS.path = os.path.abspath(os.path.realpath(os.path.expanduser(ARGS.path))) 193 | ARGS.output = os.path.join(ARGS.path, ARGS.output) 194 | OBJ = SpellDictGenerator(ARGS.path, ARGS.output) 195 | OBJ.generate_sphinx_spell_words() 196 | OBJ.generate_pylint_spell_words() 197 | OBJ.merge_sort_dict_to_output() 198 | 199 | sys.exit(0) 200 | -------------------------------------------------------------------------------- /tools/ignore_untagged_notes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This code is part of a Qiskit project. 4 | # 5 | # (C) Copyright IBM 2021, 2023. 6 | # 7 | # This code is licensed under the Apache License, Version 2.0. You may 8 | # obtain a copy of this license in the LICENSE.txt file in the root directory 9 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 10 | # 11 | # Any modifications or derivative works of this code must retain this 12 | # copyright notice, and modified files need to carry a notice indicating 13 | # that they have been altered from the originals. 14 | 15 | # Script to ignore untagged release notes prior to deploying docs to site. 16 | 17 | LATEST_TAG=$(git describe --tags --abbrev=0) 18 | 19 | # select only release notes added after the tag was created 20 | for file_changed in `git diff --name-only HEAD $LATEST_TAG releasenotes/notes` 21 | do 22 | if [[ $file_changed == releasenotes/notes/* ]]; then 23 | isInFile=$(grep -Exq "\s*$file_changed," docs/release_notes.rst >/dev/null; echo $?) 24 | if [ $isInFile -ne 0 ]; then 25 | isInFile=$(grep -Exq "\s*:ignore-notes:\s*" docs/release_notes.rst >/dev/null; echo $?) 26 | if [ $isInFile -ne 0 ]; then 27 | echo " :ignore-notes:" >> docs/release_notes.rst 28 | fi 29 | echo "Release note changed since $LATEST_TAG: $file_changed. Ignore in docs/release_notes.rst" 30 | echo " $file_changed," >> docs/release_notes.rst 31 | fi 32 | fi 33 | done 34 | 35 | echo "Contents of docs/release_notes.rst:" 36 | echo "$(cat docs/release_notes.rst)" 37 | 38 | exit 0 39 | -------------------------------------------------------------------------------- /tools/verify_headers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # This code is part of a Qiskit project. 3 | # 4 | # (C) Copyright IBM 2022, 2023. 5 | # 6 | # This code is licensed under the Apache License, Version 2.0. You may 7 | # obtain a copy of this license in the LICENSE.txt file in the root directory 8 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 9 | # 10 | # Any modifications or derivative works of this code must retain this 11 | # copyright notice, and modified files need to carry a notice indicating 12 | # that they have been altered from the originals. 13 | 14 | """Utility script to verify qiskit copyright file headers""" 15 | 16 | import argparse 17 | import multiprocessing 18 | import os 19 | import sys 20 | import re 21 | 22 | # regex for character encoding from PEP 263 23 | pep263 = re.compile(r"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)") 24 | 25 | 26 | def discover_files(code_paths, exclude_folders): 27 | """Find all .py, .pyx, .pxd files in a list of trees""" 28 | out_paths = [] 29 | for path in code_paths: 30 | if os.path.isfile(path): 31 | out_paths.append(path) 32 | else: 33 | for directory in os.walk(path): 34 | dir_path = directory[0] 35 | for folder in exclude_folders: 36 | if folder in directory[1]: 37 | directory[1].remove(folder) 38 | for subfile in directory[2]: 39 | if ( 40 | subfile.endswith(".py") 41 | or subfile.endswith(".pyx") 42 | or subfile.endswith(".pxd") 43 | ): 44 | out_paths.append(os.path.join(dir_path, subfile)) 45 | return out_paths 46 | 47 | 48 | def validate_header(file_path): 49 | """Validate the header for a single file""" 50 | header = """# This code is part of a Qiskit project. 51 | # 52 | """ 53 | apache_text = """# 54 | # This code is licensed under the Apache License, Version 2.0. You may 55 | # obtain a copy of this license in the LICENSE.txt file in the root directory 56 | # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. 57 | # 58 | # Any modifications or derivative works of this code must retain this 59 | # copyright notice, and modified files need to carry a notice indicating 60 | # that they have been altered from the originals. 61 | """ 62 | count = 0 63 | with open(file_path, encoding="utf8") as code_file: 64 | lines = code_file.readlines() 65 | start = 0 66 | for index, line in enumerate(lines): 67 | count += 1 68 | if count > 5: 69 | return file_path, False, "Header not found in first 5 lines" 70 | if count <= 2 and pep263.match(line): 71 | return file_path, False, "Unnecessary encoding specification (PEP 263, 3120)" 72 | if line == "# This code is part of a Qiskit project.\n": 73 | start = index 74 | break 75 | if "".join(lines[start : start + 2]) != header: 76 | return file_path, False, f"Header up to copyright line does not match: {header}" 77 | if not lines[start + 2].startswith("# (C) Copyright IBM 20"): 78 | return file_path, False, "Header copyright line not found" 79 | if "".join(lines[start + 3 : start + 11]) != apache_text: 80 | return file_path, False, f"Header apache text string doesn't match:\n {apache_text}" 81 | return file_path, True, None 82 | 83 | 84 | def _main(): 85 | default_path = os.path.join( 86 | os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "qiskit_nature_pyscf" 87 | ) 88 | parser = argparse.ArgumentParser(description="Check file headers.") 89 | parser.add_argument( 90 | "paths", 91 | type=str, 92 | nargs="*", 93 | default=[default_path], 94 | help="Paths to scan by default uses ../qiskit_nature_pyscf from the script", 95 | ) 96 | args = parser.parse_args() 97 | files = discover_files(args.paths, exclude_folders=[]) 98 | with multiprocessing.Pool() as pool: 99 | res = pool.map(validate_header, files) 100 | failed_files = [x for x in res if x[1] is False] 101 | if len(failed_files) > 0: 102 | for failed_file in failed_files: 103 | sys.stderr.write(f"{failed_file[0]} failed header check because:\n") 104 | sys.stderr.write(f"{failed_file[2]}\n\n") 105 | sys.exit(1) 106 | sys.exit(0) 107 | 108 | 109 | if __name__ == "__main__": 110 | _main() 111 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 3.3.0 3 | envlist = py38, py39, py310, py311, py312, lint 4 | skipsdist = True 5 | 6 | [testenv] 7 | usedevelop = True 8 | install_command = pip install -c constraints.txt -U {opts} {packages} 9 | platform = 10 | linux: linux 11 | mac: darwin 12 | setenv = 13 | VIRTUAL_ENV={envdir} 14 | LANGUAGE=en_US 15 | LC_ALL=en_US.utf-8 16 | ARGS="-V" 17 | deps = git+https://github.com/Qiskit/qiskit-terra.git 18 | git+https://github.com/Qiskit/qiskit-nature.git 19 | -r{toxinidir}/requirements.txt 20 | -r{toxinidir}/requirements-dev.txt 21 | commands = 22 | stestr run {posargs} 23 | 24 | [testenv:lint] 25 | envdir = .tox/lint 26 | basepython = python3 27 | commands = 28 | black --check {posargs} qiskit_nature_pyscf test tools docs 29 | pylint -rn qiskit_nature_pyscf test tools 30 | mypy qiskit_nature_pyscf test tools 31 | python3 {toxinidir}/tools/check_copyright.py -path {toxinidir} 32 | python3 {toxinidir}/tools/verify_headers.py qiskit_nature_pyscf test tools 33 | python3 {toxinidir}/tools/find_stray_release_notes.py 34 | 35 | [testenv:black] 36 | envdir = .tox/lint 37 | commands = black {posargs} qiskit_nature_pyscf test tools docs 38 | 39 | [testenv:coverage] 40 | basepython = python3 41 | setenv = 42 | {[testenv]setenv} 43 | PYTHON=coverage3 run --source qiskit_nature_pyscf --parallel-mode 44 | commands = 45 | stestr run {posargs} 46 | coverage3 combine 47 | coverage3 report 48 | 49 | [testenv:docs] 50 | envdir = .tox/docs 51 | basepython = python3 52 | commands = 53 | sphinx-build -j auto -W -T --keep-going -b html {posargs} docs/ docs/_build/html 54 | 55 | [testenv:docs-clean] 56 | skip_install = true 57 | deps = 58 | allowlist_externals = rm 59 | commands = rm -rf {toxinidir}/docs/stubs/ {toxinidir}/docs/_build 60 | --------------------------------------------------------------------------------