add dependency #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test Solution Notebooks | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - "pull-request/[0-9]+" | |
| - "**" | |
| # paths: | |
| # - '**/solutions/**/*.ipynb' | |
| # - '**/Solutions/**/*.ipynb' | |
| # - '**/*Solution*.ipynb' | |
| # - '.github/workflows/notebook-tests.yml' | |
| workflow_dispatch: | |
| inputs: | |
| run_all: | |
| description: "Run all modules (ignore change detection)" | |
| type: boolean | |
| default: true | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| detect-changes: | |
| name: Detect changed modules | |
| runs-on: ubuntu-latest | |
| outputs: | |
| modules: ${{ steps.filter.outputs.modules }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Detect changed modules | |
| id: filter | |
| shell: bash | |
| run: | | |
| ALL_MODULES='["quick-start-to-quantum","chemistry-simulations","qis-examples","hybrid-workflows","qaoa-for-max-cut","qec101","dynamics101","quantum-applications-to-finance","ai-for-quantum"]' | |
| # Run all modules on workflow_dispatch or push to main | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ] || [ "${{ github.ref }}" = "refs/heads/main" ]; then | |
| echo "modules=$ALL_MODULES" >> "$GITHUB_OUTPUT" | |
| echo "Running all modules (trigger: ${{ github.event_name }}, ref: ${{ github.ref }})" | |
| exit 0 | |
| fi | |
| # For branch pushes, detect which modules changed | |
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD) | |
| echo "Changed files:" | |
| echo "$CHANGED_FILES" | |
| echo "" | |
| # If the workflow file itself changed, run everything | |
| if echo "$CHANGED_FILES" | grep -q '.github/workflows/test-solution-notebooks.yml'; then | |
| echo "Workflow file changed — running all modules" | |
| echo "modules=$ALL_MODULES" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| CHANGED_MODULES="[]" | |
| for mod in quick-start-to-quantum chemistry-simulations qis-examples hybrid-workflows qaoa-for-max-cut qec101 dynamics101 quantum-applications-to-finance ai-for-quantum; do | |
| if echo "$CHANGED_FILES" | grep -q "^$mod/"; then | |
| CHANGED_MODULES=$(echo "$CHANGED_MODULES" | python3 -c "import sys,json; m=json.load(sys.stdin); m.append('$mod'); print(json.dumps(m))") | |
| fi | |
| done | |
| if [ "$CHANGED_MODULES" = "[]" ]; then | |
| echo "No module changes detected — running all modules as fallback" | |
| CHANGED_MODULES="$ALL_MODULES" | |
| fi | |
| echo "modules=$CHANGED_MODULES" >> "$GITHUB_OUTPUT" | |
| echo "Modules to test: $CHANGED_MODULES" | |
| test-notebooks: | |
| name: "${{ matrix.module }} (${{ matrix.image }})" | |
| needs: detect-changes | |
| runs-on: ubuntu-latest | |
| container: | |
| image: ${{ matrix.image }} | |
| options: --user root | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| image: | |
| - ghcr.io/nvidia/cudaqx | |
| module: ${{ fromJson(needs.detect-changes.outputs.modules) }} | |
| include: | |
| # Per-module solutions path and notebook pattern. | |
| # These properties are merged into every image × module combination. | |
| - module: quick-start-to-quantum | |
| solutions_path: quick-start-to-quantum/solutions | |
| pattern: "*.ipynb" | |
| - module: chemistry-simulations | |
| solutions_path: chemistry-simulations/solutions | |
| pattern: "*.ipynb" | |
| - module: qis-examples | |
| solutions_path: qis-examples/solutions | |
| pattern: "*.ipynb" | |
| - module: hybrid-workflows | |
| solutions_path: hybrid-workflows/solutions | |
| pattern: "*.ipynb" | |
| - module: qaoa-for-max-cut | |
| solutions_path: qaoa-for-max-cut | |
| pattern: "*Solution*.ipynb" | |
| - module: qec101 | |
| solutions_path: qec101/Solutions | |
| pattern: "*.ipynb" | |
| - module: dynamics101 | |
| solutions_path: dynamics101/Solutions | |
| pattern: "*.ipynb" | |
| - module: quantum-applications-to-finance | |
| solutions_path: quantum-applications-to-finance/solutions | |
| pattern: "*.ipynb" | |
| - module: ai-for-quantum | |
| solutions_path: ai-for-quantum/solutions | |
| pattern: "*.ipynb" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Install Jupyter and dependencies | |
| run: | | |
| pip install --quiet --upgrade \ | |
| jupyter \ | |
| nbconvert \ | |
| qutip \ | |
| pyscf \ | |
| genQC \ | |
| galois \ | |
| torch \ | |
| scikit-learn \ | |
| libgfortran5 \ | |
| matplotlib-venn | |
| - name: Run solution notebooks — ${{ matrix.module }} | |
| shell: bash | |
| run: | | |
| SOLUTIONS_PATH="${{ matrix.solutions_path }}" | |
| PATTERN="${{ matrix.pattern }}" | |
| MODULE="${{ matrix.module }}" | |
| IMAGE="${{ matrix.image }}" | |
| # Verify the directory exists | |
| if [ ! -d "$SOLUTIONS_PATH" ]; then | |
| echo "Directory '$SOLUTIONS_PATH' not found — skipping." | |
| { | |
| echo "## 📓 $MODULE ($IMAGE)" | |
| echo "> ⚠️ Solutions directory not found — skipped." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| # Collect matching notebooks (maxdepth 1 to avoid .ipynb_checkpoints) | |
| # Use NUL-delimited output to safely handle filenames with spaces | |
| NOTEBOOKS=$(find "$SOLUTIONS_PATH" -maxdepth 1 -name "$PATTERN" -print0 | sort -z | xargs -0 printf '%s\n') | |
| if [ -z "$NOTEBOOKS" ]; then | |
| echo "No notebooks matching '$PATTERN' found in '$SOLUTIONS_PATH' — skipping." | |
| { | |
| echo "## 📓 $MODULE ($IMAGE)" | |
| echo "> ⚠️ No solution notebooks found — skipped." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| echo "Notebooks to execute:" | |
| echo "$NOTEBOOKS" | |
| echo "================================" | |
| # Start summary table | |
| { | |
| echo "## 📓 $MODULE ($IMAGE)" | |
| echo "| Notebook | Status |" | |
| echo "|---|---|" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| PASSED=0 | |
| FAILED=0 | |
| FAILED_LIST="" | |
| while IFS= read -r nb; do | |
| BASENAME=$(basename "$nb") | |
| # Get path relative to the module root | |
| NB_REL="${nb#$MODULE/}" | |
| echo "::group::▶ $nb" | |
| if PYTHONPATH="$PWD/$MODULE:${PYTHONPATH:-}" jupyter nbconvert \ | |
| --to notebook \ | |
| --execute \ | |
| --inplace \ | |
| --ExecutePreprocessor.timeout=600 \ | |
| --ExecutePreprocessor.kernel_name=python3 \ | |
| "$nb"; then | |
| echo "✅ Passed: $nb" | |
| PASSED=$((PASSED + 1)) | |
| echo "| \`$BASENAME\` | ✅ Passed |" >> "$GITHUB_STEP_SUMMARY" | |
| else | |
| echo "❌ Failed: $nb" | |
| FAILED=$((FAILED + 1)) | |
| FAILED_LIST="$FAILED_LIST\n - $nb" | |
| echo "| \`$BASENAME\` | ❌ Failed |" >> "$GITHUB_STEP_SUMMARY" | |
| fi | |
| echo "::endgroup::" | |
| done <<< "$NOTEBOOKS" | |
| # Write summary footer | |
| { | |
| echo "" | |
| echo "**$PASSED passed, $FAILED failed**" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| echo "================================" | |
| echo "Results for $MODULE:" | |
| echo " Passed : $PASSED" | |
| echo " Failed : $FAILED" | |
| if [ $FAILED -gt 0 ]; then | |
| echo "::error::$FAILED notebook(s) failed in $MODULE:$FAILED_LIST" | |
| exit 1 | |
| fi |