├── .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 | [](https://opensource.org/licenses/Apache-2.0)[](https://github.com/qiskit-community/qiskit-nature-pyscf/actions?query=workflow%3A"Nature%20PySCF%20Unit%20Tests"+branch%3Amain+event%3Apush)[](https://github.com/qiskit-community/qiskit-nature-pyscf/releases)[](https://pypi.org/project/qiskit-nature-pyscf/)[](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 |
--------------------------------------------------------------------------------