Commit ·
3f91234
1
Parent(s): d5cfbf0
ci: add package and runtime smoke checks
Browse files
.github/workflows/ci.yml
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
pull_request:
|
| 6 |
+
|
| 7 |
+
concurrency:
|
| 8 |
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
| 9 |
+
cancel-in-progress: true
|
| 10 |
+
|
| 11 |
+
jobs:
|
| 12 |
+
lint:
|
| 13 |
+
name: lint
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
steps:
|
| 16 |
+
- uses: actions/checkout@v4
|
| 17 |
+
- uses: astral-sh/setup-uv@v6
|
| 18 |
+
with:
|
| 19 |
+
version: "0.8.x"
|
| 20 |
+
enable-cache: true
|
| 21 |
+
- run: uv sync --dev
|
| 22 |
+
- run: uv run ruff check .
|
| 23 |
+
- run: uv run ruff format --check .
|
| 24 |
+
|
| 25 |
+
test:
|
| 26 |
+
name: test
|
| 27 |
+
runs-on: ubuntu-latest
|
| 28 |
+
steps:
|
| 29 |
+
- uses: actions/checkout@v4
|
| 30 |
+
- uses: astral-sh/setup-uv@v6
|
| 31 |
+
with:
|
| 32 |
+
version: "0.8.x"
|
| 33 |
+
enable-cache: true
|
| 34 |
+
- run: uv sync --dev
|
| 35 |
+
- run: uv run pytest
|
| 36 |
+
|
| 37 |
+
package:
|
| 38 |
+
name: package
|
| 39 |
+
runs-on: ubuntu-latest
|
| 40 |
+
steps:
|
| 41 |
+
- uses: actions/checkout@v4
|
| 42 |
+
- uses: astral-sh/setup-uv@v6
|
| 43 |
+
with:
|
| 44 |
+
version: "0.8.x"
|
| 45 |
+
enable-cache: true
|
| 46 |
+
- run: uv sync --dev
|
| 47 |
+
- run: uv build
|
| 48 |
+
- run: uv run python -m twine check dist/*
|
| 49 |
+
- name: Wheel install smoke test
|
| 50 |
+
shell: bash
|
| 51 |
+
run: |
|
| 52 |
+
set -euo pipefail
|
| 53 |
+
smoke_dir="$(mktemp -d)"
|
| 54 |
+
trap 'rm -rf "$smoke_dir"' EXIT
|
| 55 |
+
uv venv "$smoke_dir/venv"
|
| 56 |
+
uv pip install --python "$smoke_dir/venv/bin/python" dist/*.whl
|
| 57 |
+
(
|
| 58 |
+
cd "$smoke_dir"
|
| 59 |
+
"$smoke_dir/venv/bin/python" -c 'import gnn4colliders; print(gnn4colliders.__version__)'
|
| 60 |
+
"$smoke_dir/venv/bin/gnn4colliders" --help
|
| 61 |
+
"$smoke_dir/venv/bin/gnn4colliders" train --help
|
| 62 |
+
"$smoke_dir/venv/bin/gnn4colliders" export --help
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
onnx:
|
| 66 |
+
name: onnx
|
| 67 |
+
runs-on: ubuntu-latest
|
| 68 |
+
steps:
|
| 69 |
+
- uses: actions/checkout@v4
|
| 70 |
+
- uses: astral-sh/setup-uv@v6
|
| 71 |
+
with:
|
| 72 |
+
version: "0.8.x"
|
| 73 |
+
enable-cache: true
|
| 74 |
+
- run: uv sync --dev --extra root-gnn --extra onnx
|
| 75 |
+
- run: uv run pytest tests/unit/export -v
|
tests/parity/test_legacy_tracking.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Characterization of the legacy dataset item contract."""
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def _dataset_item(legacy_dataset_module_without_dgl, labels, tracking, globals_):
|
| 7 |
+
dataset = object.__new__(legacy_dataset_module_without_dgl.RootDataset)
|
| 8 |
+
dataset.graphs = [object() for _ in labels]
|
| 9 |
+
dataset.labels = torch.as_tensor(labels)
|
| 10 |
+
dataset.tracking = torch.as_tensor(tracking, dtype=torch.float32)
|
| 11 |
+
dataset.global_features = torch.as_tensor(globals_, dtype=torch.float32)
|
| 12 |
+
return dataset
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def test_dataset_item_contract_preserves_label_tracking_and_global_shapes(
|
| 16 |
+
legacy_dataset_module_without_dgl,
|
| 17 |
+
):
|
| 18 |
+
dataset = object.__new__(legacy_dataset_module_without_dgl.RootDataset)
|
| 19 |
+
graph = object()
|
| 20 |
+
dataset.graphs = [graph]
|
| 21 |
+
dataset.labels = torch.tensor([7])
|
| 22 |
+
dataset.tracking = torch.tensor([[3.0, 2.5]], dtype=torch.float32)
|
| 23 |
+
dataset.global_features = torch.empty((1, 0), dtype=torch.float32)
|
| 24 |
+
item = dataset[0]
|
| 25 |
+
assert item[0] is graph
|
| 26 |
+
assert item[1].shape == torch.Size([])
|
| 27 |
+
assert item[1].dtype == torch.int64
|
| 28 |
+
assert item[2].shape == (2,)
|
| 29 |
+
assert item[2].dtype == torch.float32
|
| 30 |
+
assert item[2].tolist() == [3.0, 2.5]
|
| 31 |
+
assert item[3].shape == (0,)
|
| 32 |
+
assert item[3].dtype == torch.float32
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_dataset_item_contract_preserves_binary_and_multiclass_labels(
|
| 36 |
+
legacy_dataset_module_without_dgl,
|
| 37 |
+
):
|
| 38 |
+
dataset = _dataset_item(
|
| 39 |
+
legacy_dataset_module_without_dgl,
|
| 40 |
+
labels=[0, 1, 11],
|
| 41 |
+
tracking=[[0.0, 1.0], [3.0, -2.5], [7.0, 0.0]],
|
| 42 |
+
globals_=[[], [], []],
|
| 43 |
+
)
|
| 44 |
+
items = [dataset[index] for index in range(3)]
|
| 45 |
+
assert [item[1].item() for item in items] == [0, 1, 11]
|
| 46 |
+
assert all(item[1].shape == torch.Size([]) for item in items)
|
| 47 |
+
assert all(item[1].dtype == torch.int64 for item in items)
|
| 48 |
+
assert items[1][2].tolist() == [3.0, -2.5]
|
| 49 |
+
assert all(item[3].shape == (0,) for item in items)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def test_dataset_item_contract_preserves_nonempty_global_features(
|
| 53 |
+
legacy_dataset_module_without_dgl,
|
| 54 |
+
):
|
| 55 |
+
dataset = _dataset_item(
|
| 56 |
+
legacy_dataset_module_without_dgl,
|
| 57 |
+
labels=[1],
|
| 58 |
+
tracking=[[4.0, 2.0]],
|
| 59 |
+
globals_=[[12.5, -3.0]],
|
| 60 |
+
)
|
| 61 |
+
item = dataset[0]
|
| 62 |
+
assert item[3].shape == (2,)
|
| 63 |
+
assert item[3].dtype == torch.float32
|
| 64 |
+
assert item[3].tolist() == [12.5, -3.0]
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def test_fold_selection_uses_tracking_column_zero_and_keeps_weights(
|
| 68 |
+
legacy_utils_module,
|
| 69 |
+
):
|
| 70 |
+
sample = type(
|
| 71 |
+
"Sample",
|
| 72 |
+
(),
|
| 73 |
+
{"tracking": torch.tensor([[0.0, 1.0], [1.0, -2.5], [2.0, 0.0], [3.0, 4.0]])},
|
| 74 |
+
)()
|
| 75 |
+
selection = legacy_utils_module.fold_selection({"n_folds": 2, "test": [1]}, "test")
|
| 76 |
+
assert selection(sample).tolist() == [False, True, False, True]
|
| 77 |
+
assert sample.tracking[:, 1].tolist() == [1.0, -2.5, 0.0, 4.0]
|
tests/unit/test_package_import.py
CHANGED
|
@@ -5,3 +5,35 @@ def test_package_imports() -> None:
|
|
| 5 |
import gnn4colliders
|
| 6 |
|
| 7 |
assert gnn4colliders.__file__ is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
import gnn4colliders
|
| 6 |
|
| 7 |
assert gnn4colliders.__file__ is not None
|
| 8 |
+
assert gnn4colliders.__version__ == "0.1.0"
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def test_public_import_contract() -> None:
|
| 12 |
+
from gnn4colliders.inference import Predictor
|
| 13 |
+
from gnn4colliders.models.root_gnn import EdgeNetwork, FineTunedEdgeNetwork
|
| 14 |
+
from gnn4colliders.tasks import (
|
| 15 |
+
BinaryClassificationTask,
|
| 16 |
+
MulticlassClassificationTask,
|
| 17 |
+
)
|
| 18 |
+
from gnn4colliders.training import CheckpointManager, Trainer
|
| 19 |
+
|
| 20 |
+
assert all(
|
| 21 |
+
item is not None
|
| 22 |
+
for item in (
|
| 23 |
+
EdgeNetwork,
|
| 24 |
+
FineTunedEdgeNetwork,
|
| 25 |
+
BinaryClassificationTask,
|
| 26 |
+
MulticlassClassificationTask,
|
| 27 |
+
Trainer,
|
| 28 |
+
CheckpointManager,
|
| 29 |
+
Predictor,
|
| 30 |
+
)
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_hydra_config_is_discoverable_from_package() -> None:
|
| 35 |
+
from gnn4colliders.cli import _config
|
| 36 |
+
|
| 37 |
+
config = _config([])
|
| 38 |
+
assert config.model.name == "edge_network"
|
| 39 |
+
assert config.task.type == "multiclass_classification"
|