Skip to content

Adding time-dependent Hamiltonian evolution #2094

Adding time-dependent Hamiltonian evolution

Adding time-dependent Hamiltonian evolution #2094

name: Build and Test
on:
push:
branches:
- main
pull_request:
branches:
- '**'
merge_group:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
checks: write
statuses: write
jobs:
check-up-to-date:
name: Check if branch is up to date with main
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: recursive
- name: Check if branch is up to date with main (Pull Request)
run: |
# Ensure origin/main is present
git fetch --no-tags --prune origin +refs/heads/main:refs/remotes/origin/main
# Check if the current commit is ahead of main
# Kills pipeline if branch is not up to date with main
# For pull requests, we use github.event.pull_request.sha to get the commit SHA,
# as GitHub treats the current commit as one that would be produced by merging into main
if ! git merge-base --is-ancestor origin/main "${{ github.event.pull_request.head.sha }}"; then
echo "This branch is not up to date with main. Please merge the latest changes from main into your branch to continue."
exit 1
fi
echo "Branch is up to date, starting Build and Test workflow."
coverage:
name: ${{ matrix.os-name }} Build, Test, and Coverage Analysis
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- os-name: Linux
runner: [self-hosted, 1ES.Pool=1es-gh-hb120-pool]
build-parallel: 120
uarch: x86-64-v3
python-version: '3.10'
cpp-test-threads: 2
python-test-threads: 2
- os-name: Linux
runner: [self-hosted, 1ES.Pool=1es-gh-hb120-pool]
build-parallel: 120
uarch: x86-64-v3
python-version: '3.13'
cpp-test-threads: 2
python-test-threads: 2
- os-name: Linux
runner: [self-hosted, 1ES.Pool=1es-gh-hb120-pool]
build-parallel: 120
uarch: x86-64-v3
python-version: '3.14'
# qiskit-extras is not supported on Python 3.14
skip-qiskit-extras: true
cpp-test-threads: 2
python-test-threads: 2
- os-name: macOS
runner: macos-26-xlarge
build-parallel: 3
uarch: native
python-version: '3.13'
cpp-test-threads: 16
python-test-threads: 16
env:
BUILD_DIR: cpp/build
QSHARP_PYTHON_TELEMETRY: 'false'
CMAKE_INSTALL_PREFIX: ${{ github.workspace }}/install
CPP_DEPS_PREFIX: ${{ github.workspace }}/cpp_deps_install
CTEST_OUTPUT_ON_FAILURE: 1
CMAKE_BUILD_PARALLEL_LEVEL: ${{ matrix.build-parallel }}
QDK_UARCH: ${{ matrix.uarch }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up environment paths
run: |
echo "PATH=${{ github.workspace }}/install/bin:${{ github.workspace }}/install/lib:$PATH" >> $GITHUB_ENV
echo "PYTHONPATH=${{ github.workspace }}/install/lib/python${{ matrix.python-version }}/site-packages:${{ github.workspace }}/install/lib:$PYTHONPATH" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=${{ github.workspace }}/install/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- name: Install Linux system packages
if: matrix.os-name == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
ninja-build \
cmake \
libeigen3-dev \
libboost-all-dev \
libhdf5-dev \
libopenblas-dev \
libgfortran5 \
pkg-config \
doxygen \
graphviz \
gcovr
- name: Install macOS system packages
if: matrix.os-name == 'macOS'
run: |
brew update
brew upgrade
arch -arm64 brew install \
cmake \
ninja \
eigen \
boost \
hdf5 \
graphviz \
gcovr \
nlohmann-json \
bison \
flex \
googletest
- name: Cache Doxygen (macOS)
if: matrix.os-name == 'macOS'
id: cache-doxygen
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/doxygen_install
key: doxygen-${{ runner.os }}-Release_1_9_8
- name: Build doxygen from source on macOS
if: matrix.os-name == 'macOS' && steps.cache-doxygen.outputs.cache-hit != 'true'
run: |
# Clone repo for working Doxygen version
git clone https://github.com/doxygen/doxygen.git
cd doxygen
# Checkout working release
git checkout Release_1_9_8
mkdir build && cd build
cmake -DBISON_EXECUTABLE=/opt/homebrew/Cellar/bison/3.8.2/bin/bison \
-DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/doxygen_install" \
-DCMAKE_CXX_FLAGS="-Wno-missing-template-arg-list-after-template-kw $CMAKE_CXX_FLAGS" \
-G "Unix Makefiles" ..
make -j$(sysctl -n hw.ncpu)
make install
- name: Add Doxygen to PATH (macOS)
if: matrix.os-name == 'macOS'
run: |
echo "PATH=${{ github.workspace }}/doxygen_install/bin:$PATH" >> $GITHUB_ENV
- name: Cache C++ dependency install # Checks if manifest files or build script have changed to rebuild dependencies
id: cache-cpp-deps
uses: actions/cache@v4
with:
path: |
${{ env.CPP_DEPS_PREFIX }}
key: >-
cpp-deps-${{ runner.os }}-${{ matrix.uarch }}-
${{ hashFiles('cpp/manifest/qdk-chemistry/cgmanifest.json', 'external/macis/manifest/cgmanifest.json', '.devcontainer/scripts/install_cpp_dependencies.sh') }}
- name: Add C++ deps prefix to CMake search path
run: |
echo "CMAKE_PREFIX_PATH=${{ env.CPP_DEPS_PREFIX }}:${CMAKE_PREFIX_PATH}" >> $GITHUB_ENV
echo "LD_LIBRARY_PATH=${{ env.CPP_DEPS_PREFIX }}/lib:${LD_LIBRARY_PATH}" >> $GITHUB_ENV
echo "PATH=${{ env.CPP_DEPS_PREFIX }}/bin:${PATH}" >> $GITHUB_ENV
- name: Install C++ dependencies
if: steps.cache-cpp-deps.outputs.cache-hit != 'true' # Run the dependency build if we don't find the right hash in the cache
env:
INSTALL_PREFIX: ${{ env.CPP_DEPS_PREFIX }}
BUILD_DIR: ${{ github.workspace }}/cpp_deps_build
KEEP_BUILD_DIR: 1
run: |
bash ${{ github.workspace }}/.devcontainer/scripts/install_cpp_dependencies.sh \
${{ github.workspace }}/cpp/manifest/qdk-chemistry/cgmanifest.json \
${{ github.workspace }}/external/macis/manifest/cgmanifest.json
- name: Configure C++ RelWithDebInfo/Coverage build
run: |
cmake -S cpp -B "$BUILD_DIR" \
-GNinja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DQDK_UARCH="$QDK_UARCH" \
-DQDK_CHEMISTRY_ENABLE_COVERAGE=ON \
-DBUILD_TESTING=ON
- name: Build C++ RelWithDebInfo/Coverage library
run: cmake --build "$BUILD_DIR"
- name: Run C++ tests in RelWithDebInfo/Coverage mode with coverage
env:
OMP_NUM_THREADS: ${{ matrix.cpp-test-threads }}
run: |
mkdir -p test-results ${{ github.workspace }}/coverage-reports
cd "$BUILD_DIR"
echo "=== Running C++ Tests in Debug Mode with Coverage ==="
set +e
if [ "$QDK_UARCH" == "native" ]; then # Exclude particular test on macOS builds
ctest --output-on-failure --verbose --timeout 300 --output-junit ${{ github.workspace }}/test-results/ctest_results.xml -E "MACIS_SERIAL_TEST|AtomInitGuessEnergyConvergence"
else
ctest --output-on-failure --verbose --timeout 300 --output-junit ${{ github.workspace }}/test-results/ctest_results.xml -E "MACIS_SERIAL_TEST"
fi
CTEST_EXIT_CODE=$?
set -e
if [ $CTEST_EXIT_CODE -ne 0 ]; then
echo "=== Some tests failed ==="
echo "Exit code: $CTEST_EXIT_CODE"
fi
cd ${{ github.workspace }}
gcovr --root ${{ github.workspace }}/cpp \
--object-directory "${{ github.workspace }}/${BUILD_DIR}" \
--filter '${{ github.workspace }}/cpp/src/.*' \
--filter '${{ github.workspace }}/cpp/include/.*' \
--exclude '.*/tests/.*' \
--exclude '.*test.*\.cpp' \
--exclude '.*gtest.*' \
--exclude '.*gmock.*' \
--exclude '.*/external/.*' \
--html-details ${{ github.workspace }}/coverage-reports/cpp_coverage.html \
--xml ${{ github.workspace }}/coverage-reports/cpp_coverage.xml \
--txt ${{ github.workspace }}/coverage-reports/cpp_coverage.txt \
--merge-mode-functions=separate \
--gcov-ignore-parse-errors
echo "C++ Coverage Summary:"
cat ${{ github.workspace }}/coverage-reports/cpp_coverage.txt
if [ $CTEST_EXIT_CODE -ne 0 ]; then
exit $CTEST_EXIT_CODE
fi
- name: Install C++ library
run: |
cmake --install "$BUILD_DIR"
- name: Test C++ installation
run: |
cmake -S examples/language/cpp -B build_example -GNinja -DCMAKE_PREFIX_PATH=${{ github.workspace }}/install
cmake --build build_example --target all
echo "C++ Example Compiled with Pre-Installed QDK/Chemistry-C++ Libraries!"
- name: Install Python package
working-directory: python
env:
QDK_CHEMISTRY_ENABLE_COVERAGE: 1
CMAKE_BUILD_TYPE: RelWithDebInfo
run: |
# Remove CMake files to build Python package without picking the installed library
rm -r ${{ github.workspace }}/install/lib/cmake/qdk
# Use persistent build directory for coverage analysis
# Note: qiskit-extras is not supported on Python 3.14
if [ "${{ matrix.skip-qiskit-extras }}" == "true" ]; then
echo "Skipping qiskit-extras installation (not supported on Python ${{ matrix.python-version }})"
python -m pip install --prefix=${{ github.workspace }}/install -C build-dir="build/{wheel_tag}" .[plugins,dev,docs,coverage]
else
python -m pip install --prefix=${{ github.workspace }}/install -C build-dir="build/{wheel_tag}" .[all]
fi
python3 -c "import qdk_chemistry; print('qdk_chemistry version:', qdk_chemistry.__version__)"
# Install optional dependencies for tests
python -m pip install --prefix=${{ github.workspace }}/install pennylane gast ipykernel
# Register the Jupyter kernel for notebook tests
python -m ipykernel install --user --name python3 --display-name "Python 3"
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --prefix=${{ github.workspace }}/install coverage pytest pytest-cov
- name: Run Python tests
env:
OMP_NUM_THREADS: ${{ matrix.python-test-threads }}
QDK_CHEMISTRY_RUN_SLOW_TESTS: 0
working-directory: python
run: |
set -e
mkdir -p ../test-results ../coverage-reports
BUILD_TEMP=$(find . -type d -path './build/cp*' -print -quit)
echo "Found build directory: $BUILD_TEMP"
set +e
set -o pipefail
pytest \
--cov=qdk_chemistry \
--cov-report=xml:../coverage-reports/python_coverage.xml \
--cov-report=html:../coverage-reports/python_coverage_html \
--cov-report=term \
--junitxml=../test-results/pytest_report.xml \
-v \
--tb=short \
2>&1 | tee ../coverage-reports/python_coverage.txt
PYTEST_EXIT_CODE=${PIPESTATUS[0]}
set -e
echo "=== Python Test Output ==="
cat ../coverage-reports/python_coverage.txt
if [ $PYTEST_EXIT_CODE -ne 0 ]; then
echo "ERROR: Python tests failed with exit code $PYTEST_EXIT_CODE"
fi
if [ -n "$BUILD_TEMP" ] && [ -d "$BUILD_TEMP" ]; then
find "$BUILD_TEMP" -name "*.gcda" -exec cp {} coverage_build/ \; 2>/dev/null || true
fi
if [ $PYTEST_EXIT_CODE -ne 0 ]; then
exit 1
fi
- name: Generate Pybind11 coverage report
working-directory: python
run: |
PYBIND_BUILD_DIR=$(find . -wholename "./build/cp*/CMakeFiles/_core.dir" | head -1)
CLEAN_BUILD_DIR="${PYBIND_BUILD_DIR#./}"
gcovr \
--root ${{ github.workspace }}/python \
--object-directory "${{ github.workspace }}/python/$CLEAN_BUILD_DIR" \
--filter '.*src/pybind11.*' \
--filter '.*src/cpp.*' \
--exclude '.*test.*' \
--exclude '.*external.*' \
--gcov-ignore-errors=no_working_dir_found \
--gcov-ignore-errors=source_not_found \
--xml "../coverage-reports/pybind11_coverage.xml" \
--txt "../coverage-reports/pybind11_coverage.txt" \
--html-details "../coverage-reports/pybind11_coverage.html"
echo "Pybind11 Coverage Summary:"
cat ../coverage-reports/pybind11_coverage.txt
- name: Generate coverage statistics
run: |
# Extract coverage percentages
CPP_COVERAGE=$(grep -oP 'TOTAL.*?\K[0-9.]+(?=%)' coverage-reports/cpp_coverage.txt | tail -1 || echo "0")
PYTHON_COVERAGE=$(grep -oP 'TOTAL.*?\K[0-9]+(?=%)' coverage-reports/python_coverage.txt | tail -1 || echo "0")
PYBIND11_COVERAGE=$(grep -oP 'TOTAL.*?\K[0-9.]+(?=%)' coverage-reports/pybind11_coverage.txt | tail -1 || echo "0")
echo "CPP_COVERAGE=$CPP_COVERAGE" >> $GITHUB_ENV
echo "PYTHON_COVERAGE=$PYTHON_COVERAGE" >> $GITHUB_ENV
echo "PYBIND11_COVERAGE=$PYBIND11_COVERAGE" >> $GITHUB_ENV
# Create simple PR comment with just statistics
echo "## 📊 Coverage Summary" > pr-comment.md
echo "" >> pr-comment.md
echo "| Component | Coverage |" >> pr-comment.md
echo "|-----------|----------|" >> pr-comment.md
echo "| C++ Library | ${CPP_COVERAGE}% |" >> pr-comment.md
echo "| Python Package | ${PYTHON_COVERAGE}% |" >> pr-comment.md
if [ "$PYBIND11_COVERAGE" != "0" ]; then
echo "| Pybind11 Bindings | ${PYBIND11_COVERAGE}% |" >> pr-comment.md
fi
echo "" >> pr-comment.md
echo "---" >> pr-comment.md
echo "" >> pr-comment.md
echo "### Detailed Coverage Reports" >> pr-comment.md
echo "" >> pr-comment.md
echo "<details>" >> pr-comment.md
echo "<summary>C++ Coverage Details</summary>" >> pr-comment.md
echo "" >> pr-comment.md
echo '```' >> pr-comment.md
cat coverage-reports/cpp_coverage.txt >> pr-comment.md
echo '```' >> pr-comment.md
echo "</details>" >> pr-comment.md
echo "" >> pr-comment.md
if [ -f coverage-reports/python_coverage.txt ]; then
echo "<details>" >> pr-comment.md
echo "<summary>Python Coverage Details</summary>" >> pr-comment.md
echo "" >> pr-comment.md
echo '```' >> pr-comment.md
tail -50 coverage-reports/python_coverage.txt >> pr-comment.md
echo '```' >> pr-comment.md
echo "</details>" >> pr-comment.md
echo "" >> pr-comment.md
fi
if [ -f coverage-reports/pybind11_coverage.txt ]; then
echo "<details>" >> pr-comment.md
echo "<summary>Pybind11 Coverage Details</summary>" >> pr-comment.md
echo "" >> pr-comment.md
echo '```' >> pr-comment.md
cat coverage-reports/pybind11_coverage.txt >> pr-comment.md
echo '```' >> pr-comment.md
echo "</details>" >> pr-comment.md
fi
- name: Comment PR with coverage
if: github.event_name == 'pull_request'
uses: thollander/actions-comment-pull-request@v2
with:
filePath: pr-comment.md
comment_tag: coverage-report
- name: Add coverage to job summary
run: |
# Create comprehensive summary with table and detailed reports
echo "## 📊 Coverage Summary" > coverage-summary.md
echo "" >> coverage-summary.md
echo "| Component | Coverage |" >> coverage-summary.md
echo "|-----------|----------|" >> coverage-summary.md
echo "| C++ Library | ${CPP_COVERAGE}% |" >> coverage-summary.md
echo "| Python Package | ${PYTHON_COVERAGE}% |" >> coverage-summary.md
if [ "$PYBIND11_COVERAGE" != "0" ]; then
echo "| Pybind11 Bindings | ${PYBIND11_COVERAGE}% |" >> coverage-summary.md
fi
echo "" >> coverage-summary.md
echo "---" >> coverage-summary.md
echo "" >> coverage-summary.md
echo "### Detailed Coverage Reports" >> coverage-summary.md
echo "" >> coverage-summary.md
echo "<details>" >> coverage-summary.md
echo "<summary>C++ Coverage Details</summary>" >> coverage-summary.md
echo "" >> coverage-summary.md
echo '```' >> coverage-summary.md
cat coverage-reports/cpp_coverage.txt >> coverage-summary.md
echo '```' >> coverage-summary.md
echo "</details>" >> coverage-summary.md
echo "" >> coverage-summary.md
if [ -f coverage-reports/python_coverage.txt ]; then
echo "<details>" >> coverage-summary.md
echo "<summary>Python Coverage Details</summary>" >> coverage-summary.md
echo "" >> coverage-summary.md
echo '```' >> coverage-summary.md
tail -50 coverage-reports/python_coverage.txt >> coverage-summary.md
echo '```' >> coverage-summary.md
echo "</details>" >> coverage-summary.md
echo "" >> coverage-summary.md
fi
if [ -f coverage-reports/pybind11_coverage.txt ]; then
echo "<details>" >> coverage-summary.md
echo "<summary>Pybind11 Coverage Details</summary>" >> coverage-summary.md
echo "" >> coverage-summary.md
echo '```' >> coverage-summary.md
cat coverage-reports/pybind11_coverage.txt >> coverage-summary.md
echo '```' >> coverage-summary.md
echo "</details>" >> coverage-summary.md
fi
# Add to job summary
cat coverage-summary.md >> $GITHUB_STEP_SUMMARY
- name: Build documentation
if: matrix.python-version == '3.13'
working-directory: docs
run: |
set +e
make clean all
MAKE_EXIT_CODE=$?
set -e
if [ $MAKE_EXIT_CODE -ne 0 ]; then
echo "=== Documentation build failed ==="
echo "=== Contents of all .txt output files ==="
for txtfile in *-output.txt *-warnings.txt; do
if [ -f "$txtfile" ]; then
echo ""
echo "==== File: $txtfile ===="
cat "$txtfile"
fi
done
exit $MAKE_EXIT_CODE
fi