3-class strength GP + joint_hamming_matern production kernel #204
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
| # GitHub Actions workflow for executing Jupyter notebooks | |
| # Runs on pull requests and nightly to ensure notebooks work correctly | |
| name: Notebooks | |
| # Cancel obsolete runs on rapid pushes. | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| on: | |
| push: | |
| branches: [main, master] | |
| paths: ¬ebook_paths | |
| - 'notebooks/**' | |
| - 'boxcrete/**' | |
| - 'pyproject.toml' | |
| - '.github/workflows/notebooks.yml' | |
| pull_request: | |
| branches: [main, master] | |
| paths: *notebook_paths | |
| schedule: | |
| # Run nightly at 3:00 AM UTC | |
| - cron: '0 3 * * *' | |
| workflow_dispatch: | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Notebooks that use BOXCRETE_OPTIMIZATION_MODE run in both mortar and concrete modes | |
| mode-dependent-notebooks: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| optimization-mode: ['mortar', 'concrete'] | |
| include-cost: ['false', 'true'] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Cache pip dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-3.12-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-3.12- | |
| ${{ runner.os }}-pip- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[notebooks]" | |
| - name: Install Jupyter kernel | |
| run: | | |
| python -m ipykernel install --user --name python3 --display-name "Python 3" | |
| - name: Execute mode-dependent notebooks (${{ matrix.optimization-mode }}, cost=${{ matrix.include-cost }}) | |
| # nbconvert produces the rendered .ipynb artifact (cells + plots | |
| # embedded as base64) for the upload-artifact step below. The | |
| # cell-by-cell fast-fail check above runs first and catches | |
| # hangs / cell errors with clear progress markers, so we don't | |
| # need the diagnostic wrapper that previously surrounded this | |
| # step (free -h / dmesg / signal-name parsing). If a cell ever | |
| # hangs again, the cell-by-cell step will surface it before we | |
| # reach this nbconvert call. | |
| env: | |
| BOXCRETE_OPTIMIZATION_MODE: ${{ matrix.optimization-mode }} | |
| BOXCRETE_INCLUDE_COST: ${{ matrix.include-cost }} | |
| BOXCRETE_SMOKE_TEST: "1" | |
| run: | | |
| python -m jupyter nbconvert \ | |
| --to notebook \ | |
| --execute \ | |
| --inplace \ | |
| --ExecutePreprocessor.timeout=600 \ | |
| --ExecutePreprocessor.kernel_name=python3 \ | |
| notebooks/prediction_and_optimization_tutorial.ipynb | |
| - name: Upload executed notebooks as artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| # Include both matrix dimensions in the name so the four matrix | |
| # jobs upload to four distinct artifact slots. Without `include-cost` | |
| # in the name the (cost=true) job for each optimization-mode races | |
| # the (cost=false) job and the loser hits a 409 Conflict from the | |
| # upload-artifact action. | |
| name: notebooks-mode-${{ matrix.optimization-mode }}-cost-${{ matrix.include-cost }} | |
| path: notebooks/prediction_and_optimization_tutorial.ipynb | |
| retention-days: 7 | |
| # Notebooks that don't depend on optimization mode run once | |
| mode-independent-notebooks: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Set up Python 3.12 | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Cache pip dependencies | |
| uses: actions/cache@v5 | |
| with: | |
| path: ~/.cache/pip | |
| key: ${{ runner.os }}-pip-3.12-${{ hashFiles('pyproject.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pip-3.12- | |
| ${{ runner.os }}-pip- | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -e ".[notebooks]" | |
| - name: Install Jupyter kernel | |
| run: | | |
| python -m ipykernel install --user --name python3 | |
| - name: Execute mode-independent notebooks | |
| run: | | |
| python - << 'PYEOF' | |
| import subprocess, sys | |
| from pathlib import Path | |
| # Notebooks that don't use BOXCRETE_OPTIMIZATION_MODE | |
| MODE_DEPENDENT = { | |
| "prediction_and_optimization_tutorial.ipynb", | |
| } | |
| notebooks = sorted( | |
| nb for nb in Path("notebooks").glob("*.ipynb") | |
| if nb.name not in MODE_DEPENDENT | |
| ) | |
| failed = [] | |
| for nb in notebooks: | |
| print(f"{'=' * 40}") | |
| print(f"Executing: {nb}") | |
| print(f"{'=' * 40}") | |
| result = subprocess.run( | |
| [ | |
| sys.executable, "-m", "jupyter", "nbconvert", | |
| "--to", "notebook", | |
| "--execute", | |
| "--inplace", | |
| "--ExecutePreprocessor.timeout=600", | |
| "--ExecutePreprocessor.kernel_name=python3", | |
| str(nb), | |
| ], | |
| capture_output=False, | |
| ) | |
| if result.returncode != 0: | |
| failed.append(str(nb)) | |
| print(f"❌ Failed: {nb}") | |
| else: | |
| print(f"✅ Successfully executed: {nb}") | |
| print() | |
| if failed: | |
| print(f"\n{len(failed)} notebook(s) failed:") | |
| for f in failed: | |
| print(f" - {f}") | |
| sys.exit(1) | |
| else: | |
| print(f"\nAll {len(notebooks)} notebook(s) executed successfully.") | |
| PYEOF | |
| - name: Upload executed notebooks as artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: notebooks-independent | |
| path: notebooks/*.ipynb | |
| retention-days: 7 | |
| notebook-lint: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v5 | |
| - name: Set up Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.12' | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install nbformat | |
| - name: Validate notebook format | |
| run: | | |
| python - << 'EOF' | |
| import nbformat | |
| import sys | |
| from pathlib import Path | |
| notebooks = list(Path('notebooks').glob('*.ipynb')) | |
| errors = [] | |
| for nb_path in notebooks: | |
| try: | |
| with open(nb_path, 'r', encoding='utf-8') as f: | |
| nb = nbformat.read(f, as_version=4) | |
| # `read` only checks parseability; `validate` enforces | |
| # the nbformat schema. Without this, schema mismatches | |
| # (e.g. cell `id` fields requiring nbformat_minor>=5) | |
| # only surface as ERROR-level log lines during the much | |
| # slower `nbconvert --execute` step and don't fail CI. | |
| nbformat.validate(nb) | |
| print(f"✅ Valid notebook: {nb_path}") | |
| except Exception as e: | |
| errors.append(f"❌ Invalid notebook {nb_path}: {e}") | |
| print(errors[-1]) | |
| if errors: | |
| print(f"\n{len(errors)} notebook(s) failed validation.") | |
| sys.exit(1) | |
| else: | |
| print(f"\nAll {len(notebooks)} notebook(s) are valid.") | |
| EOF |