diff --git a/.gitattributes b/.gitattributes index 9030923a7..5be91f972 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.ipynb linguist-vendored \ No newline at end of file +*.ipynb linguist-vendored diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index fac374d31..65cf5ffee 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -73,4 +73,4 @@ available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.ht [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq \ No newline at end of file +https://www.contributor-covenant.org/faq diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0f7f5a185..dbd427b95 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -59,4 +59,4 @@ jobs: token: ${{ secrets.DOCUMENTATION_KEY }} repository-name: geometric-intelligence/geometric-intelligence.github.io target-folder: topobench - clean: true \ No newline at end of file + clean: true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 42d3152ec..8a2fd3aed 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,4 +22,4 @@ jobs: # This ensures the ruff version matches what you have in pyproject.toml version-file: "pyproject.toml" # Optional: fails the build if the code changes (e.g. formatting) - changed-files: "true" \ No newline at end of file + changed-files: "true" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fccede8cd..abc219ca2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,4 +44,4 @@ jobs: with: token: ${{ secrets.CODECOV_TOKEN }} file: coverage.xml - fail_ci_if_error: false \ No newline at end of file + fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index c80ce3360..43d350c52 100755 --- a/.gitignore +++ b/.gitignore @@ -195,4 +195,4 @@ wandb/ # test temporary .test_tmp/ -uv.lock \ No newline at end of file +uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d9bf57982..4d29c9812 100755 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,10 +5,17 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: + - id: end-of-file-fixer + - id: trailing-whitespace + args: + - --markdown-linebreak-ext=md - id: fix-byte-order-marker - id: check-case-conflict - id: check-merge-conflict + - id: check-ast + - id: check-json - id: check-yaml + - id: check-symlinks - id: mixed-line-ending args: - --fix=no diff --git a/README.md b/README.md index 4d32d0ace..a3e2dbf72 100755 --- a/README.md +++ b/README.md @@ -30,19 +30,19 @@ Assess how your model compares against state-of-the-art topological neural netwo Neural NetworksLiftings and TransformsDatasets • - References + References

--- -> 🏆 The **TAG-DS Topological Deep Learning Challenge 2025** has concluded! A huge shotout to all participants. Check out the winners and honorable mentions on [`the challenge website`](https://geometric-intelligence.github.io/topobench/tdl-challenge/index.html). +> 🏆 The **TAG-DS Topological Deep Learning Challenge 2025** has concluded! A huge shout-out to all participants. Check out the winners and honorable mentions on [`the challenge website`](https://geometric-intelligence.github.io/topobench/tdl-challenge/index.html). --- ## :pushpin: Overview -`TopoBench` (TB) is a modular Python library designed to standardize benchmarking and accelerate research in Topological Deep Learning (TDL). In particular, TB allows to train and compare the performances of all sorts of Topological Neural Networks (TNNs) across the different topological domains, where by _topological domain_ we refer to a graph, a simplicial complex, a cellular complex, or a hypergraph. For detailed information, please refer to the [`TopoBench: A Framework for Benchmarking Topological Deep Learning`](https://arxiv.org/pdf/2406.06642) paper. +`TopoBench` (TB) is a modular Python library designed to standardize benchmarking and accelerate research in Topological Deep Learning (TDL). In particular, TB allows training and comparing the performances of all sorts of Topological Neural Networks (TNNs) across the different topological domains, where by _topological domain_ we refer to a graph, a simplicial complex, a cellular complex, or a hypergraph. For detailed information, please refer to the [`TopoBench: A Framework for Benchmarking Topological Deep Learning`](https://arxiv.org/pdf/2406.06642) paper.

@@ -94,7 +94,7 @@ uv sync --all-extras Once the environment is active, you can launch the TopoBench pipeline: ```bash # Using the activated virtual environment -python -m topobench +python -m topobench # Or execute directly via uv without manual activation uv run python -m topobench @@ -201,7 +201,7 @@ bash scripts/reproduce.sh ## :anchor: Tutorials -Explore our [tutorials](https://github.com/geometric-intelligence/TopoBench/tree/main/tutorials) for further details on how to add new datasets, transforms/liftings, and benchmark tasks. +Explore our [tutorials](https://github.com/geometric-intelligence/TopoBench/tree/main/tutorials) for further details on how to add new datasets, transforms/liftings, and benchmark tasks. ## :gear: Neural Networks @@ -264,13 +264,13 @@ We list the neural networks trained and evaluated by `TopoBench`, organized by t ## :rocket: Liftings and Transforms -We list the liftings used in `TopoBench` to transform datasets. Here, a _lifting_ refers to a function that transforms a dataset defined on a topological domain (_e.g._, on a graph) into the same dataset but supported on a different topological domain (_e.g._, on a simplicial complex). +We list the liftings used in `TopoBench` to transform datasets. Here, a _lifting_ refers to a function that transforms a dataset defined on a topological domain (_e.g._, on a graph) into the same dataset but supported on a different topological domain (_e.g._, on a simplicial complex). ### Structural Liftings The structural lifting is responsible for the transformation of the underlying relationships or elements of the data. For instance, it might determine how nodes and edges in a graph are mapped into triangles and tetrahedra in a simplicial complex. This structural transformation can be further categorized into connectivity-based, where the mapping relies solely on the existing connections within the data, and feature-based, where the data's inherent properties or features guide the new structure. -We enumerate below the structural liftings currently implemented in `TopoBench`; please check out the provided description links for further details. +We enumerate below the structural liftings currently implemented in `TopoBench`; please check out the provided description links for further details. **Remark:**: Most of these liftings are adaptations of winner submissions of the ICML TDL Challenge 2024 ([paper](https://proceedings.mlr.press/v251/bernardez24a.html) | [repo](https://github.com/pyt-team/challenge-icml-2024)); see the [Structural Liftings wiki](https://github.com/geometric-intelligence/TopoBench/wiki/Structural-Liftings) for a complete list of compatible liftings. @@ -338,11 +338,11 @@ Feature liftings address the transfer of data attributes or features during mapp | ProjectionSum | Projects r-cell features of a graph to r+1-cell structures utilizing incidence matrices \(B_{r}\). | All | | ConcatenationLifting | Concatenate r-cell features to obtain r+1-cell features. | Simplicial | -### Data Transformations +### Data Transformations Specially useful in pre-processing steps, these are the general data manipulations currently implemented in `TopoBench`: -| Transform | Description | +| Transform | Description | | --- | --- | | OneHotDegreeFeatures | Adds the node degree as one hot encodings to the node features. | | NodeFeaturesToFloat |Converts the node features of the input graph to float. | @@ -473,12 +473,12 @@ If you find `TopoBench` useful, we would appreciate if you cite us!

More information regarding Topological Deep Learning - + [Topological Graph Signal Compression](https://arxiv.org/pdf/2308.11068) - + [Architectures of Topological Deep Learning: A Survey on Topological Neural Networks](https://par.nsf.gov/servlets/purl/10477141) - - [TopoX: a suite of Python packages for machine learning on topological domains](https://arxiv.org/pdf/2402.02441) + + [TopoX: a suite of Python packages for machine learning on topological domains](https://arxiv.org/pdf/2402.02441)
--- @@ -490,4 +490,3 @@ Feel free to reach out via email if you want to collaborate, do your thesis with 📧 **Contact Email:** [topological.intelligence@gmail.com](mailto:topological.intelligence@gmail.com) ▶️ **YouTube Channel:** [Topological Intelligence](https://www.youtube.com/@TopologicalIntelligence) - diff --git a/codecov.yml b/codecov.yml index bd528f9ba..67ba6ee51 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,4 +5,4 @@ coverage: precision: 2 ignore: - "test/" - - "topobench/run.py" \ No newline at end of file + - "topobench/run.py" diff --git a/configs/callbacks/default.yaml b/configs/callbacks/default.yaml index 3e5420c10..56662e54e 100755 --- a/configs/callbacks/default.yaml +++ b/configs/callbacks/default.yaml @@ -25,4 +25,4 @@ model_summary: learning_rate_monitor: _target_: lightning.pytorch.callbacks.LearningRateMonitor - logging_interval: "epoch" \ No newline at end of file + logging_interval: "epoch" diff --git a/configs/callbacks/model_timer.yaml b/configs/callbacks/model_timer.yaml index 233a52e62..41d61152f 100644 --- a/configs/callbacks/model_timer.yaml +++ b/configs/callbacks/model_timer.yaml @@ -1,4 +1,4 @@ # https://lightning.ai/docs/pytorch/stable/api/lightning.pytorch.callbacks.Timer.html model_timer: - _target_: topobench.callbacks.timer_callback.PipelineTimer \ No newline at end of file + _target_: topobench.callbacks.timer_callback.PipelineTimer diff --git a/configs/dataset/graph/AQSOL.yaml b/configs/dataset/graph/AQSOL.yaml index 5f004c44d..53b6e719f 100644 --- a/configs/dataset/graph/AQSOL.yaml +++ b/configs/dataset/graph/AQSOL.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.MoleculeDatasetLoader - parameters: + parameters: data_domain: graph data_type: AQSOL data_name: AQSOL @@ -29,4 +29,3 @@ dataloader_params: batch_size: 10 # We have an issue with allowing multiple graphs in a batch due to sparse incidences num_workers: 0 pin_memory: False - diff --git a/configs/dataset/graph/IMDB-BINARY.yaml b/configs/dataset/graph/IMDB-BINARY.yaml index 34c2b4e16..c760a7c73 100755 --- a/configs/dataset/graph/IMDB-BINARY.yaml +++ b/configs/dataset/graph/IMDB-BINARY.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: IMDB-BINARY @@ -29,9 +29,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 64 + batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/IMDB-MULTI.yaml b/configs/dataset/graph/IMDB-MULTI.yaml index f1d879adc..09d615d67 100755 --- a/configs/dataset/graph/IMDB-MULTI.yaml +++ b/configs/dataset/graph/IMDB-MULTI.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: IMDB-MULTI @@ -32,6 +32,3 @@ dataloader_params: batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/MUTAG.yaml b/configs/dataset/graph/MUTAG.yaml index 5a09da6dc..1e3cbea57 100755 --- a/configs/dataset/graph/MUTAG.yaml +++ b/configs/dataset/graph/MUTAG.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: MUTAG diff --git a/configs/dataset/graph/NCI1.yaml b/configs/dataset/graph/NCI1.yaml index 4a6e35823..2123d47da 100755 --- a/configs/dataset/graph/NCI1.yaml +++ b/configs/dataset/graph/NCI1.yaml @@ -1,6 +1,6 @@ loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: NCI1 @@ -26,9 +26,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 64 + batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/NCI109.yaml b/configs/dataset/graph/NCI109.yaml index 1978e75a8..6038c1139 100755 --- a/configs/dataset/graph/NCI109.yaml +++ b/configs/dataset/graph/NCI109.yaml @@ -1,6 +1,6 @@ loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: NCI109 @@ -29,6 +29,3 @@ dataloader_params: batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/PROTEINS.yaml b/configs/dataset/graph/PROTEINS.yaml index 0ab71fc71..827c6aaa7 100755 --- a/configs/dataset/graph/PROTEINS.yaml +++ b/configs/dataset/graph/PROTEINS.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: PROTEINS @@ -30,6 +30,3 @@ dataloader_params: batch_size: 128 # We have an issue with allowing multiple graphs in a batch due to sparse incidences num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/REDDIT-BINARY.yaml b/configs/dataset/graph/REDDIT-BINARY.yaml index 25df4b0bb..f368a3b7b 100755 --- a/configs/dataset/graph/REDDIT-BINARY.yaml +++ b/configs/dataset/graph/REDDIT-BINARY.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.TUDatasetLoader - parameters: + parameters: data_domain: graph data_type: TUDataset data_name: REDDIT-BINARY @@ -29,9 +29,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 128 + batch_size: 128 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/US-county-demos.yaml b/configs/dataset/graph/US-county-demos.yaml index 5d3782e26..e91acb4b7 100755 --- a/configs/dataset/graph/US-county-demos.yaml +++ b/configs/dataset/graph/US-county-demos.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.USCountyDemosDatasetLoader - parameters: + parameters: data_domain: graph data_type: cornel data_name: US-county-demos diff --git a/configs/dataset/graph/ZINC.yaml b/configs/dataset/graph/ZINC.yaml index 9fc0a8f41..44d7103f4 100644 --- a/configs/dataset/graph/ZINC.yaml +++ b/configs/dataset/graph/ZINC.yaml @@ -3,7 +3,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.MoleculeDatasetLoader - parameters: + parameters: data_domain: graph data_type: ZINC data_name: ZINC diff --git a/configs/dataset/graph/amazon_ratings.yaml b/configs/dataset/graph/amazon_ratings.yaml index 163f47c8b..1c6976893 100755 --- a/configs/dataset/graph/amazon_ratings.yaml +++ b/configs/dataset/graph/amazon_ratings.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HeterophilousGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: heterophilic data_name: amazon_ratings diff --git a/configs/dataset/graph/cocitation_citeseer.yaml b/configs/dataset/graph/cocitation_citeseer.yaml index 4b4483877..f250c5a67 100755 --- a/configs/dataset/graph/cocitation_citeseer.yaml +++ b/configs/dataset/graph/cocitation_citeseer.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.PlanetoidDatasetLoader - parameters: + parameters: data_domain: graph data_type: cocitation data_name: citeseer diff --git a/configs/dataset/graph/cocitation_cora.yaml b/configs/dataset/graph/cocitation_cora.yaml index d344f44fc..31f342d94 100755 --- a/configs/dataset/graph/cocitation_cora.yaml +++ b/configs/dataset/graph/cocitation_cora.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.PlanetoidDatasetLoader - parameters: + parameters: data_domain: graph data_type: cocitation data_name: Cora diff --git a/configs/dataset/graph/cocitation_pubmed.yaml b/configs/dataset/graph/cocitation_pubmed.yaml index 4c7173ac1..32e869cce 100755 --- a/configs/dataset/graph/cocitation_pubmed.yaml +++ b/configs/dataset/graph/cocitation_pubmed.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.PlanetoidDatasetLoader - parameters: + parameters: data_domain: graph data_type: cocitation data_name: PubMed diff --git a/configs/dataset/graph/manual_dataset.yaml b/configs/dataset/graph/manual_dataset.yaml index 73a98fbd3..57b4a7169 100755 --- a/configs/dataset/graph/manual_dataset.yaml +++ b/configs/dataset/graph/manual_dataset.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.ManualGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: manual_dataset data_name: manual @@ -28,6 +28,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 1 + batch_size: 1 num_workers: 1 pin_memory: False diff --git a/configs/dataset/graph/minesweeper.yaml b/configs/dataset/graph/minesweeper.yaml index 00d9c3cdd..dffbca48a 100755 --- a/configs/dataset/graph/minesweeper.yaml +++ b/configs/dataset/graph/minesweeper.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HeterophilousGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: heterophilic data_name: minesweeper diff --git a/configs/dataset/graph/ogbg-molhiv.yaml b/configs/dataset/graph/ogbg-molhiv.yaml index afcae4756..6e8e4730d 100644 --- a/configs/dataset/graph/ogbg-molhiv.yaml +++ b/configs/dataset/graph/ogbg-molhiv.yaml @@ -1,7 +1,7 @@ loader: _target_: topobench.data.loaders.OGBGDatasetLoader - parameters: + parameters: data_domain: graph data_type: OGBGDataset data_name: ogbg-molhiv @@ -11,7 +11,7 @@ loader: parameters: num_features: 9 num_classes: 2 - task: classification + task: classification loss_type: cross_entropy monitor_metric: accuracy task_level: graph @@ -27,9 +27,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 64 + batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/ogbg-molpcba.yaml b/configs/dataset/graph/ogbg-molpcba.yaml index 0b8edde41..7f936ec40 100644 --- a/configs/dataset/graph/ogbg-molpcba.yaml +++ b/configs/dataset/graph/ogbg-molpcba.yaml @@ -1,7 +1,7 @@ loader: _target_: topobench.data.loaders.OGBGDatasetLoader - parameters: + parameters: data_domain: graph data_type: OGBGDataset data_name: ogbg-molpcba @@ -27,9 +27,6 @@ split_params: # Dataloader parameters dataloader_params: - batch_size: 64 + batch_size: 64 num_workers: 0 pin_memory: False - - - diff --git a/configs/dataset/graph/questions.yaml b/configs/dataset/graph/questions.yaml index 676de53cf..a51b2d8f9 100755 --- a/configs/dataset/graph/questions.yaml +++ b/configs/dataset/graph/questions.yaml @@ -1,6 +1,6 @@ loader: _target_: topobench.data.loaders.HeterophilousGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: heterophilic data_name: questions diff --git a/configs/dataset/graph/roman_empire.yaml b/configs/dataset/graph/roman_empire.yaml index 900115d87..a90608afd 100755 --- a/configs/dataset/graph/roman_empire.yaml +++ b/configs/dataset/graph/roman_empire.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HeterophilousGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: heterophilic data_name: roman_empire diff --git a/configs/dataset/graph/tolokers.yaml b/configs/dataset/graph/tolokers.yaml index f8c628b3f..327d0bda4 100755 --- a/configs/dataset/graph/tolokers.yaml +++ b/configs/dataset/graph/tolokers.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HeterophilousGraphDatasetLoader - parameters: + parameters: data_domain: graph data_type: heterophilic data_name: tolokers diff --git a/configs/dataset/hypergraph/20newsgroup.yaml b/configs/dataset/hypergraph/20newsgroup.yaml index f1e5fe365..e42a34690 100755 --- a/configs/dataset/hypergraph/20newsgroup.yaml +++ b/configs/dataset/hypergraph/20newsgroup.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: ml_datasets data_name: 20newsW100 diff --git a/configs/dataset/hypergraph/ModelNet40.yaml b/configs/dataset/hypergraph/ModelNet40.yaml index dfc59e274..4b0f6492c 100755 --- a/configs/dataset/hypergraph/ModelNet40.yaml +++ b/configs/dataset/hypergraph/ModelNet40.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: shapes data_name: ModelNet40 diff --git a/configs/dataset/hypergraph/Mushroom.yaml b/configs/dataset/hypergraph/Mushroom.yaml index a0b5d829e..37bc50bb4 100755 --- a/configs/dataset/hypergraph/Mushroom.yaml +++ b/configs/dataset/hypergraph/Mushroom.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: ml_datasets data_name: Mushroom diff --git a/configs/dataset/hypergraph/NTU2012.yaml b/configs/dataset/hypergraph/NTU2012.yaml index 59c5be255..4389e9fe0 100755 --- a/configs/dataset/hypergraph/NTU2012.yaml +++ b/configs/dataset/hypergraph/NTU2012.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: shapes data_name: NTU2012 diff --git a/configs/dataset/hypergraph/coauthorship_cora.yaml b/configs/dataset/hypergraph/coauthorship_cora.yaml index 81592ef53..020204e6f 100755 --- a/configs/dataset/hypergraph/coauthorship_cora.yaml +++ b/configs/dataset/hypergraph/coauthorship_cora.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.CitationHypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: coauthorship data_name: coauthorship_cora diff --git a/configs/dataset/hypergraph/coauthorship_dblp.yaml b/configs/dataset/hypergraph/coauthorship_dblp.yaml index 12d9a79e4..7e0eebbba 100755 --- a/configs/dataset/hypergraph/coauthorship_dblp.yaml +++ b/configs/dataset/hypergraph/coauthorship_dblp.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.CitationHypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: coauthorship data_name: coauthorship_dblp diff --git a/configs/dataset/hypergraph/cocitation_citeseer.yaml b/configs/dataset/hypergraph/cocitation_citeseer.yaml index 1bd453331..bb8500ec8 100755 --- a/configs/dataset/hypergraph/cocitation_citeseer.yaml +++ b/configs/dataset/hypergraph/cocitation_citeseer.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.CitationHypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: cocitation data_name: cocitation_citeseer diff --git a/configs/dataset/hypergraph/cocitation_cora.yaml b/configs/dataset/hypergraph/cocitation_cora.yaml index 403ff6dc9..83841ca07 100755 --- a/configs/dataset/hypergraph/cocitation_cora.yaml +++ b/configs/dataset/hypergraph/cocitation_cora.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.CitationHypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: cocitation data_name: cocitation_cora diff --git a/configs/dataset/hypergraph/cocitation_pubmed.yaml b/configs/dataset/hypergraph/cocitation_pubmed.yaml index e0b9a8b2b..27ac459f2 100755 --- a/configs/dataset/hypergraph/cocitation_pubmed.yaml +++ b/configs/dataset/hypergraph/cocitation_pubmed.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.CitationHypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: cocitation data_name: cocitation_pubmed diff --git a/configs/dataset/hypergraph/zoo.yaml b/configs/dataset/hypergraph/zoo.yaml index 690a8dc58..9f9809dd9 100755 --- a/configs/dataset/hypergraph/zoo.yaml +++ b/configs/dataset/hypergraph/zoo.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.HypergraphDatasetLoader - parameters: + parameters: data_domain: hypergraph data_type: ml_datasets data_name: zoo diff --git a/configs/dataset/pointcloud/geometric_shapes.yaml b/configs/dataset/pointcloud/geometric_shapes.yaml index 0a9fa511e..7ad05248b 100644 --- a/configs/dataset/pointcloud/geometric_shapes.yaml +++ b/configs/dataset/pointcloud/geometric_shapes.yaml @@ -1,7 +1,7 @@ # Dataset loader config loader: _target_: topobench.data.loaders.GeometricShapesDatasetLoader - parameters: + parameters: data_domain: pointcloud data_type: geometric_shapes data_name: geometric_shapes @@ -29,4 +29,3 @@ dataloader_params: batch_size: 10 # We have an issue with allowing multiple graphs in a batch due to sparse incidences num_workers: 0 pin_memory: False - diff --git a/configs/dataset/simplicial/mantra_betti_numbers.yaml b/configs/dataset/simplicial/mantra_betti_numbers.yaml index 57c0a6c14..eae90b5f4 100755 --- a/configs/dataset/simplicial/mantra_betti_numbers.yaml +++ b/configs/dataset/simplicial/mantra_betti_numbers.yaml @@ -1,8 +1,8 @@ loader: _target_: topobench.data.loaders.MantraSimplicialDatasetLoader - parameters: + parameters: data_domain: simplicial - data_type: topological + data_type: topological data_name: MANTRA data_dir: ${paths.data_dir}/${dataset.loader.parameters.data_domain}/${dataset.loader.parameters.data_type} manifold_dim: 3 @@ -13,20 +13,20 @@ loader: # Data definition parameters: - # In the case of higher-order datasets we have multiple feature dimentions + # In the case of higher-order datasets we have multiple feature dimensions num_features: [1,1,1] - #num_classes: 2 # Num classes depents on the task_variable + #num_classes: 2 # Num classes depends on the task_variable # Dataset parameters # task: classification # TODO: adapt pipeline to support multilabel classification # loss_type: cross_entropy # TODO: adapt pipeline to support multilabel classification # monitor_metric: accuracy # TODO: adapt pipeline to support multilabel classification - task_level: graph + task_level: graph data_seed: 0 #splits split_params: - learning_setting: inductive + learning_setting: inductive data_split_dir: ${paths.data_dir}/data_splits/${dataset.loader.parameters.data_name} data_seed: 0 split_type: random #'k-fold' # either "k-fold" or "random" strategies @@ -38,4 +38,4 @@ dataloader_params: batch_size: 5 num_workers: 0 pin_memory: False - persistent_workers: False \ No newline at end of file + persistent_workers: False diff --git a/configs/dataset/simplicial/mantra_genus.yaml b/configs/dataset/simplicial/mantra_genus.yaml index 017b146e2..31d484987 100755 --- a/configs/dataset/simplicial/mantra_genus.yaml +++ b/configs/dataset/simplicial/mantra_genus.yaml @@ -1,8 +1,8 @@ loader: _target_: topobench.data.loaders.MantraSimplicialDatasetLoader - parameters: + parameters: data_domain: simplicial - data_type: topological + data_type: topological data_name: MANTRA data_dir: ${paths.data_dir}/${dataset.loader.parameters.data_domain}/${dataset.loader.parameters.data_type} manifold_dim: 2 @@ -13,20 +13,20 @@ loader: # Data definition parameters: - # In the case of higher-order datasets we have multiple feature dimentions + # In the case of higher-order datasets we have multiple feature dimensions num_features: [1,1,1] - num_classes: 8 # Num classes depents on the task_variable + num_classes: 8 # Num classes depends on the task_variable # Dataset parameters task: classification loss_type: cross_entropy monitor_metric: accuracy - task_level: graph + task_level: graph data_seed: 0 #splits split_params: - learning_setting: inductive + learning_setting: inductive data_split_dir: ${paths.data_dir}/data_splits/${dataset.loader.parameters.data_name} data_seed: 0 split_type: random #'k-fold' # either "k-fold" or "random" strategies @@ -38,4 +38,4 @@ dataloader_params: batch_size: 5 num_workers: 0 pin_memory: False - persistent_workers: False \ No newline at end of file + persistent_workers: False diff --git a/configs/dataset/simplicial/mantra_name.yaml b/configs/dataset/simplicial/mantra_name.yaml index 523d88734..3c0f09106 100755 --- a/configs/dataset/simplicial/mantra_name.yaml +++ b/configs/dataset/simplicial/mantra_name.yaml @@ -1,8 +1,8 @@ loader: _target_: topobench.data.loaders.MantraSimplicialDatasetLoader - parameters: + parameters: data_domain: simplicial - data_type: topological + data_type: topological data_name: MANTRA data_dir: ${paths.data_dir}/${dataset.loader.parameters.data_domain}/${dataset.loader.parameters.data_type} manifold_dim: 2 @@ -13,20 +13,20 @@ loader: # Data definition parameters: - # In the case of higher-order datasets we have multiple feature dimentions + # In the case of higher-order datasets we have multiple feature dimensions num_features: [1,1,1] - num_classes: 8 # Num classes depents on the task_variable + num_classes: 8 # Num classes depends on the task_variable # Dataset parameters task: classification loss_type: cross_entropy monitor_metric: accuracy - task_level: graph + task_level: graph data_seed: 0 #splits split_params: - learning_setting: inductive + learning_setting: inductive data_split_dir: ${paths.data_dir}/data_splits/${dataset.loader.parameters.data_name} data_seed: 0 split_type: random #'k-fold' # either "k-fold" or "random" strategies @@ -38,4 +38,4 @@ dataloader_params: batch_size: 5 num_workers: 0 pin_memory: False - persistent_workers: False \ No newline at end of file + persistent_workers: False diff --git a/configs/dataset/simplicial/mantra_orientation.yaml b/configs/dataset/simplicial/mantra_orientation.yaml index 7d79b0079..dc11c8092 100755 --- a/configs/dataset/simplicial/mantra_orientation.yaml +++ b/configs/dataset/simplicial/mantra_orientation.yaml @@ -1,8 +1,8 @@ loader: _target_: topobench.data.loaders.MantraSimplicialDatasetLoader - parameters: + parameters: data_domain: simplicial - data_type: topological + data_type: topological data_name: MANTRA data_dir: ${paths.data_dir}/${dataset.loader.parameters.data_domain}/${dataset.loader.parameters.data_type} manifold_dim: 2 @@ -13,20 +13,20 @@ loader: # Data definition parameters: - # In the case of higher-order datasets we have multiple feature dimentions + # In the case of higher-order datasets we have multiple feature dimensions num_features: [1,1,1] - num_classes: 2 # Num classes depents on the task_variable + num_classes: 2 # Num classes depends on the task_variable # Dataset parameters task: classification loss_type: cross_entropy monitor_metric: accuracy - task_level: graph + task_level: graph data_seed: 0 #splits split_params: - learning_setting: inductive + learning_setting: inductive data_split_dir: ${paths.data_dir}/data_splits/${dataset.loader.parameters.data_name} data_seed: 0 split_type: random #'k-fold' # either "k-fold" or "random" strategies @@ -38,4 +38,4 @@ dataloader_params: batch_size: 5 num_workers: 0 pin_memory: False - persistent_workers: False \ No newline at end of file + persistent_workers: False diff --git a/configs/evaluator/classification.yaml b/configs/evaluator/classification.yaml index b7afca268..d7e12d2c2 100755 --- a/configs/evaluator/classification.yaml +++ b/configs/evaluator/classification.yaml @@ -4,4 +4,4 @@ task_level: ${dataset.parameters.task_level} num_classes: ${dataset.parameters.num_classes} # Metrics -metrics: [accuracy, precision, recall, auroc] # Available options: accuracy, auroc, precision, recall \ No newline at end of file +metrics: [accuracy, precision, recall, auroc] # Available options: accuracy, auroc, precision, recall diff --git a/configs/evaluator/default.yaml b/configs/evaluator/default.yaml index f89dec6c2..18c77ad0a 100755 --- a/configs/evaluator/default.yaml +++ b/configs/evaluator/default.yaml @@ -7,4 +7,4 @@ num_classes: ${dataset.parameters.num_classes} # Classification: [accuracy, precision, recall, auroc] # Regression: [mae, mse] metrics: ${get_default_metrics:${evaluator.task},${oc.select:dataset.parameters.metrics,null}} -# Select classification/regression config files to manually define the metrics \ No newline at end of file +# Select classification/regression config files to manually define the metrics diff --git a/configs/evaluator/regression.yaml b/configs/evaluator/regression.yaml index 623291761..fab3283c9 100755 --- a/configs/evaluator/regression.yaml +++ b/configs/evaluator/regression.yaml @@ -4,4 +4,4 @@ task_level: ${dataset.parameters.task_level} num_classes: ${dataset.parameters.num_classes} # Metrics -metrics: [mae, mse] #Available options: mae, mse \ No newline at end of file +metrics: [mae, mse] #Available options: mae, mse diff --git a/configs/experiment/example.yaml b/configs/experiment/example.yaml index dd60c2d8a..63efcfbb4 100755 --- a/configs/experiment/example.yaml +++ b/configs/experiment/example.yaml @@ -31,4 +31,3 @@ model: dataset: split_type: k-fold k: 10 - diff --git a/configs/hydra/default.yaml b/configs/hydra/default.yaml index d09ee508d..a61e9b3a3 100644 --- a/configs/hydra/default.yaml +++ b/configs/hydra/default.yaml @@ -16,4 +16,4 @@ job_logging: handlers: file: # Incorporates fix from https://github.com/facebookresearch/hydra/pull/2242 - filename: ${hydra.runtime.output_dir}/${task_name}.log \ No newline at end of file + filename: ${hydra.runtime.output_dir}/${task_name}.log diff --git a/configs/loss/default.yaml b/configs/loss/default.yaml index a97ad8549..3d5b4bcd5 100644 --- a/configs/loss/default.yaml +++ b/configs/loss/default.yaml @@ -7,4 +7,4 @@ dataset_loss: modules_losses: # Collect model losses feature_encoder: ${oc.select:model.feature_encoder.loss,null} backbone: ${oc.select:model.backbone.loss,null} - readout: ${oc.select:model.readout.loss,null} \ No newline at end of file + readout: ${oc.select:model.readout.loss,null} diff --git a/configs/model/cell/can.yaml b/configs/model/cell/can.yaml index 5f0638043..be81d82fa 100755 --- a/configs/model/cell/can.yaml +++ b/configs/model/cell/can.yaml @@ -6,7 +6,7 @@ model_domain: cell feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 128 proj_dropout: 0.0 selected_dimensions: @@ -19,7 +19,7 @@ backbone: in_channels_1: ${model.feature_encoder.out_channels} out_channels: ${model.feature_encoder.out_channels} dropout: 0.0 - heads: 1 # For now we stuck to out_channels//heads, keep heads = 1 + heads: 1 # For now we stuck to out_channels//heads, keep heads = 1 concat: True skip_connection: True n_layers: 1 diff --git a/configs/model/cell/cccn.yaml b/configs/model/cell/cccn.yaml index 3d3a09811..19bfd9839 100755 --- a/configs/model/cell/cccn.yaml +++ b/configs/model/cell/cccn.yaml @@ -6,7 +6,7 @@ model_domain: cell feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/model/cell/ccxn.yaml b/configs/model/cell/ccxn.yaml index cd4755048..ce3e44339 100755 --- a/configs/model/cell/ccxn.yaml +++ b/configs/model/cell/ccxn.yaml @@ -6,7 +6,7 @@ model_domain: cell feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0.0 diff --git a/configs/model/cell/cwn.yaml b/configs/model/cell/cwn.yaml index f765365f5..ccd85647c 100755 --- a/configs/model/cell/cwn.yaml +++ b/configs/model/cell/cwn.yaml @@ -6,7 +6,7 @@ model_domain: cell feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 64 proj_dropout: 0.0 diff --git a/configs/model/cell/topotune.yaml b/configs/model/cell/topotune.yaml index 3cfb83504..abea53926 100755 --- a/configs/model/cell/topotune.yaml +++ b/configs/model/cell/topotune.yaml @@ -7,7 +7,7 @@ tune_gnn: GCN feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/model/cell/topotune_onehasse.yaml b/configs/model/cell/topotune_onehasse.yaml index 1aba73a22..02cf88a0c 100644 --- a/configs/model/cell/topotune_onehasse.yaml +++ b/configs/model/cell/topotune_onehasse.yaml @@ -7,7 +7,7 @@ tune_gnn: IdentityGCN feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/model/combinatorial/topotune.yaml b/configs/model/combinatorial/topotune.yaml index 0f25af76b..0fb9d9cea 100755 --- a/configs/model/combinatorial/topotune.yaml +++ b/configs/model/combinatorial/topotune.yaml @@ -7,7 +7,7 @@ tune_gnn: GCN feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/model/graph/gat.yaml b/configs/model/graph/gat.yaml index a3af71b68..642e2bca2 100755 --- a/configs/model/graph/gat.yaml +++ b/configs/model/graph/gat.yaml @@ -6,7 +6,7 @@ model_domain: graph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0.0 diff --git a/configs/model/graph/gcn.yaml b/configs/model/graph/gcn.yaml index fa32dab6c..d9b71c87e 100755 --- a/configs/model/graph/gcn.yaml +++ b/configs/model/graph/gcn.yaml @@ -19,7 +19,7 @@ backbone: act: relu backbone_wrapper: - _target_: topobench.nn.wrappers.GNNWrapper + _target_: topobench.nn.wrappers.GNNWrapper _partial_: true wrapper_name: GNNWrapper out_channels: ${model.feature_encoder.out_channels} diff --git a/configs/model/graph/gcn_dgm.yaml b/configs/model/graph/gcn_dgm.yaml index d90a955dc..90e352eb0 100755 --- a/configs/model/graph/gcn_dgm.yaml +++ b/configs/model/graph/gcn_dgm.yaml @@ -22,7 +22,7 @@ backbone: act: relu backbone_wrapper: - _target_: topobench.nn.wrappers.GNNWrapper + _target_: topobench.nn.wrappers.GNNWrapper _partial_: true wrapper_name: GNNWrapper out_channels: ${model.feature_encoder.out_channels} diff --git a/configs/model/graph/gin.yaml b/configs/model/graph/gin.yaml index fce972cf3..ad64587cc 100755 --- a/configs/model/graph/gin.yaml +++ b/configs/model/graph/gin.yaml @@ -6,7 +6,7 @@ model_domain: graph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0.0 diff --git a/configs/model/graph/gps.yaml b/configs/model/graph/gps.yaml index 15c5d5572..81cc1bf1f 100644 --- a/configs/model/graph/gps.yaml +++ b/configs/model/graph/gps.yaml @@ -1,12 +1,12 @@ _target_: topobench.model.TBModel -model_name: GPS +model_name: GPS model_domain: graph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 64 proj_dropout: 0.2 @@ -18,7 +18,7 @@ backbone: dropout: 0.4 # SWEEP: [0.0, 0.25, 0.5] heads: 4 # SWEEP: [4,8] attn_type: multihead # SWEEP: [multihead, performer] - local_conv_type: gin # no sweep. + local_conv_type: gin # no sweep. backbone_wrapper: _target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name} @@ -30,7 +30,7 @@ backbone_wrapper: readout: _target_: topobench.nn.readouts.${model.readout.readout_name} - readout_name: MLPReadout + readout_name: MLPReadout num_cell_dimensions: ${infer_num_cell_dimensions:${oc.select:model.feature_encoder.selected_dimensions,null},${model.feature_encoder.in_channels}} # The highest order of cell dimensions to consider in_channels: ${model.feature_encoder.out_channels} hidden_layers: [16] @@ -44,4 +44,4 @@ readout: final_act: null # compile model for faster training with pytorch 2.0 -compile: false \ No newline at end of file +compile: false diff --git a/configs/model/graph/graph_mlp.yaml b/configs/model/graph/graph_mlp.yaml index a9cf4951e..f2a643f26 100755 --- a/configs/model/graph/graph_mlp.yaml +++ b/configs/model/graph/graph_mlp.yaml @@ -6,7 +6,7 @@ model_domain: graph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0.0 diff --git a/configs/model/graph/nsd.yaml b/configs/model/graph/nsd.yaml index a3157cb90..a3254bd56 100644 --- a/configs/model/graph/nsd.yaml +++ b/configs/model/graph/nsd.yaml @@ -6,7 +6,7 @@ model_domain: graph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 64 proj_dropout: 0.0 @@ -30,7 +30,7 @@ backbone_wrapper: readout: _target_: topobench.nn.readouts.${model.readout.readout_name} - readout_name: MLPReadout + readout_name: MLPReadout num_cell_dimensions: ${infer_num_cell_dimensions:${oc.select:model.feature_encoder.selected_dimensions,null},${model.feature_encoder.in_channels}} # The highest order of cell dimensions to consider in_channels: ${model.feature_encoder.out_channels} hidden_layers: [16] @@ -44,4 +44,4 @@ readout: final_act: null # compile model for faster training with pytorch 2.0 -compile: false \ No newline at end of file +compile: false diff --git a/configs/model/hypergraph/allsettransformer.yaml b/configs/model/hypergraph/allsettransformer.yaml index c59ccf7fe..52b9072f3 100755 --- a/configs/model/hypergraph/allsettransformer.yaml +++ b/configs/model/hypergraph/allsettransformer.yaml @@ -37,4 +37,4 @@ readout: pooling_type: sum # compile model for faster training with pytorch 2.0 -compile: false \ No newline at end of file +compile: false diff --git a/configs/model/hypergraph/edgnn.yaml b/configs/model/hypergraph/edgnn.yaml index f3a3355aa..6144f9143 100755 --- a/configs/model/hypergraph/edgnn.yaml +++ b/configs/model/hypergraph/edgnn.yaml @@ -6,13 +6,13 @@ model_domain: hypergraph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 64 proj_dropout: 0.0 backbone: _target_: topobench.nn.backbones.hypergraph.edgnn.EDGNN - num_features: ${model.feature_encoder.out_channels} + num_features: ${model.feature_encoder.out_channels} input_dropout: 0. dropout: 0.5 activation: relu diff --git a/configs/model/hypergraph/unignn2.yaml b/configs/model/hypergraph/unignn2.yaml index 3b7ebb85a..5beb1d622 100755 --- a/configs/model/hypergraph/unignn2.yaml +++ b/configs/model/hypergraph/unignn2.yaml @@ -6,13 +6,13 @@ model_domain: hypergraph feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 128 proj_dropout: 0.0 backbone: _target_: topomodelx.nn.hypergraph.unigcnii.UniGCNII - in_channels: ${model.feature_encoder.out_channels} + in_channels: ${model.feature_encoder.out_channels} hidden_channels: ${model.feature_encoder.out_channels} n_layers: 4 alpha: 0.5 diff --git a/configs/model/pointcloud/deepset.yaml b/configs/model/pointcloud/deepset.yaml index 1ff01ce94..7e59d169f 100644 --- a/configs/model/pointcloud/deepset.yaml +++ b/configs/model/pointcloud/deepset.yaml @@ -16,7 +16,7 @@ backbone: hidden_channels: ${model.feature_encoder.out_channels} backbone_wrapper: - _target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name} + _target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name} _partial_: true wrapper_name: PointcloudWrapper out_channels: ${model.feature_encoder.out_channels} @@ -24,7 +24,7 @@ backbone_wrapper: readout: _target_: topobench.nn.readouts.${model.readout.readout_name} - readout_name: MLPReadout + readout_name: MLPReadout num_cell_dimensions: ${infer_num_cell_dimensions:${oc.select:model.feature_encoder.selected_dimensions,null},${model.feature_encoder.in_channels}} # The highest order of cell dimensions to consider in_channels: ${model.feature_encoder.out_channels} hidden_layers: [64,32] @@ -38,4 +38,4 @@ readout: final_act: null # compile model for faster training with pytorch 2.0 -compile: false \ No newline at end of file +compile: false diff --git a/configs/model/simplicial/san.yaml b/configs/model/simplicial/san.yaml index 6080cf0d7..4ff1c9b7e 100755 --- a/configs/model/simplicial/san.yaml +++ b/configs/model/simplicial/san.yaml @@ -6,7 +6,7 @@ model_domain: simplicial feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 64 proj_dropout: 0.0 selected_dimensions: @@ -37,6 +37,6 @@ readout: out_channels: ${dataset.parameters.num_classes} task_level: ${dataset.parameters.task_level} pooling_type: sum - + # compile model for faster training with pytorch 2.0 compile: false diff --git a/configs/model/simplicial/sccnn.yaml b/configs/model/simplicial/sccnn.yaml index 572a41dad..d507e3e45 100755 --- a/configs/model/simplicial/sccnn.yaml +++ b/configs/model/simplicial/sccnn.yaml @@ -1,6 +1,6 @@ _target_: topobench.model.TBModel -model_name: sccnn +model_name: sccnn model_domain: simplicial feature_encoder: diff --git a/configs/model/simplicial/sccnn_custom.yaml b/configs/model/simplicial/sccnn_custom.yaml index 697dd7c11..1c1dc91d9 100755 --- a/configs/model/simplicial/sccnn_custom.yaml +++ b/configs/model/simplicial/sccnn_custom.yaml @@ -24,7 +24,7 @@ backbone: - ${model.backbone.in_channels_all[0]} - ${model.backbone.in_channels_all[1]} - ${model.backbone.in_channels_all[2]} - conv_order: 1 # Increasing order make the training unstable + conv_order: 1 # Increasing order make the training unstable sc_order: 3 aggr_norm: False update_func: "sigmoid" diff --git a/configs/model/simplicial/scn.yaml b/configs/model/simplicial/scn.yaml index 4fb3a05f0..caeaa24f6 100755 --- a/configs/model/simplicial/scn.yaml +++ b/configs/model/simplicial/scn.yaml @@ -1,6 +1,6 @@ _target_: topobench.model.TBModel -model_name: scn +model_name: scn model_domain: simplicial feature_encoder: diff --git a/configs/model/simplicial/topotune.yaml b/configs/model/simplicial/topotune.yaml index b5a8c63d6..208328640 100755 --- a/configs/model/simplicial/topotune.yaml +++ b/configs/model/simplicial/topotune.yaml @@ -7,7 +7,7 @@ tune_gnn: GIN feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/model/simplicial/topotune_onehasse.yaml b/configs/model/simplicial/topotune_onehasse.yaml index b8e99f71a..d7504109c 100644 --- a/configs/model/simplicial/topotune_onehasse.yaml +++ b/configs/model/simplicial/topotune_onehasse.yaml @@ -7,7 +7,7 @@ tune_gnn: GCN feature_encoder: _target_: topobench.nn.encoders.${model.feature_encoder.encoder_name} encoder_name: AllCellFeatureEncoder - in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} + in_channels: ${infer_in_channels:${dataset},${oc.select:transforms,null}} out_channels: 32 proj_dropout: 0. selected_dimensions: diff --git a/configs/paths/test.yaml b/configs/paths/test.yaml index 3d395cc30..050c339ff 100644 --- a/configs/paths/test.yaml +++ b/configs/paths/test.yaml @@ -15,4 +15,4 @@ log_dir: ${paths.root_dir}/logs output_dir: ${paths.root_dir}/outputs # path to working directory -work_dir: ${hydra.runtime.cwd} \ No newline at end of file +work_dir: ${hydra.runtime.cwd} diff --git a/configs/run.yaml b/configs/run.yaml index ec9083ccd..94bba373f 100755 --- a/configs/run.yaml +++ b/configs/run.yaml @@ -56,4 +56,4 @@ ckpt_path: null seed: 42 # make cudnn backend fully reproducible -deterministic: False \ No newline at end of file +deterministic: False diff --git a/configs/transforms/custom_example.yaml b/configs/transforms/custom_example.yaml index 1848586e8..e29f9736d 100644 --- a/configs/transforms/custom_example.yaml +++ b/configs/transforms/custom_example.yaml @@ -2,4 +2,4 @@ defaults: - data_manipulations@data_transform_1: identity - data_manipulations@data_transform_2: node_degrees - data_manipulations@data_transform_3: one_hot_node_degree_features - - liftings/graph2cell@graph2cell_lifting: cycle \ No newline at end of file + - liftings/graph2cell@graph2cell_lifting: cycle diff --git a/configs/transforms/data_manipulations/combined_encodings.yaml b/configs/transforms/data_manipulations/combined_encodings.yaml index 8342b7dfc..0d5b07302 100644 --- a/configs/transforms/data_manipulations/combined_encodings.yaml +++ b/configs/transforms/data_manipulations/combined_encodings.yaml @@ -7,6 +7,6 @@ parameters: include_eigenvalues: false include_first: false concat_to_x: true - RWSE: + RWSE: max_pe_dim: 10 - concat_to_x: true \ No newline at end of file + concat_to_x: true diff --git a/configs/transforms/data_manipulations/data_fields_to_dense.yaml b/configs/transforms/data_manipulations/data_fields_to_dense.yaml index 52505086b..a1d5729bd 100755 --- a/configs/transforms/data_manipulations/data_fields_to_dense.yaml +++ b/configs/transforms/data_manipulations/data_fields_to_dense.yaml @@ -1,2 +1,2 @@ transform_name: "DataFieldsToDense" -transform_type: "data manipulation" \ No newline at end of file +transform_type: "data manipulation" diff --git a/configs/transforms/data_manipulations/equal_gaus_features.yaml b/configs/transforms/data_manipulations/equal_gaus_features.yaml index 631589e63..94d175c35 100755 --- a/configs/transforms/data_manipulations/equal_gaus_features.yaml +++ b/configs/transforms/data_manipulations/equal_gaus_features.yaml @@ -4,4 +4,3 @@ transform_type: "data manipulation" mean: 0 std: 0.1 num_features: ${dataset.parameters.num_features} - diff --git a/configs/transforms/data_manipulations/identity.yaml b/configs/transforms/data_manipulations/identity.yaml index 8e355278d..2030016a7 100755 --- a/configs/transforms/data_manipulations/identity.yaml +++ b/configs/transforms/data_manipulations/identity.yaml @@ -1,2 +1,2 @@ transform_name: "Identity" -transform_type: null \ No newline at end of file +transform_type: null diff --git a/configs/transforms/data_manipulations/infere_radius_connectivity.yaml b/configs/transforms/data_manipulations/infere_radius_connectivity.yaml index e480de230..fed3bb4eb 100755 --- a/configs/transforms/data_manipulations/infere_radius_connectivity.yaml +++ b/configs/transforms/data_manipulations/infere_radius_connectivity.yaml @@ -2,6 +2,5 @@ transform_name: "InfereRadiusConnectivity" transform_type: "data manipulation" args: r: 1. # Number of nearest neighbors to consider - max_num_neighbors: 10 # The maximum number of neighbors to return for each element in y. + max_num_neighbors: 10 # The maximum number of neighbors to return for each element in y. # Add additional parameters below if needed (see https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.nn.pool.radius_graph.html#torch_geometric.nn.pool.radius_graph) - diff --git a/configs/transforms/data_manipulations/keep_connected_component.yaml b/configs/transforms/data_manipulations/keep_connected_component.yaml index cb50242c2..1f86d8774 100644 --- a/configs/transforms/data_manipulations/keep_connected_component.yaml +++ b/configs/transforms/data_manipulations/keep_connected_component.yaml @@ -1,3 +1,3 @@ transform_name: "KeepOnlyConnectedComponent" transform_type: "data manipulation" -num_components: 1 \ No newline at end of file +num_components: 1 diff --git a/configs/transforms/data_manipulations/keep_selected_fields.yaml b/configs/transforms/data_manipulations/keep_selected_fields.yaml index 86d878e01..6ef16867f 100644 --- a/configs/transforms/data_manipulations/keep_selected_fields.yaml +++ b/configs/transforms/data_manipulations/keep_selected_fields.yaml @@ -5,4 +5,4 @@ base_fields: ["x", "y", "val_mask", "test_mask", "train_mask", "edge_index", "sh # Fields that optional for the pipeline preserved_fields: ["0_cell_curvature", "1_cell_curvature", "2_cell_curvature"] -#["x_0", "x_1", "x_2", "x_3","incidence_1", "incidence_2", "incidence_3", "hodge_laplacian_0", "hodge_laplacian_1", "hodge_laplacian_2"] \ No newline at end of file +#["x_0", "x_1", "x_2", "x_3","incidence_1", "incidence_2", "incidence_3", "hodge_laplacian_0", "hodge_laplacian_1", "hodge_laplacian_2"] diff --git a/configs/transforms/data_manipulations/laplacian_encodings.yaml b/configs/transforms/data_manipulations/laplacian_encodings.yaml index ad2c3bad5..19c515900 100644 --- a/configs/transforms/data_manipulations/laplacian_encodings.yaml +++ b/configs/transforms/data_manipulations/laplacian_encodings.yaml @@ -3,4 +3,4 @@ transform_type: "data manipulation" max_pe_dim: 10 include_eigenvalues: false include_first: false -concat_to_x: true \ No newline at end of file +concat_to_x: true diff --git a/configs/transforms/data_manipulations/mp_homophily.yaml b/configs/transforms/data_manipulations/mp_homophily.yaml index b387e166b..e8db03e56 100755 --- a/configs/transforms/data_manipulations/mp_homophily.yaml +++ b/configs/transforms/data_manipulations/mp_homophily.yaml @@ -1,4 +1,4 @@ transform_name: "MessagePassingHomophily" transform_type: "data manipulation" num_steps: 10 -incidence_field: "incidence_hyperedges" # incidence_hyperedges for hypergraphs, incidence_1,..,incidence_k for other domains \ No newline at end of file +incidence_field: "incidence_hyperedges" # incidence_hyperedges for hypergraphs, incidence_1,..,incidence_k for other domains diff --git a/configs/transforms/data_manipulations/node_degrees.yaml b/configs/transforms/data_manipulations/node_degrees.yaml index 02fc9ae1a..6c4fc2d10 100755 --- a/configs/transforms/data_manipulations/node_degrees.yaml +++ b/configs/transforms/data_manipulations/node_degrees.yaml @@ -1,4 +1,3 @@ transform_name: "NodeDegrees" transform_type: "data manipulation" selected_fields: ["edge_index"] # "incidence" - diff --git a/configs/transforms/data_manipulations/node_feat_to_float.yaml b/configs/transforms/data_manipulations/node_feat_to_float.yaml index a801575e0..65c44b21b 100755 --- a/configs/transforms/data_manipulations/node_feat_to_float.yaml +++ b/configs/transforms/data_manipulations/node_feat_to_float.yaml @@ -1,3 +1,2 @@ transform_name: "NodeFeaturesToFloat" transform_type: "data manipulation" - diff --git a/configs/transforms/data_manipulations/one_hot_node_degree_features.yaml b/configs/transforms/data_manipulations/one_hot_node_degree_features.yaml index 6ff260f62..5206a0497 100755 --- a/configs/transforms/data_manipulations/one_hot_node_degree_features.yaml +++ b/configs/transforms/data_manipulations/one_hot_node_degree_features.yaml @@ -3,4 +3,3 @@ transform_type: "data manipulation" degrees_fields: ${oc.select:dataset.parameters.degrees_fields,"node_degrees"} features_fields: "x" max_degree: ${dataset.parameters.max_node_degree} - diff --git a/configs/transforms/data_manipulations/random_walk_encodings.yaml b/configs/transforms/data_manipulations/random_walk_encodings.yaml index f20da197c..7796c9258 100644 --- a/configs/transforms/data_manipulations/random_walk_encodings.yaml +++ b/configs/transforms/data_manipulations/random_walk_encodings.yaml @@ -1,4 +1,4 @@ transform_name: "RWSE" transform_type: "data manipulation" max_pe_dim: 10 -concat_to_x: true \ No newline at end of file +concat_to_x: true diff --git a/configs/transforms/data_manipulations/redefine_simplicial_neighbourhoods.yaml b/configs/transforms/data_manipulations/redefine_simplicial_neighbourhoods.yaml index d201eef37..38c31d827 100644 --- a/configs/transforms/data_manipulations/redefine_simplicial_neighbourhoods.yaml +++ b/configs/transforms/data_manipulations/redefine_simplicial_neighbourhoods.yaml @@ -1,2 +1,2 @@ transform_name: "RedefineSimplicialNeighbourhoods" -transform_type: null \ No newline at end of file +transform_type: null diff --git a/configs/transforms/data_manipulations/remove_extra_feature.yaml b/configs/transforms/data_manipulations/remove_extra_feature.yaml index 31f670960..9fa5c7c33 100755 --- a/configs/transforms/data_manipulations/remove_extra_feature.yaml +++ b/configs/transforms/data_manipulations/remove_extra_feature.yaml @@ -3,4 +3,3 @@ transform_type: "data manipulation" remove_first_n_features: 1 search_field: "x_" expected_number_of_features: ${dataset.parameters.num_features} - diff --git a/configs/transforms/data_manipulations/simplicial_curvature.yaml b/configs/transforms/data_manipulations/simplicial_curvature.yaml index 238eeef12..271b5c723 100755 --- a/configs/transforms/data_manipulations/simplicial_curvature.yaml +++ b/configs/transforms/data_manipulations/simplicial_curvature.yaml @@ -1,4 +1,2 @@ transform_name: "CalculateSimplicialCurvature" transform_type: "data manipulation" - - diff --git a/configs/transforms/dataset_defaults/IMDB-BINARY.yaml b/configs/transforms/dataset_defaults/IMDB-BINARY.yaml index f13ee2140..f4c4bd350 100755 --- a/configs/transforms/dataset_defaults/IMDB-BINARY.yaml +++ b/configs/transforms/dataset_defaults/IMDB-BINARY.yaml @@ -1,4 +1,4 @@ defaults: - data_manipulations: node_degrees - data_manipulations@one_hot_node_degree_features: one_hot_node_degree_features - - liftings@_here_: ${get_required_lifting:graph,${model}} \ No newline at end of file + - liftings@_here_: ${get_required_lifting:graph,${model}} diff --git a/configs/transforms/dataset_defaults/IMDB-MULTI.yaml b/configs/transforms/dataset_defaults/IMDB-MULTI.yaml index f13ee2140..f4c4bd350 100755 --- a/configs/transforms/dataset_defaults/IMDB-MULTI.yaml +++ b/configs/transforms/dataset_defaults/IMDB-MULTI.yaml @@ -1,4 +1,4 @@ defaults: - data_manipulations: node_degrees - data_manipulations@one_hot_node_degree_features: one_hot_node_degree_features - - liftings@_here_: ${get_required_lifting:graph,${model}} \ No newline at end of file + - liftings@_here_: ${get_required_lifting:graph,${model}} diff --git a/configs/transforms/dataset_defaults/PROTEINS.yaml b/configs/transforms/dataset_defaults/PROTEINS.yaml index c7070b48e..3278b191c 100644 --- a/configs/transforms/dataset_defaults/PROTEINS.yaml +++ b/configs/transforms/dataset_defaults/PROTEINS.yaml @@ -1,4 +1,4 @@ # PROTEINS dataset needs identity transform to avoid adding random float feature to feature matrix defaults: - data_manipulations: identity - - liftings@_here_: ${get_required_lifting:graph,${model}} \ No newline at end of file + - liftings@_here_: ${get_required_lifting:graph,${model}} diff --git a/configs/transforms/feature_liftings/base_lifting.yaml b/configs/transforms/feature_liftings/base_lifting.yaml index d45635a92..6f863b222 100755 --- a/configs/transforms/feature_liftings/base_lifting.yaml +++ b/configs/transforms/feature_liftings/base_lifting.yaml @@ -1,2 +1,2 @@ transform_name: "ProjectionSum" -transform_type: "feature_lifting" \ No newline at end of file +transform_type: "feature_lifting" diff --git a/configs/transforms/feature_liftings/concatenate.yaml b/configs/transforms/feature_liftings/concatenate.yaml index 57935297c..f72678fce 100755 --- a/configs/transforms/feature_liftings/concatenate.yaml +++ b/configs/transforms/feature_liftings/concatenate.yaml @@ -1,2 +1,2 @@ transform_name: "ConcatentionLifting" -transform_type: null \ No newline at end of file +transform_type: null diff --git a/configs/transforms/liftings/cell2hypergraph_default.yaml b/configs/transforms/liftings/cell2hypergraph_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/cell2hypergraph_default.yaml +++ b/configs/transforms/liftings/cell2hypergraph_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/cell2simplicial_default.yaml b/configs/transforms/liftings/cell2simplicial_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/cell2simplicial_default.yaml +++ b/configs/transforms/liftings/cell2simplicial_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/graph2cell/cycle.yaml b/configs/transforms/liftings/graph2cell/cycle.yaml index 95b4aed63..9308150cc 100644 --- a/configs/transforms/liftings/graph2cell/cycle.yaml +++ b/configs/transforms/liftings/graph2cell/cycle.yaml @@ -3,6 +3,6 @@ transform_name: "CellCycleLifting" complex_dim: ${oc.select:dataset.parameters.max_dim_if_lifted,3} max_cell_length: 18 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: ${oc.select:dataset.parameters.preserve_edge_attr_if_lifted,False} neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2cell/discrete_configuration_complex.yaml b/configs/transforms/liftings/graph2cell/discrete_configuration_complex.yaml index df07ad64c..348f0ee53 100644 --- a/configs/transforms/liftings/graph2cell/discrete_configuration_complex.yaml +++ b/configs/transforms/liftings/graph2cell/discrete_configuration_complex.yaml @@ -4,7 +4,7 @@ k: 2 feature_aggregation: "sum" feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: 3 preserve_edge_attr: False neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2combinatorial/graph_triangle_induced_cc.yaml b/configs/transforms/liftings/graph2combinatorial/graph_triangle_induced_cc.yaml index d4afb2437..85e3c0191 100644 --- a/configs/transforms/liftings/graph2combinatorial/graph_triangle_induced_cc.yaml +++ b/configs/transforms/liftings/graph2combinatorial/graph_triangle_induced_cc.yaml @@ -4,4 +4,4 @@ transform_name: "GraphTriangleInducedCC" complex_dim: 3 preserve_edge_attr: False feature_lifting: ProjectionSum -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2combinatorial/ring_close_atoms.yaml b/configs/transforms/liftings/graph2combinatorial/ring_close_atoms.yaml index 538e61eb7..6565a1b83 100644 --- a/configs/transforms/liftings/graph2combinatorial/ring_close_atoms.yaml +++ b/configs/transforms/liftings/graph2combinatorial/ring_close_atoms.yaml @@ -4,4 +4,4 @@ max_cell_length: null preserve_edge_attr: True feature_lifting: ProjectionSum -threshold_distance: 1.5 \ No newline at end of file +threshold_distance: 1.5 diff --git a/configs/transforms/liftings/graph2combinatorial/simplicial_paths.yaml b/configs/transforms/liftings/graph2combinatorial/simplicial_paths.yaml index 792f712d7..8a92005f4 100644 --- a/configs/transforms/liftings/graph2combinatorial/simplicial_paths.yaml +++ b/configs/transforms/liftings/graph2combinatorial/simplicial_paths.yaml @@ -9,6 +9,6 @@ j: 2 complex_dim: 2 chunk_size: 1024 threshold: 1 -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2combinatorial_default.yaml b/configs/transforms/liftings/graph2combinatorial_default.yaml index f835e7a3e..09c622585 100644 --- a/configs/transforms/liftings/graph2combinatorial_default.yaml +++ b/configs/transforms/liftings/graph2combinatorial_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings/graph2combinatorial@graph2combinatorial_lifting: graph_triangle_induced_cc \ No newline at end of file + - /transforms/liftings/graph2combinatorial@graph2combinatorial_lifting: graph_triangle_induced_cc diff --git a/configs/transforms/liftings/graph2hypergraph/expander_graph.yaml b/configs/transforms/liftings/graph2hypergraph/expander_graph.yaml index 42401d732..0fb495154 100755 --- a/configs/transforms/liftings/graph2hypergraph/expander_graph.yaml +++ b/configs/transforms/liftings/graph2hypergraph/expander_graph.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "ExpanderGraphLifting" node_degree: 2 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/forman_ricci_curvature.yaml b/configs/transforms/liftings/graph2hypergraph/forman_ricci_curvature.yaml index 184d5be5d..13d76cda2 100755 --- a/configs/transforms/liftings/graph2hypergraph/forman_ricci_curvature.yaml +++ b/configs/transforms/liftings/graph2hypergraph/forman_ricci_curvature.yaml @@ -5,7 +5,7 @@ threshold_type: 'quantile' threshold_direction: 'above' threshold: 0.1 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/kernel.yaml b/configs/transforms/liftings/graph2hypergraph/kernel.yaml index a2306dd2e..eb58020b1 100755 --- a/configs/transforms/liftings/graph2hypergraph/kernel.yaml +++ b/configs/transforms/liftings/graph2hypergraph/kernel.yaml @@ -4,7 +4,7 @@ kernel_graph: heat kernel_feat: identity t: 1 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/khop.yaml b/configs/transforms/liftings/graph2hypergraph/khop.yaml index 01418ebcb..df4af666f 100755 --- a/configs/transforms/liftings/graph2hypergraph/khop.yaml +++ b/configs/transforms/liftings/graph2hypergraph/khop.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "HypergraphKHopLifting" k_value: 1 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/knn.yaml b/configs/transforms/liftings/graph2hypergraph/knn.yaml index 021be023e..46d95e514 100755 --- a/configs/transforms/liftings/graph2hypergraph/knn.yaml +++ b/configs/transforms/liftings/graph2hypergraph/knn.yaml @@ -3,7 +3,7 @@ transform_name: "HypergraphKNNLifting" k_value: 3 loop: True feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/mapper.yaml b/configs/transforms/liftings/graph2hypergraph/mapper.yaml index 73185fc21..fd95f5a24 100644 --- a/configs/transforms/liftings/graph2hypergraph/mapper.yaml +++ b/configs/transforms/liftings/graph2hypergraph/mapper.yaml @@ -4,7 +4,7 @@ filter_attr: "laplacian" resolution: 10 gain: 0.3 filter_func: -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph/modularity_maximization.yaml b/configs/transforms/liftings/graph2hypergraph/modularity_maximization.yaml index 0c120af8b..b35e3cfbf 100644 --- a/configs/transforms/liftings/graph2hypergraph/modularity_maximization.yaml +++ b/configs/transforms/liftings/graph2hypergraph/modularity_maximization.yaml @@ -3,7 +3,7 @@ transform_name: "ModularityMaximizationLifting" num_communities: 2 k_neighbors: 3 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable preserve_edge_attr: False complex_dim: 1 -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2hypergraph_default.yaml b/configs/transforms/liftings/graph2hypergraph_default.yaml index d1ad95d24..64bc9d330 100644 --- a/configs/transforms/liftings/graph2hypergraph_default.yaml +++ b/configs/transforms/liftings/graph2hypergraph_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings/graph2hypergraph@graph2hypergraph_lifting: khop #knn \ No newline at end of file + - /transforms/liftings/graph2hypergraph@graph2hypergraph_lifting: khop #knn diff --git a/configs/transforms/liftings/graph2simplicial/dnd.yaml b/configs/transforms/liftings/graph2simplicial/dnd.yaml index 9ba75a223..130d11641 100755 --- a/configs/transforms/liftings/graph2simplicial/dnd.yaml +++ b/configs/transforms/liftings/graph2simplicial/dnd.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "SimplicialDnDLifting" signed: True feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: 3 preserve_edge_attr: False neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/eccentricity.yaml b/configs/transforms/liftings/graph2simplicial/eccentricity.yaml index a119d7ba9..52caf9cc1 100644 --- a/configs/transforms/liftings/graph2simplicial/eccentricity.yaml +++ b/configs/transforms/liftings/graph2simplicial/eccentricity.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "SimplicialEccentricityLifting" signed: True feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: 3 preserve_edge_attr: False -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/khop.yaml b/configs/transforms/liftings/graph2simplicial/khop.yaml index 6f1608faf..0a6dd3a07 100755 --- a/configs/transforms/liftings/graph2simplicial/khop.yaml +++ b/configs/transforms/liftings/graph2simplicial/khop.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "SimplicialKHopLifting" max_k_simplices: 25 feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: ${oc.select:dataset.parameters.max_dim_if_lifted,3} preserve_edge_attr: ${oc.select:dataset.parameters.preserve_edge_attr_if_lifted,False} -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/latent_clique.yaml b/configs/transforms/liftings/graph2simplicial/latent_clique.yaml index f391f7388..b65ba39e7 100644 --- a/configs/transforms/liftings/graph2simplicial/latent_clique.yaml +++ b/configs/transforms/liftings/graph2simplicial/latent_clique.yaml @@ -5,6 +5,6 @@ edge_prob_var: 0.0001 # Uncertainty of the prior distribution of pie ~ Beta(a, b signed: True preserve_edge_attr: False # This lifting do not explicitly preserves the graph structure, hence it is necessary to set preserve_edge_attr to False or modify the lifting source code. feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: ${oc.select:dataset.parameters.max_dim_if_lifted,3} -neighborhoods: ${oc.select:model.backbone.neighborhoods,null} \ No newline at end of file +neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/line.yaml b/configs/transforms/liftings/graph2simplicial/line.yaml index afceebd9a..ea3d9a2f8 100644 --- a/configs/transforms/liftings/graph2simplicial/line.yaml +++ b/configs/transforms/liftings/graph2simplicial/line.yaml @@ -1,7 +1,7 @@ transform_type: "lifting" transform_name: "SimplicialLineLifting" feature_lifting: ProjectionSum -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: ${oc.select:dataset.parameters.max_dim_if_lifted,3} preserve_edge_attr: False neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/neighborhood_complex.yaml b/configs/transforms/liftings/graph2simplicial/neighborhood_complex.yaml index f3a1f1190..9d8f0ad43 100755 --- a/configs/transforms/liftings/graph2simplicial/neighborhood_complex.yaml +++ b/configs/transforms/liftings/graph2simplicial/neighborhood_complex.yaml @@ -2,7 +2,7 @@ transform_type: 'lifting' transform_name: "NeighborhoodComplexLifting" signed: True feature_lifting: ProjectionSum #ElementwiseMean -# Required for all liftings to make the pipeline executable +# Required for all liftings to make the pipeline executable complex_dim: 3 preserve_edge_attr: False neighborhoods: ${oc.select:model.backbone.neighborhoods,null} diff --git a/configs/transforms/liftings/graph2simplicial/vietoris_rips.yaml b/configs/transforms/liftings/graph2simplicial/vietoris_rips.yaml index d35160638..5569297b8 100644 --- a/configs/transforms/liftings/graph2simplicial/vietoris_rips.yaml +++ b/configs/transforms/liftings/graph2simplicial/vietoris_rips.yaml @@ -4,4 +4,4 @@ complex_dim: 3 signed: True distance_threshold: 2.0 feature_lifting: ProjectionSum -preserve_edge_attr: False # This lifting do not explicitly preserves the graph structure, hence it is necessary to set preserve_edge_attr to False or modify the lifting source code. \ No newline at end of file +preserve_edge_attr: False # This lifting do not explicitly preserves the graph structure, hence it is necessary to set preserve_edge_attr to False or modify the lifting source code. diff --git a/configs/transforms/liftings/graph2simplicial_default.yaml b/configs/transforms/liftings/graph2simplicial_default.yaml index 95d9b4f6d..16337be4d 100644 --- a/configs/transforms/liftings/graph2simplicial_default.yaml +++ b/configs/transforms/liftings/graph2simplicial_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings/graph2simplicial@graph2simplicial_lifting: clique #khop \ No newline at end of file + - /transforms/liftings/graph2simplicial@graph2simplicial_lifting: clique #khop diff --git a/configs/transforms/liftings/hypergraph2cell_default.yaml b/configs/transforms/liftings/hypergraph2cell_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/hypergraph2cell_default.yaml +++ b/configs/transforms/liftings/hypergraph2cell_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/hypergraph2combinatorial/universal_strict.yaml b/configs/transforms/liftings/hypergraph2combinatorial/universal_strict.yaml index dfa885f77..dc725284d 100644 --- a/configs/transforms/liftings/hypergraph2combinatorial/universal_strict.yaml +++ b/configs/transforms/liftings/hypergraph2combinatorial/universal_strict.yaml @@ -2,4 +2,3 @@ transform_type: "lifting" transform_name: "UniversalStrictLifting" neighborhoods: ${oc.select:model.backbone.neighborhoods,null} complex_dim: 5 - diff --git a/configs/transforms/liftings/hypergraph2graph_default.yaml b/configs/transforms/liftings/hypergraph2graph_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/hypergraph2graph_default.yaml +++ b/configs/transforms/liftings/hypergraph2graph_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/hypergraph2simplicial_default.yaml b/configs/transforms/liftings/hypergraph2simplicial_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/hypergraph2simplicial_default.yaml +++ b/configs/transforms/liftings/hypergraph2simplicial_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/identity.yaml b/configs/transforms/liftings/identity.yaml index 3d2e4dc91..cc9d8548d 100644 --- a/configs/transforms/liftings/identity.yaml +++ b/configs/transforms/liftings/identity.yaml @@ -1,4 +1,4 @@ # @package _global_ defaults: - - transforms@_here_: null \ No newline at end of file + - transforms@_here_: null diff --git a/configs/transforms/liftings/no_lifting.yaml b/configs/transforms/liftings/no_lifting.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/no_lifting.yaml +++ b/configs/transforms/liftings/no_lifting.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/pointcloud2graph_default.yaml b/configs/transforms/liftings/pointcloud2graph_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/pointcloud2graph_default.yaml +++ b/configs/transforms/liftings/pointcloud2graph_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/liftings/pointcloud2hypergraph/mogmst.yaml b/configs/transforms/liftings/pointcloud2hypergraph/mogmst.yaml index 7a1d0f1df..d1d9d5ecc 100644 --- a/configs/transforms/liftings/pointcloud2hypergraph/mogmst.yaml +++ b/configs/transforms/liftings/pointcloud2hypergraph/mogmst.yaml @@ -4,4 +4,4 @@ min_components: null max_components: null random_state: null preserve_edge_attr: False -complex_dim: 1 \ No newline at end of file +complex_dim: 1 diff --git a/configs/transforms/liftings/pointcloud2hypergraph/pointnet.yaml b/configs/transforms/liftings/pointcloud2hypergraph/pointnet.yaml index 604deec69..5cb1a97c5 100644 --- a/configs/transforms/liftings/pointcloud2hypergraph/pointnet.yaml +++ b/configs/transforms/liftings/pointcloud2hypergraph/pointnet.yaml @@ -4,4 +4,4 @@ sampling_ratio: 0.25 cluster_radius: 10 feature_lifting: ProjectionSum preserve_edge_attr: False -complex_dim: 1 \ No newline at end of file +complex_dim: 1 diff --git a/configs/transforms/liftings/pointcloud2hypergraph/voronoi.yaml b/configs/transforms/liftings/pointcloud2hypergraph/voronoi.yaml index f2c26f985..62394041c 100644 --- a/configs/transforms/liftings/pointcloud2hypergraph/voronoi.yaml +++ b/configs/transforms/liftings/pointcloud2hypergraph/voronoi.yaml @@ -3,4 +3,4 @@ transform_name: "VoronoiLifting" support_ratio: 0.005 feature_lifting: ProjectionSum preserve_edge_attr: False -complex_dim: 1 \ No newline at end of file +complex_dim: 1 diff --git a/configs/transforms/liftings/simplicial2combinatorial/coface_cc.yaml b/configs/transforms/liftings/simplicial2combinatorial/coface_cc.yaml index 443afcf3a..6e1b7dbe4 100644 --- a/configs/transforms/liftings/simplicial2combinatorial/coface_cc.yaml +++ b/configs/transforms/liftings/simplicial2combinatorial/coface_cc.yaml @@ -1,4 +1,4 @@ transform_type: 'lifting' transform_name: "CofaceCCLifting" feature_lifting: ProjectionSum -keep_features: False \ No newline at end of file +keep_features: False diff --git a/configs/transforms/liftings/simplicial2graph_default.yaml b/configs/transforms/liftings/simplicial2graph_default.yaml index 92ec7dff6..5a1824279 100644 --- a/configs/transforms/liftings/simplicial2graph_default.yaml +++ b/configs/transforms/liftings/simplicial2graph_default.yaml @@ -1,2 +1,2 @@ defaults: - - /transforms/liftings: null \ No newline at end of file + - /transforms/liftings: null diff --git a/configs/transforms/model_defaults/gps.yaml b/configs/transforms/model_defaults/gps.yaml index 0985dd3b0..42aa40696 100644 --- a/configs/transforms/model_defaults/gps.yaml +++ b/configs/transforms/model_defaults/gps.yaml @@ -1,2 +1,2 @@ defaults: - - data_manipulations@CombinedPSEs: combined_encodings \ No newline at end of file + - data_manipulations@CombinedPSEs: combined_encodings diff --git a/configs/transforms/model_defaults/nsd.yaml b/configs/transforms/model_defaults/nsd.yaml index 0985dd3b0..42aa40696 100644 --- a/configs/transforms/model_defaults/nsd.yaml +++ b/configs/transforms/model_defaults/nsd.yaml @@ -1,2 +1,2 @@ defaults: - - data_manipulations@CombinedPSEs: combined_encodings \ No newline at end of file + - data_manipulations@CombinedPSEs: combined_encodings diff --git a/configs/transforms/no_transform.yaml b/configs/transforms/no_transform.yaml index 3d2e4dc91..cc9d8548d 100644 --- a/configs/transforms/no_transform.yaml +++ b/configs/transforms/no_transform.yaml @@ -1,4 +1,4 @@ # @package _global_ defaults: - - transforms@_here_: null \ No newline at end of file + - transforms@_here_: null diff --git a/docs/QUICK_REFERENCE.md b/docs/QUICK_REFERENCE.md index dc7b1685a..bcfab7168 100644 --- a/docs/QUICK_REFERENCE.md +++ b/docs/QUICK_REFERENCE.md @@ -96,27 +96,27 @@ TopoBench uses NumPy-style docstrings: ```python def my_function(param1, param2): """Short description of function. - + Longer description with more details about what the function does and why. - + Parameters ---------- param1 : str Description of param1 param2 : int, optional Description of param2 (default is 42) - + Returns ------- result : bool Description of return value - + Examples -------- >>> my_function("hello", 5) True - + Notes ----- Additional notes about edge cases or behavior. diff --git a/docs/_static/custom.css b/docs/_static/custom.css index 3f134d50d..549bbf3b8 100644 --- a/docs/_static/custom.css +++ b/docs/_static/custom.css @@ -36,8 +36,8 @@ div.section > h4 { } /* Improve class and function signatures */ -dl.py.class, -dl.py.function, +dl.py.class, +dl.py.function, dl.py.method, dl.class.py, dl.function.py, @@ -293,8 +293,8 @@ html[data-theme="light"] a.reference.internal .viewcode-link:hover { } /* Improve class and function signatures in dark mode */ - dl.py.class, - dl.py.function, + dl.py.class, + dl.py.function, dl.py.method, dl.class.py, dl.function.py, diff --git a/docs/api/index.rst b/docs/api/index.rst index bd2da25cc..bb5967566 100644 --- a/docs/api/index.rst +++ b/docs/api/index.rst @@ -168,4 +168,3 @@ Helper functions and utility modules :maxdepth: 2 topobench.utils - diff --git a/docs/build_docs.sh b/docs/build_docs.sh index ffd44ff72..1f82fc020 100755 --- a/docs/build_docs.sh +++ b/docs/build_docs.sh @@ -119,7 +119,7 @@ if [ "$SERVE" = true ]; then elif [ "$OPEN_BROWSER" = true ]; then if [ -f "$INDEX_FILE" ]; then echo -e "${GREEN}✓ Opening documentation in browser...${NC}" - + # Detect OS and open browser if command -v xdg-open > /dev/null; then xdg-open "$INDEX_FILE" > /dev/null 2>&1 & diff --git a/docs/index.rst b/docs/index.rst index f9698d0a3..d448faf1d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,8 +6,8 @@ :class: with-shadow :width: 1000px -`TopoBench` (TB) is a modular Python library designed to standardize benchmarking and accelerate research in Topological Deep Learning (TDL). -In particular, TB allows to train and compare the performances of all sorts of Topological Neural Networks (TNNs) across the different topological domains, +`TopoBench` (TB) is a modular Python library designed to standardize benchmarking and accelerate research in Topological Deep Learning (TDL). +In particular, TB allows training and comparing the performances of all sorts of Topological Neural Networks (TNNs) across the different topological domains, where by *topological domain* we refer to a graph, a simplicial complex, a cellular complex, or a hypergraph. .. figure:: https://github.com/geometric-intelligence/TopoBench/raw/main/resources/workflow.jpg @@ -20,7 +20,7 @@ where by *topological domain* we refer to a graph, a simplicial complex, a cellu ``TopoBench`` (TB) is a modular Python library designed to standardize benchmarking and accelerate research in Topological Deep Learning (TDL). -In particular, TB allows to train and compare the performances of all +In particular, TB allows training and comparing the performances of all sorts of Topological Neural Networks (TNNs) across the different topological domains, where by *topological domain* we refer to a graph, a simplicial complex, a cellular complex, or a hypergraph. For detailed @@ -97,7 +97,7 @@ Next, train the neural networks by running the following command: .. code:: bash - python -m topobench + python -m topobench Customizing Experiment Configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -870,13 +870,13 @@ To learn more about ``TopoBench``, we invite you to read the paper: :: @article{telyatnikov2024topobench, - title={TopoBench: A Framework for Benchmarking Topological Deep Learning}, + title={TopoBench: A Framework for Benchmarking Topological Deep Learning}, author={Lev Telyatnikov and Guillermo Bernardez and Marco Montagna and Pavlo Vasylenko and Ghada Zamzmi and Mustafa Hajij and Michael T Schaub and Nina Miolane and Simone Scardapane and Theodore Papamarkou}, year={2024}, eprint={2406.06642}, archivePrefix={arXiv}, primaryClass={cs.LG}, - url={https://arxiv.org/abs/2406.06642}, + url={https://arxiv.org/abs/2406.06642}, } If you find ``TopoBench`` useful, we would appreciate if you cite us! @@ -993,4 +993,3 @@ domains `__ tdl-challenge/index api/index contributing/index - diff --git a/docs/tdl-challenge/index.rst b/docs/tdl-challenge/index.rst index 53babde82..e29c691fc 100644 --- a/docs/tdl-challenge/index.rst +++ b/docs/tdl-challenge/index.rst @@ -64,7 +64,7 @@ evaluation. To propel the field forward, we focus on the following fronts: - **Deepening:** TDL capabilities by building infrastructure for large-scale datasets and novel benchmark tasks. -We are calling on the community to build a richer, more robust, and scalable data ecosystem for TDL. +We are calling on the community to build a richer, more robust, and scalable data ecosystem for TDL. By participating, you will be paving the way for the next generation of topological models. @@ -506,43 +506,43 @@ Graphs * - Dataset - Reference * - TwiBot-20 - - *A Comprehensive Twitter Bot Detection Benchmark (Large).* + - *A Comprehensive Twitter Bot Detection Benchmark (Large).* * - TwiBot-22 - - *Towards Graph-Based Twitter Bot Detection (Large).* + - *Towards Graph-Based Twitter Bot Detection (Large).* * - MGTAB - - *A Multi-Relational Graph-Based Twitter Account Detection Benchmark.* + - *A Multi-Relational Graph-Based Twitter Account Detection Benchmark.* * - GraphLand (14 datasets) - - *GraphLand: Evaluating Graph Machine Learning Models on Diverse Industrial Data.* + - *GraphLand: Evaluating Graph Machine Learning Models on Diverse Industrial Data.* * - Karate Club (7 datasets) - - *Karate Club: An API Oriented Open-Source Python Framework for Unsupervised…* + - *Karate Club: An API Oriented Open-Source Python Framework for Unsupervised…* * - Coauth-DBLP - - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* + - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* * - Coauth-AMiner - - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* + - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* * - Email-Enron - - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* + - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* * - Email-Eu - - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* + - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* * - Stack-Physics - - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* + - *Classification of Edge-dependent Labels of Nodes in Hypergraphs.* * - Wiki-CS - - *Wiki-CS: A Wikipedia-Based Benchmark for Graph Neural Networks.* + - *Wiki-CS: A Wikipedia-Based Benchmark for Graph Neural Networks.* * - FacebookPagePage - - *Multi-Scale Attributed Node Embedding (MUSAE).* + - *Multi-Scale Attributed Node Embedding (MUSAE).* * - GitHub (MUSAE) - - *Multi-Scale Attributed Node Embedding (MUSAE).* + - *Multi-Scale Attributed Node Embedding (MUSAE).* * - LastFMAsia - - *Multi-Scale Attributed Node Embedding (MUSAE).* + - *Multi-Scale Attributed Node Embedding (MUSAE).* * - Deezer Europe - - *Multi-Scale Attributed Node Embedding (MUSAE).* + - *Multi-Scale Attributed Node Embedding (MUSAE).* * - Twitch - - *Multi-Scale Attributed Node Embedding (MUSAE).* + - *Multi-Scale Attributed Node Embedding (MUSAE).* * - GemsecDeezer - - *GEMSEC: Graph Embedding with Self Clustering.* + - *GEMSEC: Graph Embedding with Self Clustering.* * - Torus dataset - - *Topological Blindspots: Understanding and Extending Topological Deep Learning through the Lens of Expressivity.* + - *Topological Blindspots: Understanding and Extending Topological Deep Learning through the Lens of Expressivity.* * - QM9 - - *MoleculeNet: A Benchmark for Molecular Machine Learning.* + - *MoleculeNet: A Benchmark for Molecular Machine Learning.* Hypergraphs @@ -555,21 +555,21 @@ Hypergraphs * - Dataset - Reference * - CornellTemporalHyperGraphDataset - - *Simplicial Closure and Higher-Order Link Prediction.* + - *Simplicial Closure and Higher-Order Link Prediction.* * - CornellLabelledNodes - *Various references.* * - RHG-3 - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* * - RHG-10 - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* * - IMDB-Dir-Form - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* * - IMDB-Dir-Genre - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* * - Steam-Player - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* * - Twitter-Friend - - *Hypergraph Isomorphism Computation.* + - *Hypergraph Isomorphism Computation.* Organizers and Sponsors @@ -600,4 +600,4 @@ Organizers and Sponsors .. |logo_imos| image:: ../_static/logos/logo_EPFL_IMOS.png :height: 90px :alt: IMOS Lab, EPFL - :target: https://www.epfl.ch/labs/imos/ \ No newline at end of file + :target: https://www.epfl.ch/labs/imos/ diff --git a/docs/watch_and_regenerate.sh b/docs/watch_and_regenerate.sh index 360d7a92d..34f5d7bef 100755 --- a/docs/watch_and_regenerate.sh +++ b/docs/watch_and_regenerate.sh @@ -74,11 +74,11 @@ else while true; do # Calculate hash of all Python files CURRENT_HASH=$(find "$PROJECT_DIR/topobench" -name "*.py" -type f -exec md5sum {} \; 2>/dev/null | sort | md5sum) - + if [ "$LAST_HASH" != "$CURRENT_HASH" ] && [ -n "$LAST_HASH" ]; then regenerate_docs fi - + LAST_HASH=$CURRENT_HASH sleep 30 done diff --git a/format_and_lint.sh b/format_and_lint.sh index 75a89a6e7..e6e0969dc 100755 --- a/format_and_lint.sh +++ b/format_and_lint.sh @@ -1,4 +1,4 @@ #!/bin/sh # Run ruff to check for issues and fix them -ruff check . --fix \ No newline at end of file +ruff check . --fix diff --git a/pyproject.toml b/pyproject.toml index 900d9dd18..8ce0a528a 100755 --- a/pyproject.toml +++ b/pyproject.toml @@ -181,4 +181,4 @@ pythonpath = ["."] [tool.numpydoc_validation] checks = ["all", "GL01", "ES01", "EX01", "SA01"] -exclude = ['\.undocumented_method$', '\.__init__$', '\.__repr__$'] \ No newline at end of file +exclude = ['\.undocumented_method$', '\.__init__$', '\.__repr__$'] diff --git a/scripts/reproduce.sh b/scripts/reproduce.sh index ee81776fa..7f7751593 100644 --- a/scripts/reproduce.sh +++ b/scripts/reproduce.sh @@ -13,10 +13,10 @@ FAILED_LOG_FILE="scripts/failed_runs.log" # Function to run a command and check for failure run_command() { local cmd="$1" - + # Run the command and capture the output and error { eval "$cmd" 2>&1 | tee -a "$LOG_FILE"; } 2>> "$ERROR_LOG_FILE" - + # Check if the command failed if [ ${PIPESTATUS[0]} -ne 0 ]; then echo "Command failed: $cmd" >> "$FAILED_LOG_FILE" @@ -291,4 +291,3 @@ for cmd in "${commands[@]}"; do echo "Running: $cmd" run_command "$cmd" done - diff --git a/scripts/topotune/existing_models/tune_cwn.sh b/scripts/topotune/existing_models/tune_cwn.sh index 0ef84536c..13aab87b6 100644 --- a/scripts/topotune/existing_models/tune_cwn.sh +++ b/scripts/topotune/existing_models/tune_cwn.sh @@ -149,4 +149,4 @@ python -m topobench \ trainer.check_val_every_n_epoch=1 \ callbacks.early_stopping.patience=50 \ trainer.devices=\[4\] \ - --multirun \ No newline at end of file + --multirun diff --git a/scripts/topotune/existing_models/tune_sccn.sh b/scripts/topotune/existing_models/tune_sccn.sh index 13deedc49..7fc250629 100644 --- a/scripts/topotune/existing_models/tune_sccn.sh +++ b/scripts/topotune/existing_models/tune_sccn.sh @@ -176,4 +176,4 @@ python -m topobench \ callbacks.early_stopping.patience=50 \ logger.wandb.project=TopoTune_repSCCNone \ trainer.devices=\[1\] \ - --multirun \ No newline at end of file + --multirun diff --git a/scripts/topotune/search_gccn_cell.sh b/scripts/topotune/search_gccn_cell.sh index 5a54e2a47..700fef220 100644 --- a/scripts/topotune/search_gccn_cell.sh +++ b/scripts/topotune/search_gccn_cell.sh @@ -297,4 +297,4 @@ python -m topobench \ trainer.check_val_every_n_epoch=1 \ callbacks.early_stopping.patience=50 \ tags="[FirstExperiments]" \ - --multirun \ No newline at end of file + --multirun diff --git a/scripts/topotune/search_gccn_simplicial.sh b/scripts/topotune/search_gccn_simplicial.sh index 0bb7d085e..e072a8fe6 100644 --- a/scripts/topotune/search_gccn_simplicial.sh +++ b/scripts/topotune/search_gccn_simplicial.sh @@ -337,4 +337,4 @@ python -m topobench \ trainer.check_val_every_n_epoch=1 \ callbacks.early_stopping.patience=50 \ tags="[FirstExperiments]" \ - --multirun \ No newline at end of file + --multirun diff --git a/test/__init__.py b/test/__init__.py index 982880fc6..851c6d604 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1 +1 @@ -"""Init file for test.""" \ No newline at end of file +"""Init file for test.""" diff --git a/test/_utils/__init__.py b/test/_utils/__init__.py index d03f36f0a..9de230173 100644 --- a/test/_utils/__init__.py +++ b/test/_utils/__init__.py @@ -1 +1 @@ -"""Init file for _utils.""" \ No newline at end of file +"""Init file for _utils.""" diff --git a/test/_utils/flow_mocker.py b/test/_utils/flow_mocker.py index c441eafd4..1ac87a65d 100644 --- a/test/_utils/flow_mocker.py +++ b/test/_utils/flow_mocker.py @@ -5,9 +5,9 @@ class FlowMocker: """Flow mocker. - - Mocker for the flow of the test. It allows to create mock objects and assert them. - + + Mocker for the flow of the test. It allows creating mock objects and asserting them. + Parameters ---------- mocker : pytest_mock.plugin.MockerFixture @@ -26,7 +26,7 @@ def __init__(self, mocker, params, setup=True): def parse_mock(self, params): """Parse mock object from parameters. - + Parameters ---------- params : dict @@ -67,15 +67,15 @@ def setup(self): if mock_alias is not None: if mock_alias in self.mocks: raise KeyError( - f"`{mock_alias}` is already exist in mock dictionary" + f"`{mock_alias}` already exists in mock dictionary" ) self.mocks[mock_alias] = self.mocks[patch_obj] def assert_all(self, tested_obj, params=None): """Assert test. - + Assert that specified in assert_args either `params` or self.params. We can access mock object by its alias {"mock: "mock_alias_1", ...}. - + Parameters ---------- tested_obj : any @@ -105,12 +105,12 @@ def assert_all(self, tested_obj, params=None): def get(self, mock_key): """Get mock object by its key. - + Parameters ---------- mock_key : str Key of the mock object. - + Returns ------- any diff --git a/test/_utils/nn_module_auto_test.py b/test/_utils/nn_module_auto_test.py index 8ee8b3860..2be5c6725 100644 --- a/test/_utils/nn_module_auto_test.py +++ b/test/_utils/nn_module_auto_test.py @@ -5,14 +5,14 @@ class NNModuleAutoTest: r"""Test the neural network module. - + Test the following cases: 1) Assert if the module return at least one tensor. 2) Reproducibility. Assert that the module return the same output when called with the same data Additionally . - 3) Assert returned shape. + 3) Assert returned shape. Important! If module returns multiple tensor. The shapes for assertion must be in list() not (!!!) tuple(). - + Parameters ---------- params : list @@ -29,7 +29,7 @@ def run(self): assert "module" in param and "init" in param and "forward" in param module = self.exec_func(param["module"], param["init"]) cloned_inp = self.clone_input(param["forward"]) - + result, result_2 = self.exec_twice(module, param["forward"], cloned_inp) if type(result) != tuple: @@ -47,7 +47,7 @@ def run(self): def exec_twice(self, module, inp_1, inp_2): """Execute the module twice with different data. - + Parameters ---------- module : torch.nn.Module @@ -56,7 +56,7 @@ def exec_twice(self, module, inp_1, inp_2): Input arguments. inp_2 : tuple or dict Input arguments. - + Returns ------- tuple @@ -66,7 +66,7 @@ def exec_twice(self, module, inp_1, inp_2): """ torch.manual_seed(self.SEED) result = self.exec_func(module, inp_1) - + torch.manual_seed(self.SEED) result_2 = self.exec_func(module, inp_2) @@ -74,19 +74,19 @@ def exec_twice(self, module, inp_1, inp_2): def exec_func(self, func, args): """Execute function with arguments. - + Parameters ---------- func : function Function to execute. args : tuple or dict Arguments for the function. - + Returns ------- any Output of the function. - + Raises ------ TypeError @@ -98,15 +98,15 @@ def exec_func(self, func, args): return func(**args) else: raise TypeError(f"{type(args)} is not correct type for funcntion arguments.") - + def clone_input(self, args): """Clone input arguments. - + Parameters ---------- args : tuple or dict Input arguments. - + Returns ------- tuple or dict @@ -119,12 +119,12 @@ def clone_input(self, args): def clone_object(self, obj): """Clone object. - + Parameters ---------- obj : any Object to clone. - + Returns ------- any @@ -134,10 +134,10 @@ def clone_object(self, obj): return obj.clone() else: return copy.deepcopy(obj) - + def assert_return_tensor(self, result): """Assert if the module return at least one tensor. - + Parameters ---------- result : tuple @@ -147,7 +147,7 @@ def assert_return_tensor(self, result): def assert_equal_output(self, module, result, result_2): """Assert if the output of the module is the same when called with the same data. - + Parameters ---------- module : torch.nn.Module @@ -158,7 +158,7 @@ def assert_equal_output(self, module, result, result_2): Output tensors. """ assert len(result) == len(result_2) - + for i, r1 in enumerate(result): r2 = result_2[i] if isinstance(r1, torch.Tensor): @@ -168,7 +168,7 @@ def assert_equal_output(self, module, result, result_2): def assert_shape(self, result, shapes): """Assert shapes of the output tensors. - + Parameters ---------- result : tuple @@ -179,5 +179,5 @@ def assert_shape(self, result, shapes): i = 0 for t in result: if isinstance(t, torch.Tensor): - assert t.shape == shapes[i] - i += 1 \ No newline at end of file + assert t.shape == shapes[i] + i += 1 diff --git a/test/_utils/simplified_pipeline.py b/test/_utils/simplified_pipeline.py index 39c949210..6ebd9d111 100644 --- a/test/_utils/simplified_pipeline.py +++ b/test/_utils/simplified_pipeline.py @@ -50,7 +50,7 @@ def run(cfg: DictConfig) -> DictConfig: """Run pipeline with given configuration. - + Parameters ---------- cfg : DictConfig @@ -97,6 +97,3 @@ def run(cfg: DictConfig) -> DictConfig: trainer.test( model=model, datamodule=datamodule, ckpt_path=ckpt_path ) - - - diff --git a/test/callbacks/test_best_epoch_metrics.py b/test/callbacks/test_best_epoch_metrics.py index 08fe55dd5..57b0f50d6 100644 --- a/test/callbacks/test_best_epoch_metrics.py +++ b/test/callbacks/test_best_epoch_metrics.py @@ -27,34 +27,34 @@ def test_init_with_max_mode(self): def test_on_train_start_finds_checkpoint_callback(self): """Test that on_train_start finds ModelCheckpoint callback.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + # Create mock trainer with ModelCheckpoint callback trainer = Mock() checkpoint_callback = ModelCheckpoint() trainer.callbacks = [checkpoint_callback, Mock()] pl_module = Mock() - + callback.on_train_start(trainer, pl_module) - + assert callback.checkpoint_callback is checkpoint_callback def test_on_train_start_without_checkpoint_callback(self): """Test that on_train_start works without ModelCheckpoint (checkpoint_callback stays None).""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + # Create mock trainer without ModelCheckpoint callback trainer = Mock() trainer.callbacks = [Mock(), Mock()] pl_module = Mock() - + callback.on_train_start(trainer, pl_module) - + assert callback.checkpoint_callback is None def test_on_train_epoch_end_captures_metrics(self): """Test that training metrics are captured at end of training epoch.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + trainer = Mock() trainer.callback_metrics = { "train/loss": torch.tensor(0.5), @@ -62,9 +62,9 @@ def test_on_train_epoch_end_captures_metrics(self): "val/loss": torch.tensor(0.6), # Should not be captured } pl_module = Mock() - + callback.on_train_epoch_end(trainer, pl_module) - + assert "train/loss" in callback.current_epoch_train_metrics assert "train/accuracy" in callback.current_epoch_train_metrics assert "val/loss" not in callback.current_epoch_train_metrics @@ -73,13 +73,13 @@ def test_on_train_epoch_end_captures_metrics(self): def test_on_validation_epoch_end_first_epoch(self): """Test that first epoch is always considered best.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + # Setup training metrics callback.current_epoch_train_metrics = { "train/loss": 0.5, "train/accuracy": 0.8 } - + trainer = Mock() trainer.current_epoch = 0 trainer.callback_metrics = { @@ -87,9 +87,9 @@ def test_on_validation_epoch_end_first_epoch(self): "val/accuracy": torch.tensor(0.75), } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + assert callback.best_monitored_value == pytest.approx(0.6) assert callback.best_epoch_number == 0 assert "train/loss" in callback.best_epoch_metrics @@ -100,21 +100,21 @@ def test_on_validation_epoch_end_min_mode_improvement(self): callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") callback.best_monitored_value = 0.6 callback.best_epoch_number = 0 - + # Setup training metrics callback.current_epoch_train_metrics = { "train/loss": 0.3, } - + trainer = Mock() trainer.current_epoch = 1 trainer.callback_metrics = { "val/loss": torch.tensor(0.4), # Better (lower) } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + assert callback.best_monitored_value == pytest.approx(0.4) assert callback.best_epoch_number == 1 @@ -125,21 +125,21 @@ def test_on_validation_epoch_end_min_mode_no_improvement(self): callback.best_epoch_number = 1 old_metrics = {"val/loss": 0.4} callback.best_epoch_metrics = old_metrics.copy() - + # Setup training metrics callback.current_epoch_train_metrics = { "train/loss": 0.5, } - + trainer = Mock() trainer.current_epoch = 2 trainer.callback_metrics = { "val/loss": torch.tensor(0.6), # Worse (higher) } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + # Should not update assert callback.best_monitored_value == 0.4 assert callback.best_epoch_number == 1 @@ -149,21 +149,21 @@ def test_on_validation_epoch_end_max_mode_improvement(self): callback = BestEpochMetricsCallback(monitor="val/accuracy", mode="max") callback.best_monitored_value = 0.7 callback.best_epoch_number = 0 - + # Setup training metrics callback.current_epoch_train_metrics = { "train/accuracy": 0.85, } - + trainer = Mock() trainer.current_epoch = 1 trainer.callback_metrics = { "val/accuracy": torch.tensor(0.8), # Better (higher) } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + assert callback.best_monitored_value == pytest.approx(0.8) assert callback.best_epoch_number == 1 @@ -172,21 +172,21 @@ def test_on_validation_epoch_end_max_mode_no_improvement(self): callback = BestEpochMetricsCallback(monitor="val/accuracy", mode="max") callback.best_monitored_value = 0.8 callback.best_epoch_number = 1 - + # Setup training metrics callback.current_epoch_train_metrics = { "train/accuracy": 0.75, } - + trainer = Mock() trainer.current_epoch = 2 trainer.callback_metrics = { "val/accuracy": torch.tensor(0.7), # Worse (lower) } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + # Should not update assert callback.best_monitored_value == 0.8 assert callback.best_epoch_number == 1 @@ -194,13 +194,13 @@ def test_on_validation_epoch_end_max_mode_no_improvement(self): def test_best_epoch_logging(self): """Test that best epoch number and metrics are logged.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + # Setup training metrics callback.current_epoch_train_metrics = { "train/loss": 0.5, "train/accuracy": 0.8 } - + trainer = Mock() trainer.current_epoch = 5 trainer.callback_metrics = { @@ -208,12 +208,12 @@ def test_best_epoch_logging(self): "val/accuracy": torch.tensor(0.85), } pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + # Check that log was called with best epoch number pl_module.log.assert_any_call("best_epoch", 5, prog_bar=False) - + # Check that metrics were logged with best_epoch prefix calls = [call[0] for call in pl_module.log.call_args_list] assert any("best_epoch/train/loss" in str(call) for call in calls) @@ -222,19 +222,19 @@ def test_best_epoch_logging(self): def test_on_train_end_with_checkpoint(self): """Test on_train_end logs checkpoint path when available.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + # Setup checkpoint callback checkpoint_callback = Mock() checkpoint_callback.best_model_path = "/path/to/checkpoint.ckpt" callback.checkpoint_callback = checkpoint_callback - + trainer = Mock() pl_module = Mock() pl_module.logger = Mock() pl_module.logger.experiment.summary = {} - + callback.on_train_end(trainer, pl_module) - + # Check that checkpoint path was logged assert pl_module.logger.experiment.summary["best_epoch/checkpoint"] == "/path/to/checkpoint.ckpt" assert pl_module.logger.experiment.summary["monitored_metric"] == "val/loss (min)" @@ -243,32 +243,32 @@ def test_on_train_end_without_checkpoint(self): """Test on_train_end handles missing checkpoint gracefully.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") callback.checkpoint_callback = None - + trainer = Mock() pl_module = Mock() - + # Should not raise error callback.on_train_end(trainer, pl_module) def test_on_train_end_without_logger(self): """Test on_train_end handles missing logger gracefully.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + checkpoint_callback = Mock() checkpoint_callback.best_model_path = "/path/to/checkpoint.ckpt" callback.checkpoint_callback = checkpoint_callback - + trainer = Mock() pl_module = Mock() pl_module.logger = None - + # Should not raise error callback.on_train_end(trainer, pl_module) def test_handles_tensor_values(self): """Test that callback properly converts tensor values to floats.""" callback = BestEpochMetricsCallback(monitor="val/loss", mode="min") - + trainer = Mock() trainer.current_epoch = 0 trainer.callback_metrics = { @@ -277,9 +277,9 @@ def test_handles_tensor_values(self): } callback.current_epoch_train_metrics = {} pl_module = Mock() - + callback.on_validation_epoch_end(trainer, pl_module) - + # Values should be converted to floats assert isinstance(callback.best_monitored_value, float) assert callback.best_monitored_value == 0.5 diff --git a/test/callbacks/test_timer_callback.py b/test/callbacks/test_timer_callback.py index 9870a15be..398a17319 100644 --- a/test/callbacks/test_timer_callback.py +++ b/test/callbacks/test_timer_callback.py @@ -11,7 +11,7 @@ class TestPipelineTimer: def test_init(self): """Test callback initialization.""" timer = PipelineTimer() - + # Check that all stage dictionaries are initialized expected_stages = ["train_batch", "train_epoch", "val_batch", "val_epoch", "test_batch", "test_epoch"] for stage in expected_stages: @@ -19,27 +19,27 @@ def test_init(self): assert stage in timer.counts assert timer.sums[stage] == [] assert timer.counts[stage] == 0 - + assert timer.times == {} assert timer.skip_first_n == 10 def test_start_timer(self): """Test that _start_timer records the start time.""" timer = PipelineTimer() - + timer._start_timer("train_batch") - + assert "train_batch" in timer.times assert isinstance(timer.times["train_batch"], float) def test_end_timer(self): """Test that _end_timer calculates and stores elapsed time.""" timer = PipelineTimer() - + timer._start_timer("train_batch") time.sleep(0.01) # Small delay timer._end_timer("train_batch") - + assert len(timer.sums["train_batch"]) == 1 assert timer.counts["train_batch"] == 1 assert timer.sums["train_batch"][0] > 0 @@ -48,10 +48,10 @@ def test_end_timer(self): def test_end_timer_without_start(self): """Test that _end_timer handles missing start gracefully.""" timer = PipelineTimer() - + # Should not raise error timer._end_timer("train_batch") - + # Nothing should be recorded assert len(timer.sums["train_batch"]) == 0 assert timer.counts["train_batch"] == 0 @@ -59,154 +59,154 @@ def test_end_timer_without_start(self): def test_multiple_timer_cycles(self): """Test multiple start/end cycles accumulate correctly.""" timer = PipelineTimer() - + for _ in range(5): timer._start_timer("train_batch") time.sleep(0.001) timer._end_timer("train_batch") - + assert len(timer.sums["train_batch"]) == 5 assert timer.counts["train_batch"] == 5 def test_train_batch_hooks(self): """Test train batch timing hooks.""" timer = PipelineTimer() - + timer.on_train_batch_start() assert "train_batch" in timer.times - + time.sleep(0.001) timer.on_train_batch_end() - + assert len(timer.sums["train_batch"]) == 1 assert timer.counts["train_batch"] == 1 def test_train_epoch_hooks(self): """Test train epoch timing hooks.""" timer = PipelineTimer() - + timer.on_train_epoch_start() assert "train_epoch" in timer.times - + time.sleep(0.001) timer.on_train_epoch_end() - + assert len(timer.sums["train_epoch"]) == 1 assert timer.counts["train_epoch"] == 1 def test_validation_batch_hooks(self): """Test validation batch timing hooks.""" timer = PipelineTimer() - + timer.on_validation_batch_start() assert "val_batch" in timer.times - + time.sleep(0.001) timer.on_validation_batch_end() - + assert len(timer.sums["val_batch"]) == 1 assert timer.counts["val_batch"] == 1 def test_validation_epoch_hooks(self): """Test validation epoch timing hooks.""" timer = PipelineTimer() - + timer.on_validation_epoch_start() assert "val_epoch" in timer.times - + time.sleep(0.001) timer.on_validation_epoch_end() - + assert len(timer.sums["val_epoch"]) == 1 assert timer.counts["val_epoch"] == 1 def test_test_batch_hooks(self): """Test test batch timing hooks.""" timer = PipelineTimer() - + timer.on_test_batch_start() assert "test_batch" in timer.times - + time.sleep(0.001) timer.on_test_batch_end() - + assert len(timer.sums["test_batch"]) == 1 assert timer.counts["test_batch"] == 1 def test_test_epoch_hooks(self): """Test test epoch timing hooks.""" timer = PipelineTimer() - + timer.on_test_epoch_start() assert "test_epoch" in timer.times - + time.sleep(0.001) timer.on_test_epoch_end() - + assert len(timer.sums["test_epoch"]) == 1 assert timer.counts["test_epoch"] == 1 def test_log_hyperparams_with_single_logger(self): """Test _log_hyperparams with a single logger.""" timer = PipelineTimer() - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + params = {"metric1": 1.5, "metric2": 2.5} timer._log_hyperparams(trainer, params) - + logger.log_hyperparams.assert_called_once_with(params) def test_log_hyperparams_with_multiple_loggers(self): """Test _log_hyperparams with multiple loggers.""" timer = PipelineTimer() - + trainer = Mock() logger1 = Mock() logger1.log_hyperparams = Mock() logger2 = Mock() logger2.log_hyperparams = Mock() trainer.logger = [logger1, logger2] - + params = {"metric1": 1.5} timer._log_hyperparams(trainer, params) - + logger1.log_hyperparams.assert_called_once_with(params) logger2.log_hyperparams.assert_called_once_with(params) def test_log_hyperparams_without_logger(self): """Test _log_hyperparams handles missing logger gracefully.""" timer = PipelineTimer() - + trainer = Mock() trainer.logger = None - + # Should not raise error timer._log_hyperparams(trainer, {"metric1": 1.5}) def test_log_hyperparams_without_log_hyperparams_method(self): """Test _log_hyperparams handles loggers without log_hyperparams method.""" timer = PipelineTimer() - + trainer = Mock() logger = Mock(spec=[]) # Logger without log_hyperparams method trainer.logger = logger - + # Should not raise error timer._log_hyperparams(trainer, {"metric1": 1.5}) def test_log_hyperparams_exception_suppression(self): """Test that exceptions in log_hyperparams are suppressed.""" timer = PipelineTimer() - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock(side_effect=Exception("Test error")) trainer.logger = logger - + # Should not raise error timer._log_hyperparams(trainer, {"metric1": 1.5}) @@ -214,23 +214,23 @@ def test_log_averages_skips_first_n(self): """Test that _log_averages skips first N measurements for non-test stages.""" timer = PipelineTimer() timer.skip_first_n = 3 - + # Add some measurements for i in range(10): timer.sums["train_batch"].append(float(i)) timer.counts["train_batch"] += 1 - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + timer._log_averages(trainer) - + # Should be called with averaged values (skipping first 3) logger.log_hyperparams.assert_called_once() call_args = logger.log_hyperparams.call_args[0][0] - + # Average of [3, 4, 5, 6, 7, 8, 9] = 6.0 assert "AvgTime/train_batch_mean" in call_args assert call_args["AvgTime/train_batch_mean"] == pytest.approx(6.0) @@ -239,21 +239,21 @@ def test_log_averages_test_stages_no_skip(self): """Test that _log_averages doesn't skip measurements for test stages.""" timer = PipelineTimer() timer.skip_first_n = 3 - + # Add test measurements for i in range(5): timer.sums["test_batch"].append(float(i)) timer.counts["test_batch"] += 1 - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + timer._log_averages(trainer) - + call_args = logger.log_hyperparams.call_args[0][0] - + # Average of all [0, 1, 2, 3, 4] = 2.0 assert "AvgTime/test_batch_mean" in call_args assert call_args["AvgTime/test_batch_mean"] == pytest.approx(2.0) @@ -264,20 +264,20 @@ def test_log_averages_computes_std(self): """Test that _log_averages computes standard deviation.""" timer = PipelineTimer() timer.skip_first_n = 0 - + # Add measurements with known std timer.sums["train_batch"] = [1.0, 2.0, 3.0, 4.0, 5.0] timer.counts["train_batch"] = 5 - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + timer._log_averages(trainer) - + call_args = logger.log_hyperparams.call_args[0][0] - + assert "AvgTime/train_batch_mean" in call_args assert "AvgTime/train_batch_std" in call_args assert call_args["AvgTime/train_batch_mean"] == pytest.approx(3.0) @@ -286,16 +286,16 @@ def test_log_averages_computes_std(self): def test_log_averages_empty_stage(self): """Test that _log_averages handles stages with no measurements.""" timer = PipelineTimer() - + # Don't add any measurements trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + # Should not raise error timer._log_averages(trainer) - + # Should still be called (but with empty or minimal data) logger.log_hyperparams.assert_called_once() @@ -303,53 +303,53 @@ def test_on_train_end_calls_log_averages(self): """Test that on_train_end calls _log_averages.""" timer = PipelineTimer() timer.skip_first_n = 0 # Don't skip any measurements to avoid empty slices - + # Add some measurements to all stages to avoid warnings for stage in ["train_batch", "train_epoch", "val_batch", "val_epoch", "test_batch", "test_epoch"]: timer.sums[stage] = [1.0, 2.0, 3.0] timer.counts[stage] = 3 - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + timer.on_train_end(trainer) - + # Should have called log_hyperparams through _log_averages logger.log_hyperparams.assert_called_once() def test_full_training_cycle(self): """Test a complete training cycle with all hooks.""" timer = PipelineTimer() - + # Simulate training loop for epoch in range(2): timer.on_train_epoch_start() - + for batch in range(3): timer.on_train_batch_start() time.sleep(0.001) timer.on_train_batch_end() - + timer.on_train_epoch_end() - + # Validation timer.on_validation_epoch_start() - + for batch in range(2): timer.on_validation_batch_start() time.sleep(0.001) timer.on_validation_batch_end() - + timer.on_validation_epoch_end() - + # Check all stages were timed assert timer.counts["train_batch"] == 6 assert timer.counts["train_epoch"] == 2 assert timer.counts["val_batch"] == 4 assert timer.counts["val_epoch"] == 2 - + # Check measurements are positive assert all(t > 0 for t in timer.sums["train_batch"]) assert all(t > 0 for t in timer.sums["val_batch"]) @@ -358,22 +358,22 @@ def test_all_stages_logged(self): """Test that _log_averages logs all stages with measurements.""" timer = PipelineTimer() timer.skip_first_n = 0 - + # Add measurements to all stages all_stages = ["train_batch", "train_epoch", "val_batch", "val_epoch", "test_batch", "test_epoch"] for stage in all_stages: timer.sums[stage] = [1.0, 2.0, 3.0] timer.counts[stage] = 3 - + trainer = Mock() logger = Mock() logger.log_hyperparams = Mock() trainer.logger = logger - + timer._log_averages(trainer) - + call_args = logger.log_hyperparams.call_args[0][0] - + # All stages should have mean and std logged for stage in all_stages: assert f"AvgTime/{stage}_mean" in call_args diff --git a/test/conftest.py b/test/conftest.py index 27de49aed..c154659a1 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -14,12 +14,12 @@ @pytest.fixture def mocker_fixture(mocker): """Return pytest mocker, used when one want to use mocker in setup_method. - + Parameters ---------- mocker : pytest_mock.plugin.MockerFixture A pytest mocker. - + Returns ------- pytest_mock.plugin.MockerFixture @@ -31,7 +31,7 @@ def mocker_fixture(mocker): @pytest.fixture def simple_graph_0(): """Create a manual graph for testing purposes. - + Returns ------- torch_geometric.data.Data @@ -77,7 +77,7 @@ def simple_graph_0(): @pytest.fixture def simple_graph_1(): """Create a manual graph for testing purposes. - + Returns ------- torch_geometric.data.Data @@ -137,12 +137,12 @@ def simple_graph_1(): @pytest.fixture def sg1_clique_lifted(simple_graph_1): """Return a simple graph with a clique lifting. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data A simple graph data object. - + Returns ------- torch_geometric.data.Data @@ -158,12 +158,12 @@ def sg1_clique_lifted(simple_graph_1): @pytest.fixture def sg1_cell_lifted(simple_graph_1): """Return a simple graph with a cell lifting. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data A simple graph data object. - + Returns ------- torch_geometric.data.Data @@ -178,7 +178,7 @@ def sg1_cell_lifted(simple_graph_1): @pytest.fixture def simple_graph_2(): """Create a manual graph for testing purposes. - + Returns ------- torch_geometric.data.Data @@ -244,7 +244,7 @@ def simple_graph_2(): @pytest.fixture def random_graph_input(): """Create a random graph for testing purposes. - + Returns ------- torch.Tensor @@ -263,11 +263,10 @@ def random_graph_input(): x = torch.randn(num_nodes, 12) edges_1 = torch.randint(0, num_nodes, (2, num_nodes*2)) edges_2 = torch.randint(0, num_nodes, (2, num_nodes*2)) - + d_feat_1, d_feat_2 = 5, 17 x_1 = torch.randn(num_nodes*2, d_feat_1) x_2 = torch.randn(num_nodes*2, d_feat_2) return x, x_1, x_2, edges_1, edges_2 - diff --git a/test/data/__init__.py b/test/data/__init__.py index d6f9f795a..6a953e457 100644 --- a/test/data/__init__.py +++ b/test/data/__init__.py @@ -1 +1 @@ -"""Init file for data.""" \ No newline at end of file +"""Init file for data.""" diff --git a/test/data/dataload/test_Dataloaders.py b/test/data/dataload/test_Dataloaders.py index bc16470db..d4ef6975d 100644 --- a/test/data/dataload/test_Dataloaders.py +++ b/test/data/dataload/test_Dataloaders.py @@ -45,15 +45,15 @@ def setup_method(self): self.val_dataset = dataset_val def test_lift_features(self): - """Test the collate funciton. + """Test the collate function. - To test the collate function we use the TBDataloader class to create a dataloader that uses the collate function. - We then first check that the batched data has the expected shape. We then convert the batched data back to a list and check that the data in the list is the same as the original data. + To test the collate function we use the TBDataloader class to create a dataloader that uses the collate function. + We first check that the batched data has the expected shape. We then convert the batched data back to a list and check that the data in the list is the same as the original data. """ def check_shape(batch, elems, key): """Check that the batched data has the expected shape. - + Parameters ---------- batch : dict @@ -91,7 +91,7 @@ def check_shape(batch, elems, key): def check_separation(matrix, n_elems_0_row, n_elems_0_col): """Check that the matrix is separated into two parts diagonally concatenated. - + Parameters ---------- matrix : torch.Tensor @@ -106,7 +106,7 @@ def check_separation(matrix, n_elems_0_row, n_elems_0_col): def check_values(matrix, m1, m2): """Check that the values in the matrix are the same as the values in the original data. - + Parameters ---------- matrix : torch.Tensor @@ -168,4 +168,4 @@ def check_values(matrix, m1, m2): if __name__ == "__main__": t = TestCollateFunction() t.setup_method() - t.test_lift_features() \ No newline at end of file + t.test_lift_features() diff --git a/test/data/load/test_datasetloaders.py b/test/data/load/test_datasetloaders.py index cb21fd421..8375a5a59 100644 --- a/test/data/load/test_datasetloaders.py +++ b/test/data/load/test_datasetloaders.py @@ -9,7 +9,7 @@ from topobench.data.preprocessor.preprocessor import PreProcessor class TestLoaders: """Comprehensive test suite for all dataset loaders.""" - + @pytest.fixture(autouse=True) def setup(self): """Setup test environment before each test method.""" @@ -23,7 +23,7 @@ def setup(self): # Existing helper methods remain the same def _gather_config_files(self, base_dir: Path) -> List[str]: """Gather all relevant config files. - + Parameters ---------- base_dir : Path @@ -43,11 +43,11 @@ def _gather_config_files(self, base_dir: Path) -> List[str]: "REDDIT-BINARY.yaml", "IMDB-MULTI.yaml", "IMDB-BINARY.yaml", #"ZINC.yaml" "ogbg-molpcba.yaml", "manual_dataset.yaml" # "ogbg-molhiv.yaml" } - - # Below the datasets that takes quite some time to load and process + + # Below the datasets that takes quite some time to load and process self.long_running_datasets = {"mantra_name.yaml", "mantra_orientation.yaml", "mantra_genus.yaml", "mantra_betti_numbers.yaml"} - + for dir_path in config_base_dir.iterdir(): curr_dir = str(dir_path).split('/')[-1] if dir_path.is_dir(): @@ -66,7 +66,7 @@ def _load_dataset(self, data_domain: str, config_file: str) -> Tuple[Any, Dict]: Name of the data domain. config_file : str Name of the config file. - + Returns ------- Tuple[Any, Dict] @@ -80,8 +80,8 @@ def _load_dataset(self, data_domain: str, config_file: str) -> Tuple[Any, Dict]: print('Current config file: ', config_file) parameters = hydra.compose( config_name="run.yaml", - overrides=[f"dataset={data_domain}/{config_file}", f"model=graph/gat"], - return_hydra_config=True, + overrides=[f"dataset={data_domain}/{config_file}", f"model=graph/gat"], + return_hydra_config=True, ) dataset_loader = hydra.utils.instantiate(parameters.dataset.loader) print(repr(dataset_loader)) @@ -97,31 +97,24 @@ def test_dataset_loading_states(self): for config_data in self.config_files: data_domain, config_file = config_data dataset, _ = self._load_dataset(data_domain, config_file) - + # Test dataset size and dimensions if hasattr(dataset, "data"): assert dataset.data.x.size(0) > 0, "Empty node features" assert dataset.data.y.size(0) > 0, "Empty labels" - + # Below brakes with manual dataset - # else: + # else: # assert dataset[0].x.size(0) > 0, "Empty node features" # assert dataset[0].y.size(0) > 0, "Empty labels" - + # Test node feature dimensions if hasattr(dataset, 'num_node_features'): assert dataset.data.x.size(1) == dataset.num_node_features - + # Below brakes with manual dataset # # Test label dimensions # if hasattr(dataset, 'num_classes'): # assert torch.max(dataset.data.y) < dataset.num_classes repr(dataset) - - - - - - - diff --git a/test/data/preprocess/__init__.py b/test/data/preprocess/__init__.py index 5966a5cb8..5efe5665c 100644 --- a/test/data/preprocess/__init__.py +++ b/test/data/preprocess/__init__.py @@ -1 +1 @@ -"""Init file for preprocess.""" \ No newline at end of file +"""Init file for preprocess.""" diff --git a/test/data/preprocess/test_preprocessor.py b/test/data/preprocess/test_preprocessor.py index 89a60c321..110fb6f43 100644 --- a/test/data/preprocess/test_preprocessor.py +++ b/test/data/preprocess/test_preprocessor.py @@ -18,7 +18,7 @@ class MockTorchDataset(torch.utils.data.Dataset): """A mock of the torch.utils.data.Dataset class. - + Parameters ---------- data : Any @@ -29,7 +29,7 @@ def __init__(self, data): def __len__(self): """Return the length of the data. - + Returns ------- int @@ -39,12 +39,12 @@ def __len__(self): def __getitem__(self, idx): """Return the data at the given index. - + Parameters ---------- idx : int The index of the data to return. - + Returns ------- Any @@ -66,12 +66,12 @@ def test_init_without_transforms(self): torch_geometric.data.Data(x=torch.randn(3, 4)), torch_geometric.data.Data(x=torch.randn(5, 4)), ])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + assert preprocessor.transforms_applied == False assert hasattr(preprocessor, 'data_list') @@ -83,12 +83,12 @@ def test_init_preserves_split_idx(self): mock_dataset.slices = {} mock_dataset.split_idx = {"train": [0, 1], "val": [2], "test": [3]} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + assert hasattr(preprocessor, "split_idx") assert preprocessor.split_idx == mock_dataset.split_idx @@ -99,18 +99,18 @@ def test_processed_file_names(self): mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + assert preprocessor.processed_file_names == "data.pt" @patch("topobench.data.preprocessor.preprocessor.load_inductive_splits") def test_load_dataset_splits_inductive(self, mock_load_inductive_splits): """Test loading dataset splits for inductive learning. - + Parameters ---------- mock_load_inductive_splits : MagicMock @@ -121,15 +121,15 @@ def test_load_dataset_splits_inductive(self, mock_load_inductive_splits): mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + split_params = DictConfig({"learning_setting": "inductive"}) preprocessor.load_dataset_splits(split_params) - + mock_load_inductive_splits.assert_called_once_with( preprocessor, split_params ) @@ -137,7 +137,7 @@ def test_load_dataset_splits_inductive(self, mock_load_inductive_splits): @patch("topobench.data.preprocessor.preprocessor.load_transductive_splits") def test_load_dataset_splits_transductive(self, mock_load_transductive_splits): """Test loading dataset splits for transductive learning. - + Parameters ---------- mock_load_transductive_splits : MagicMock @@ -148,15 +148,15 @@ def test_load_dataset_splits_transductive(self, mock_load_transductive_splits): mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + split_params = DictConfig({"learning_setting": "transductive"}) preprocessor.load_dataset_splits(split_params) - + mock_load_transductive_splits.assert_called_once_with( preprocessor, split_params ) @@ -168,12 +168,12 @@ def test_invalid_learning_setting(self): mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + split_params = DictConfig({"learning_setting": "invalid"}) with pytest.raises(ValueError, match="Invalid.*learning setting"): preprocessor.load_dataset_splits(split_params) @@ -185,17 +185,17 @@ def test_no_learning_setting_error(self): mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} mock_dataset.__iter__ = MagicMock(return_value=iter([])) - + with tempfile.TemporaryDirectory() as tmpdir: with patch("torch_geometric.data.InMemoryDataset.__init__"): with patch.object(PreProcessor, "load"): preprocessor = PreProcessor(mock_dataset, tmpdir, None) - + # Test with no learning_setting key split_params = DictConfig({}) with pytest.raises(ValueError, match="No learning setting specified"): preprocessor.load_dataset_splits(split_params) - + # Test with learning_setting = False split_params = DictConfig({"learning_setting": False}) with pytest.raises(ValueError, match="No learning setting specified"): @@ -212,7 +212,7 @@ def test_process_with_torch_utils_dataset(self): torch_geometric.data.Data(x=torch.randn(5, 4)), ] mock_dataset = MockTorchDataset(mock_data) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) @@ -222,11 +222,11 @@ def test_process_with_torch_utils_dataset(self): return_value=(torch_geometric.data.Data(), {}) ) preprocessor.save = MagicMock() - + # Mock the processed_paths property with patch.object(type(preprocessor), 'processed_paths', new_callable=lambda: property(lambda self: [f"{tmpdir}/data.pt"])): preprocessor.process() - + assert len(preprocessor.data_list) == len(mock_data) preprocessor.collate.assert_called_once() preprocessor.save.assert_called_once() @@ -234,7 +234,7 @@ def test_process_with_torch_utils_dataset(self): def test_process_with_torch_geometric_data(self): """Test process method with torch_geometric.data.Data.""" mock_data = torch_geometric.data.Data(x=torch.randn(3, 4)) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) @@ -244,11 +244,11 @@ def test_process_with_torch_geometric_data(self): return_value=(torch_geometric.data.Data(), {}) ) preprocessor.save = MagicMock() - + # Mock the processed_paths property with patch.object(type(preprocessor), 'processed_paths', new_callable=lambda: property(lambda self: [f"{tmpdir}/data.pt"])): preprocessor.process() - + assert preprocessor.data_list == [mock_data] preprocessor.collate.assert_called_once_with([mock_data]) @@ -260,7 +260,7 @@ def test_process_with_pre_transform(self): ] mock_dataset = MockTorchDataset(mock_data) mock_pre_transform = MagicMock(side_effect=lambda x: x) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) @@ -270,11 +270,11 @@ def test_process_with_pre_transform(self): return_value=(torch_geometric.data.Data(), {}) ) preprocessor.save = MagicMock() - + # Mock the processed_paths property with patch.object(type(preprocessor), 'processed_paths', new_callable=lambda: property(lambda self: [f"{tmpdir}/data.pt"])): preprocessor.process() - + # Verify pre_transform was called for each data item assert mock_pre_transform.call_count == len(mock_data) @@ -285,7 +285,7 @@ class TestPreProcessorLoad: @patch("topobench.data.preprocessor.preprocessor.fs.torch_load") def test_load_backward_compatibility_2_elements(self, mock_torch_load): """Test load method with 2 elements (backward compatibility). - + Parameters ---------- mock_torch_load : MagicMock @@ -294,12 +294,12 @@ def test_load_backward_compatibility_2_elements(self, mock_torch_load): mock_data = torch_geometric.data.Data() mock_slices = {"x": torch.tensor([0, 3])} mock_torch_load.return_value = (mock_data, mock_slices) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.load("/fake/path") - + # Use _data as that's what the actual code uses assert preprocessor._data == mock_data assert preprocessor.slices == mock_slices @@ -307,7 +307,7 @@ def test_load_backward_compatibility_2_elements(self, mock_torch_load): @patch("topobench.data.preprocessor.preprocessor.fs.torch_load") def test_load_backward_compatibility_3_elements(self, mock_torch_load): """Test load method with 3 elements (backward compatibility). - + Parameters ---------- mock_torch_load : MagicMock @@ -317,19 +317,19 @@ def test_load_backward_compatibility_3_elements(self, mock_torch_load): mock_slices = {"x": torch.tensor([0, 3])} mock_data_cls = torch_geometric.data.Data mock_torch_load.return_value = (mock_data, mock_slices, mock_data_cls) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.load("/fake/path") - + assert preprocessor._data == mock_data assert preprocessor.slices == mock_slices @patch("topobench.data.preprocessor.preprocessor.fs.torch_load") def test_load_with_4_elements(self, mock_torch_load): """Test load method with 4 elements (TU Datasets format). - + Parameters ---------- mock_torch_load : MagicMock @@ -340,26 +340,26 @@ def test_load_with_4_elements(self, mock_torch_load): mock_sizes = {"x": 3} mock_data_cls = torch_geometric.data.Data mock_torch_load.return_value = (mock_data, mock_slices, mock_sizes, mock_data_cls) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.load("/fake/path") - + assert preprocessor._data == mock_data assert preprocessor.slices == mock_slices @patch("topobench.data.preprocessor.preprocessor.fs.torch_load") def test_load_with_dict_data(self, mock_torch_load): """Test load method when data is a dictionary. - + Parameters ---------- mock_torch_load : MagicMock Mock of the torch_load function. """ mock_data_dict = { - "x": torch.randn(3, 4), + "x": torch.randn(3, 4), "edge_index": torch.tensor([[0, 1], [1, 2]]) } mock_slices = {"x": torch.tensor([0, 3])} @@ -367,12 +367,12 @@ def test_load_with_dict_data(self, mock_torch_load): mock_reconstructed_data = torch_geometric.data.Data() mock_data_cls.from_dict.return_value = mock_reconstructed_data mock_torch_load.return_value = (mock_data_dict, mock_slices, mock_data_cls) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.load("/fake/path") - + mock_data_cls.from_dict.assert_called_once_with(mock_data_dict) assert preprocessor._data == mock_reconstructed_data assert preprocessor.slices == mock_slices @@ -390,15 +390,15 @@ def test_save_transform_parameters_new_file(self): preprocessor.transforms_parameters = { "transform1": {"param": "value"} } - + preprocessor.save_transform_parameters() - + # Check if file was created param_file = os.path.join( tmpdir, "path_transform_parameters_dict.json" ) assert os.path.exists(param_file) - + # Check file contents with open(param_file, 'r') as f: saved_params = json.load(f) @@ -406,7 +406,7 @@ def test_save_transform_parameters_new_file(self): def test_save_transform_parameters_existing_same(self, capsys): """Test saving transform parameters when file exists with same params. - + Parameters ---------- capsys : pytest.CaptureFixture @@ -420,14 +420,14 @@ def test_save_transform_parameters_existing_same(self, capsys): ) with open(param_file, 'w') as f: json.dump(params, f) - + with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.processed_data_dir = tmpdir preprocessor.transforms_parameters = params - + preprocessor.save_transform_parameters() - + # Check that message was printed captured = capsys.readouterr() assert "Transform parameters are the same" in captured.out @@ -442,14 +442,14 @@ def test_save_transform_parameters_existing_different(self): ) with open(param_file, 'w') as f: json.dump(existing_params, f) - + with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.processed_data_dir = tmpdir preprocessor.transforms_parameters = { "transform1": {"param": "new_value"} } - + with pytest.raises(ValueError, match="Different transform parameters"): preprocessor.save_transform_parameters() @@ -459,27 +459,27 @@ def test_instantiate_pre_transform_with_liftings(self): mock_dataset.transform = None mock_dataset._data = torch_geometric.data.Data() mock_dataset.slices = {} - + transforms_config = DictConfig({ "liftings": { "transform1": {"transform_name": "DummyTransform", "param1": "value1"} } }) - + with tempfile.TemporaryDirectory() as tmpdir: # Create preprocessor instance with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) - + # Mock DataTransform to avoid needing real transforms with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: mock_dt.return_value = MagicMock() preprocessor.set_processed_data_dir = MagicMock() - + pre_transform = preprocessor.instantiate_pre_transform( tmpdir, transforms_config ) - + # Check that a Compose object was created assert hasattr(pre_transform, '__call__') @@ -489,22 +489,22 @@ def test_instantiate_pre_transform_multiple_transforms(self): "transform1": {"transform_name": "Transform1", "param1": "value1"}, "transform2": {"transform_name": "Transform2", "param2": "value2"} }) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) - + # Mock DataTransform with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: mock_dt.return_value = MagicMock() - + # Mock set_processed_data_dir preprocessor.set_processed_data_dir = MagicMock() - + pre_transform = preprocessor.instantiate_pre_transform( tmpdir, transforms_config ) - + # DataTransform should be called for each transform assert mock_dt.call_count == 2 assert hasattr(pre_transform, '__call__') @@ -516,32 +516,32 @@ def test_instantiate_pre_transform_single_transform(self): "param1": "value1", "param2": 42 }) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) - + # Mock DataTransform with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: # Mock DataTransform to return a mock object mock_transform = MagicMock() mock_dt.return_value = mock_transform - + # Mock set_processed_data_dir preprocessor.set_processed_data_dir = MagicMock() - + pre_transform = preprocessor.instantiate_pre_transform( tmpdir, transforms_config ) - + # DataTransform should be called once with the entire config assert mock_dt.call_count == 1 # Should be called with all the config parameters mock_dt.assert_called_once_with(**transforms_config) - + # Verify the pre_transform is a Compose object assert isinstance( - pre_transform, + pre_transform, torch_geometric.transforms.Compose ) @@ -550,20 +550,20 @@ def test_instantiate_pre_transform_calls_set_processed_data_dir(self): transforms_config = DictConfig({ "transform1": {"transform_name": "Transform1", "param1": "value1"} }) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) - + with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: mock_dt.return_value = MagicMock() # Mock set_processed_data_dir preprocessor.set_processed_data_dir = MagicMock() - + pre_transform = preprocessor.instantiate_pre_transform( tmpdir, transforms_config ) - + # Verify set_processed_data_dir was called preprocessor.set_processed_data_dir.assert_called_once() call_args = preprocessor.set_processed_data_dir.call_args @@ -575,22 +575,22 @@ def test_instantiate_pre_transform_returns_compose(self): transforms_config = DictConfig({ "transform1": {"transform_name": "Transform1", "param1": "value1"} }) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) - + with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: mock_dt.return_value = MagicMock() preprocessor.set_processed_data_dir = MagicMock() - + pre_transform = preprocessor.instantiate_pre_transform( tmpdir, transforms_config ) - + # Check it's a Compose instance assert isinstance( - pre_transform, + pre_transform, torch_geometric.transforms.Compose ) @@ -600,33 +600,33 @@ def test_instantiate_pre_transform_single_vs_multiple(self): with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.set_processed_data_dir = MagicMock() - + with patch("topobench.data.preprocessor.preprocessor.DataTransform") as mock_dt: mock_dt.return_value = MagicMock() - + # Test single transform (has transform_name key) single_config = DictConfig({ "transform_name": "SingleTransform", "param1": "value1" }) - + preprocessor.instantiate_pre_transform(tmpdir, single_config) - + # Should be called once with all parameters assert mock_dt.call_count == 1 mock_dt.assert_called_with(**single_config) - + # Reset mock mock_dt.reset_mock() - + # Test multiple transforms (no transform_name key at top level) multiple_config = DictConfig({ "transform1": {"transform_name": "Transform1", "param1": "value1"}, "transform2": {"transform_name": "Transform2", "param2": "value2"} }) - + preprocessor.instantiate_pre_transform(tmpdir, multiple_config) - + # Should be called twice, once for each transform assert mock_dt.call_count == 2 @@ -637,7 +637,7 @@ class TestPreProcessorEdgeCases: def test_process_with_empty_dataset(self): """Test process method with an empty dataset.""" mock_dataset = MockTorchDataset([]) - + with tempfile.TemporaryDirectory() as tmpdir: with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) @@ -648,11 +648,11 @@ def test_process_with_empty_dataset(self): return_value=(torch_geometric.data.Data(), {}) ) preprocessor.save = MagicMock() - + # Mock the processed_paths property with patch.object(type(preprocessor), 'processed_paths', new_callable=lambda: property(lambda self: [f"{tmpdir}/data.pt"])): preprocessor.process() - + assert preprocessor.data_list == [] def test_processed_dir_property(self): @@ -663,13 +663,13 @@ def test_processed_dir_property(self): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.root = tmpdir preprocessor.transforms_applied = False - + assert preprocessor.processed_dir == tmpdir - + # With transforms with patch.object(PreProcessor, '__init__', lambda self, *args, **kwargs: None): preprocessor = PreProcessor(None, tmpdir, None) preprocessor.root = tmpdir preprocessor.transforms_applied = True - + assert preprocessor.processed_dir == tmpdir + "/processed" diff --git a/test/data/utils/test_data_utils.py b/test/data/utils/test_data_utils.py index b813c340b..33bc5a1ef 100644 --- a/test/data/utils/test_data_utils.py +++ b/test/data/utils/test_data_utils.py @@ -19,54 +19,54 @@ def setup_method(self): self.complex.add_cell([5, 6, 7],rank=2) self.neighborhoods1 = ['up_adjacency-0','2-up_adjacency-0','2-down_laplacian-2','2-down_adjacency-2','2-up_incidence-0','2-down_incidence-2'] self.neighborhoods2 = ['down_incidence-1', 'up_laplacian-0', 'down_laplacian-1', 'up_adjacency-0', 'hodge_laplacian-1'] - - + + def test_get_complex_connectivity(self): """Test get_complex_connectivity.""" out = get_complex_connectivity(self.complex, 3, neighborhoods=self.neighborhoods2) assert 'up_laplacian-0' in out.keys() - + def test_get_combinatorial_complex_connectivity(self): """Test get_complex_connectivity.""" out = get_combinatorial_complex_connectivity(self.complex, 3) assert 'adjacency_0' in out.keys() - + def test_select_neighborhoods_of_interest(self): """Test select_neighborhoods_of_interest.""" connectivity = get_complex_connectivity(self.complex, 2) out = select_neighborhoods_of_interest(connectivity, self.neighborhoods1) assert '2-down_laplacian-2' in out.keys() assert 'incidence_1' in out.keys() - + with pytest.raises(ValueError) as e: select_neighborhoods_of_interest(connectivity, ['invalid_neighborhood']) - + def test_generate_zero_sparse_connectivity(self): """Test generate_zero_sparse_connectivity.""" out = generate_zero_sparse_connectivity(10, 10) assert out.shape == (10, 10) assert torch.sum(out) == 0 - + def test_load_cell_complex_dataset(self): """Test load_cell_complex_dataset.""" with pytest.raises(NotImplementedError) as e: load_cell_complex_dataset({}) - + def test_load_simplicial_dataset(self): """Test load_simplicial_dataset.""" with pytest.raises(NotImplementedError) as e: load_simplicial_dataset({}) - + def test_load_manual_graph(self): """Test load_manual_graph.""" out = load_manual_graph() assert isinstance(out, torch_geometric.data.Data) - + def test_make_hash(self): """Test make_hash.""" out = make_hash('test') assert isinstance(out, int) - + def test_ensure_serializable(self): """Test ensure_serializable.""" objects = ['test', 1, 1.0, [1, 2, 3], {'a': 1, 'b': 2}, set([1, 2, 3]), omegaconf.dictconfig.DictConfig({'a': 1, 'b': 2}), torch_geometric.data.Data()] diff --git a/test/data/utils/test_split_utils.py b/test/data/utils/test_split_utils.py index 1453aafbb..3fac08edd 100644 --- a/test/data/utils/test_split_utils.py +++ b/test/data/utils/test_split_utils.py @@ -25,7 +25,7 @@ def setup_method(self): """Setup method for each test.""" # Create temporary directory for test splits self.test_dir = tempfile.mkdtemp() - + def teardown_method(self): """Cleanup after each test.""" if os.path.exists(self.test_dir): @@ -33,7 +33,7 @@ def teardown_method(self): def create_mock_dataset(self, n_graphs, label_shapes, has_get_data_dir=True): """Create a mock dataset with specified label shapes. - + Parameters ---------- n_graphs : int @@ -42,7 +42,7 @@ def create_mock_dataset(self, n_graphs, label_shapes, has_get_data_dir=True): List of tuples representing label shapes for each graph. has_get_data_dir : bool Whether the dataset has get_data_dir method. - + Returns ------- MagicMock @@ -50,7 +50,7 @@ def create_mock_dataset(self, n_graphs, label_shapes, has_get_data_dir=True): """ mock_dataset = MagicMock() mock_dataset.__len__ = MagicMock(return_value=n_graphs) - + # Create mock graphs with different label shapes mock_graphs = [] for i, shape in enumerate(label_shapes): @@ -62,16 +62,16 @@ def create_mock_dataset(self, n_graphs, label_shapes, has_get_data_dir=True): labels = np.random.randint(0, 3, size=shape) mock_graph.y.squeeze.return_value.numpy.return_value = labels mock_graphs.append(mock_graph) - + mock_dataset.__getitem__ = lambda self, idx: mock_graphs[idx] mock_dataset.__iter__ = lambda self: iter(mock_graphs) - + # Setup dataset.dataset.get_data_dir() if has_get_data_dir: mock_dataset.dataset.get_data_dir.return_value = self.test_dir else: mock_dataset.dataset = MagicMock(spec=[]) - + return mock_dataset def test_uniform_label_shapes_random_split(self): @@ -80,21 +80,21 @@ def test_uniform_label_shapes_random_split(self): n_graphs = 20 label_shapes = [()] * n_graphs # All single labels mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + # Verify splits exist and are non-empty assert len(train_ds) > 0 assert len(val_ds) > 0 assert len(test_ds) > 0 - + # Verify total equals original assert len(train_ds) + len(val_ds) + len(test_ds) == n_graphs @@ -103,16 +103,16 @@ def test_uniform_label_shapes_kfold_split(self): n_graphs = 20 label_shapes = [()] * n_graphs mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "k-fold", "data_seed": 0, "k": 5, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + assert len(train_ds) > 0 assert len(val_ds) > 0 # Note: test_ds and val_ds are the same in k-fold (test=valid) @@ -124,16 +124,16 @@ def test_ragged_label_shapes_random_split(self): n_graphs = 15 label_shapes = [()] * 5 + [(2,)] * 5 + [(3,)] * 5 # Mix of shapes mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + assert len(train_ds) > 0 assert len(val_ds) > 0 assert len(test_ds) > 0 @@ -144,14 +144,14 @@ def test_ragged_label_shapes_kfold_raises_error(self): n_graphs = 15 label_shapes = [()] * 5 + [(2,)] * 5 + [(3,)] * 5 mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "k-fold", "data_seed": 0, "k": 5, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + # Should raise assertion error for ragged labels with k-fold with pytest.raises((AssertionError, ValueError)): # Could be AssertionError from the check or ValueError from sklearn @@ -162,7 +162,7 @@ def test_fixed_split_type(self): n_graphs = 20 label_shapes = [()] * n_graphs mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + # Add split_idx attribute split_idx = { "train": np.arange(12), @@ -170,14 +170,14 @@ def test_fixed_split_type(self): "test": np.arange(16, 20) } mock_dataset.split_idx = split_idx - + parameters = DictConfig({ "split_type": "fixed", "data_seed": 0, }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + assert len(train_ds) == 12 assert len(val_ds) == 4 assert len(test_ds) == 4 @@ -190,12 +190,12 @@ def test_fixed_split_type_without_split_idx_raises_error(self): # Ensure split_idx attribute doesn't exist if hasattr(mock_dataset, 'split_idx'): delattr(mock_dataset, 'split_idx') - + parameters = DictConfig({ "split_type": "fixed", "data_seed": 0, }) - + with pytest.raises(NotImplementedError): load_inductive_splits(mock_dataset, parameters) @@ -204,12 +204,12 @@ def test_invalid_split_type_raises_error(self): n_graphs = 20 label_shapes = [()] * n_graphs mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "invalid_split_type", "data_seed": 0, }) - + with pytest.raises(NotImplementedError, match="not valid"): load_inductive_splits(mock_dataset, parameters) @@ -218,14 +218,14 @@ def test_single_graph_raises_assertion(self): n_graphs = 1 label_shapes = [()] mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + with pytest.raises(AssertionError, match="more than one graph"): load_inductive_splits(mock_dataset, parameters) @@ -234,17 +234,17 @@ def test_without_get_data_dir(self): n_graphs = 20 label_shapes = [()] * n_graphs mock_dataset = self.create_mock_dataset(n_graphs, label_shapes, has_get_data_dir=False) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + # Should work fine without get_data_dir train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + assert len(train_ds) > 0 assert len(val_ds) > 0 assert len(test_ds) > 0 @@ -254,16 +254,16 @@ def test_masks_are_assigned_correctly(self): n_graphs = 10 label_shapes = [()] * n_graphs mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + # Check that masks are properly assigned to the data_lst items for i in range(len(train_ds.data_lst)): graph = train_ds.data_lst[i] @@ -273,13 +273,13 @@ def test_masks_are_assigned_correctly(self): assert graph.train_mask.item() == 1 assert graph.val_mask.item() == 0 assert graph.test_mask.item() == 0 - + for i in range(len(val_ds.data_lst)): graph = val_ds.data_lst[i] assert graph.train_mask.item() == 0 assert graph.val_mask.item() == 1 assert graph.test_mask.item() == 0 - + for i in range(len(test_ds.data_lst)): graph = test_ds.data_lst[i] assert graph.train_mask.item() == 0 @@ -290,7 +290,7 @@ def test_different_data_seeds_produce_different_splits(self): """Test that different data seeds produce different splits.""" n_graphs = 20 label_shapes = [()] * n_graphs - + # First split mock_dataset1 = self.create_mock_dataset(n_graphs, label_shapes) parameters1 = DictConfig({ @@ -300,7 +300,7 @@ def test_different_data_seeds_produce_different_splits(self): "data_split_dir": os.path.join(self.test_dir, "data_splits") }) train_ds1, _, _ = load_inductive_splits(mock_dataset1, parameters1) - + # Second split with different seed mock_dataset2 = self.create_mock_dataset(n_graphs, label_shapes) parameters2 = DictConfig({ @@ -310,7 +310,7 @@ def test_different_data_seeds_produce_different_splits(self): "data_split_dir": os.path.join(self.test_dir, "data_splits") }) train_ds2, _, _ = load_inductive_splits(mock_dataset2, parameters2) - + # Splits should have same size but potentially different composition assert len(train_ds1) == len(train_ds2) @@ -320,16 +320,16 @@ def test_multidimensional_ragged_labels(self): # Mix of different multidimensional shapes label_shapes = [(5,)] * 4 + [(10,)] * 4 + [(15,)] * 4 mock_dataset = self.create_mock_dataset(n_graphs, label_shapes) - + parameters = DictConfig({ "split_type": "random", "data_seed": 0, "train_prop": 0.5, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + train_ds, val_ds, test_ds = load_inductive_splits(mock_dataset, parameters) - + assert len(train_ds) > 0 assert len(val_ds) > 0 assert len(test_ds) > 0 @@ -338,11 +338,11 @@ def test_multidimensional_ragged_labels(self): class TestKFoldSplit: """Test k_fold_split function.""" - + def setup_method(self): """Setup method for each test.""" self.test_dir = tempfile.mkdtemp() - + def teardown_method(self): """Cleanup after each test.""" if os.path.exists(self.test_dir): @@ -357,9 +357,9 @@ def test_basic_kfold(self): "data_seed": 0, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + split_idx = k_fold_split(labels, parameters) - + assert "train" in split_idx assert "valid" in split_idx assert "test" in split_idx @@ -370,15 +370,15 @@ def test_kfold_with_root_override(self): labels = np.array([0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2] * 2) # 30 samples custom_root = os.path.join(self.test_dir, "custom") os.makedirs(custom_root, exist_ok=True) - + parameters = DictConfig({ "k": 5, "data_seed": 0, "data_split_dir": "original_dir" # Should be ignored }) - + split_idx = k_fold_split(labels, parameters, root=custom_root) - + assert "train" in split_idx # Check that split was saved in custom root assert os.path.exists(os.path.join(custom_root, "data_splits", "5-fold")) @@ -386,11 +386,11 @@ def test_kfold_with_root_override(self): class TestRandomSplitting: """Test random_splitting function.""" - + def setup_method(self): """Setup method for each test.""" self.test_dir = tempfile.mkdtemp() - + def teardown_method(self): """Cleanup after each test.""" if os.path.exists(self.test_dir): @@ -404,13 +404,13 @@ def test_basic_random_split(self): "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + split_idx = random_splitting(labels, parameters) - + assert "train" in split_idx assert "valid" in split_idx assert "test" in split_idx - + total = len(split_idx["train"]) + len(split_idx["valid"]) + len(split_idx["test"]) assert total == len(labels) @@ -422,9 +422,9 @@ def test_random_split_proportions(self): "train_prop": 0.7, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + split_idx = random_splitting(labels, parameters) - + train_ratio = len(split_idx["train"]) / len(labels) # Should be approximately 0.7 assert 0.65 < train_ratio < 0.75 @@ -437,9 +437,9 @@ def test_random_split_with_custom_seed(self): "train_prop": 0.6, "data_split_dir": os.path.join(self.test_dir, "data_splits") }) - + split_idx = random_splitting(labels, parameters, global_data_seed=999) - + assert "train" in split_idx # Verify split directory reflects custom seed split_dir = os.path.join(self.test_dir, "data_splits", "train_prop=0.6_global_seed=999") @@ -448,7 +448,7 @@ def test_random_split_with_custom_seed(self): class TestAssignMasks: """Test assign_train_val_test_mask_to_graphs function.""" - + def test_assign_masks(self): """Test mask assignment to graphs.""" # Create mock graphs @@ -456,20 +456,20 @@ def test_assign_masks(self): for i in range(10): graph = MagicMock() mock_graphs.append(graph) - + mock_dataset = MagicMock() mock_dataset.__getitem__ = lambda self, idx: mock_graphs[idx] - + split_idx = { "train": np.array([0, 1, 2, 3, 4]), "valid": np.array([5, 6, 7]), "test": np.array([8, 9]) } - + train_ds, val_ds, test_ds = assign_train_val_test_mask_to_graphs( mock_dataset, split_idx ) - + assert len(train_ds) == 5 assert len(val_ds) == 3 assert len(test_ds) == 2 diff --git a/test/evaluator/test_evaluator.py b/test/evaluator/test_evaluator.py index 270336214..783303fb0 100644 --- a/test/evaluator/test_evaluator.py +++ b/test/evaluator/test_evaluator.py @@ -11,14 +11,14 @@ def setup_method(self): """Setup the test.""" self.classification_metrics = ["accuracy", "precision", "recall", "auroc"] self.evaluator_classification = TBEvaluator( - task="classification", - num_classes=3, + task="classification", + num_classes=3, metrics=self.classification_metrics ) self.regression_metrics = ["example", "mae", "mse", "rmse", "r2"] self.evaluator_regression = TBEvaluator( - task="regression", - num_classes=1, + task="regression", + num_classes=1, metrics=self.regression_metrics ) @@ -27,7 +27,7 @@ def test_repr(self): repr_str = self.evaluator_classification.__repr__() assert "TBEvaluator" in repr_str assert "classification" in repr_str - + repr_str = self.evaluator_regression.__repr__() assert "TBEvaluator" in repr_str assert "regression" in repr_str @@ -37,14 +37,14 @@ def test_classification_update_and_compute(self): # Create deterministic data for testing logits = torch.tensor([[2.0, 0.1, 0.1], [0.1, 2.0, 0.1], [0.1, 0.1, 2.0]]) labels = torch.tensor([0, 1, 2]) - + self.evaluator_classification.update({"logits": logits, "labels": labels}) out = self.evaluator_classification.compute() - + # Check all metrics are present for metric in self.classification_metrics: assert metric in out, f"Metric {metric} not found in output" - + # Check accuracy is 1.0 (perfect prediction) assert out["accuracy"] == pytest.approx(1.0, abs=0.01) @@ -53,14 +53,14 @@ def test_regression_update_and_compute(self): # Create deterministic data logits = torch.tensor([[1.0], [2.0], [3.0]]) labels = torch.tensor([1.1, 2.1, 3.1]) - + self.evaluator_regression.update({"logits": logits, "labels": labels}) out = self.evaluator_regression.compute() - + # Check all metrics are present for metric in self.regression_metrics: assert metric in out, f"Metric {metric} not found in output" - + # MAE should be approximately 0.1 assert out["mae"] < 0.2 @@ -69,7 +69,7 @@ def test_regression_with_different_shapes(self): # Test with 2D predictions (required shape for regression) logits = torch.randn(10, 1) labels = torch.randn(10) - + self.evaluator_regression.update({"logits": logits, "labels": labels}) out = self.evaluator_regression.compute() assert "mae" in out @@ -81,13 +81,13 @@ def test_binary_classification(self): num_classes=2, metrics=["accuracy", "f1", "precision", "recall"] ) - + logits = torch.tensor([[2.0, 0.1], [0.1, 2.0], [2.0, 0.1]]) labels = torch.tensor([0, 1, 0]) - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + assert out["accuracy"] == pytest.approx(1.0, abs=0.01) def test_multiclass_classification(self): @@ -97,13 +97,13 @@ def test_multiclass_classification(self): num_classes=5, metrics=["accuracy", "f1_macro", "f1_weighted"] ) - + logits = torch.randn(20, 5) labels = torch.randint(0, 5, (20,)) - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + assert "accuracy" in out assert "f1_macro" in out assert "f1_weighted" in out @@ -115,13 +115,13 @@ def test_confusion_matrix_metric(self): num_classes=3, metrics=["accuracy", "confusion_matrix"] ) - + logits = torch.tensor([[2.0, 0.1, 0.1], [0.1, 2.0, 0.1], [0.1, 0.1, 2.0]]) labels = torch.tensor([0, 1, 2]) - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + assert "confusion_matrix" in out # Should be identity matrix for perfect prediction cm = out["confusion_matrix"] @@ -139,7 +139,7 @@ def test_multilabel_not_implemented(self): num_classes=3, metrics=["accuracy"] ) - + with pytest.raises(NotImplementedError, match="Multilabel classification"): evaluator.update({ "logits": torch.tensor([[1, 0, 0], [0, 1, 1]]), @@ -154,17 +154,17 @@ def test_reset(self): "labels": torch.randn(10) }) out_before = self.evaluator_regression.compute() - + # Reset self.evaluator_regression.reset() - + # Update with new data self.evaluator_regression.update({ "logits": torch.randn(10, 1), "labels": torch.randn(10) }) out_after = self.evaluator_regression.compute() - + # Results should be different (computed on different data) assert out_before["mae"] != out_after["mae"] @@ -176,27 +176,27 @@ def test_rmse_configuration(self): num_classes=1, metrics=["mse", "rmse"] ) - + # Create data where we know MSE and RMSE # If predictions = [1, 2, 3] and labels = [1, 2, 3], both should be 0 logits = torch.tensor([[1.0], [2.0], [3.0]]) labels = torch.tensor([1.0, 2.0, 3.0]) - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + # Both should be very close to 0 assert out["mse"] < 0.01 assert out["rmse"] < 0.01 - + # For non-perfect predictions evaluator.reset() logits = torch.tensor([[1.0], [2.0]]) labels = torch.tensor([2.0, 3.0]) # Off by 1 each - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + # MSE should be 1.0, RMSE should also be 1.0 assert out["mse"] == pytest.approx(1.0, abs=0.01) assert out["rmse"] == pytest.approx(1.0, abs=0.01) @@ -208,13 +208,13 @@ def test_f1_variants(self): num_classes=3, metrics=["f1", "f1_macro", "f1_weighted"] ) - + logits = torch.randn(30, 3) labels = torch.randint(0, 3, (30,)) - + evaluator.update({"logits": logits, "labels": labels}) out = evaluator.compute() - + assert "f1" in out assert "f1_macro" in out assert "f1_weighted" in out @@ -226,13 +226,13 @@ def test_multiple_updates_before_compute(self): num_classes=2, metrics=["accuracy"] ) - + # Multiple updates (accumulating) for _ in range(5): logits = torch.randn(10, 2) labels = torch.randint(0, 2, (10,)) evaluator.update({"logits": logits, "labels": labels}) - + # Compute accumulated metrics out = evaluator.compute() assert "accuracy" in out diff --git a/test/loss/test_dataset_loss.py b/test/loss/test_dataset_loss.py index 00172a0b0..3f8e298d3 100644 --- a/test/loss/test_dataset_loss.py +++ b/test/loss/test_dataset_loss.py @@ -7,7 +7,7 @@ class TestDatasetLoss: """ Test the TBEvaluator class.""" - + def setup_method(self): """ Setup the test.""" dataset_loss = {"task": "classification", "loss_type": "cross_entropy"} @@ -18,7 +18,7 @@ def setup_method(self): self.dataset3 = DatasetLoss(dataset_loss) dataset_loss = {"task": "multilabel classification", "loss_type": "BCE"} self.dataset4 = DatasetLoss(dataset_loss) - + dataset_loss = {"task": "wrong", "loss_type": "wrong"} with pytest.raises(Exception): DatasetLoss(dataset_loss) @@ -28,19 +28,19 @@ def setup_method(self): repr = self.dataset1.__repr__() assert repr == "DatasetLoss(task=classification, loss_type=cross_entropy)" - + def test_forward(self): """ Test the forward method.""" batch = torch_geometric.data.Data() - + model_out = {"logits": torch.tensor([0.1, 0.2, 0.3]), "labels": torch.tensor([0.1, 0.2, 0.3])} out = self.dataset1.forward(model_out, batch) assert out.item() >= 0 - + model_out = {"logits": torch.tensor([0.1, 0.2, 0.3]), "labels": torch.tensor([0.1, 0.2, 0.3])} out = self.dataset3.forward(model_out, batch) assert out.item() >= 0 - + model_out = {"logits": torch.tensor([[0.1, 0.2, 0.3], [0.1, 0.2, 0.3]]), "labels": torch.tensor([[0.1, float('nan'), 0.3], [0.1, 0.2, float('nan')]])} out = self.dataset4.forward(model_out, batch) assert out.item() >= 0 @@ -48,6 +48,3 @@ def test_forward(self): self.dataset5.task = 'not defined' with pytest.raises(Exception): self.dataset5(model_out, batch) - - - \ No newline at end of file diff --git a/test/loss/test_dgm_loss.py b/test/loss/test_dgm_loss.py index d5ac5e77a..e1736198d 100644 --- a/test/loss/test_dgm_loss.py +++ b/test/loss/test_dgm_loss.py @@ -10,7 +10,7 @@ def mock_batch(): """ Create a mock batch of data for testing. - + Returns ------- MagicMock @@ -31,7 +31,7 @@ def mock_batch(): def mock_model_out(): """ Create a mock model output for testing. - + Returns ------- dict @@ -45,8 +45,8 @@ def mock_model_out(): def test_dgm_loss_init(): """ Test the initialization of the DGMLoss class. - - This function tests the DGMLoss class to ensure that it initializes correctly + + This function tests the DGMLoss class to ensure that it initializes correctly with the default loss weight and that the average accuracy is set to None. """ loss_fn = DGMLoss(loss_weight=0.7) @@ -69,7 +69,7 @@ def test_dgm_loss_forward(mock_batch, mock_model_out): mock_model_out : torch.Tensor A mock output from the model. """ - + loss_fn = DGMLoss() loss = loss_fn.forward(mock_model_out, mock_batch) assert isinstance(loss, torch.Tensor) @@ -81,15 +81,15 @@ def test_dgm_loss_forward_with_different_masks(mock_batch, mock_model_out): """ Test the DGMLoss forward method with different model states. - This function tests the `DGMLoss` forward method using different - model states (Training, Validation, and Test) to ensure that the + This function tests the `DGMLoss` forward method using different + model states (Training, Validation, and Test) to ensure that the loss is computed correctly and returns a tensor in each case. Parameters ---------- mock_batch : Mock - A mock object representing the batch input to the model, - with an attribute `model_state` that can be set to different + A mock object representing the batch input to the model, + with an attribute `model_state` that can be set to different states (Training, Validation, Test). mock_model_out : Mock A mock object representing the output of the model. @@ -117,4 +117,4 @@ def test_dgm_loss_forward_with_different_masks(mock_batch, mock_model_out): assert isinstance(loss, torch.Tensor) if __name__ == "__main__": - pytest.main() \ No newline at end of file + pytest.main() diff --git a/test/nn/__init__.py b/test/nn/__init__.py index 9480a16d0..35c852e0a 100644 --- a/test/nn/__init__.py +++ b/test/nn/__init__.py @@ -1 +1 @@ -"""Init file for nn.""" \ No newline at end of file +"""Init file for nn.""" diff --git a/test/nn/backbones/__init__.py b/test/nn/backbones/__init__.py index e85366e0d..57968cc77 100644 --- a/test/nn/backbones/__init__.py +++ b/test/nn/backbones/__init__.py @@ -1 +1 @@ -"""Init file for nn.backbones.""" \ No newline at end of file +"""Init file for nn.backbones.""" diff --git a/test/nn/backbones/cell/__init__.py b/test/nn/backbones/cell/__init__.py index 9cce41e7d..c7209d0c6 100644 --- a/test/nn/backbones/cell/__init__.py +++ b/test/nn/backbones/cell/__init__.py @@ -1 +1 @@ -"""Init file for cell backbones.""" \ No newline at end of file +"""Init file for cell backbones.""" diff --git a/test/nn/backbones/cell/test_cccn.py b/test/nn/backbones/cell/test_cccn.py index 7ae110d24..cd20e27d7 100644 --- a/test/nn/backbones/cell/test_cccn.py +++ b/test/nn/backbones/cell/test_cccn.py @@ -7,7 +7,7 @@ def test_cccn(random_graph_input): """Test CCCN. - + Parameters ---------- random_graph_input : tuple @@ -16,9 +16,9 @@ def test_cccn(random_graph_input): x, x_1, x_2, edges_1, edges_2 = random_graph_input auto_test = NNModuleAutoTest([ { - "module" : CCCN, + "module" : CCCN, "init": (x.shape[1], ), "forward": (x, edges_1, edges_2), "assert_shape": x.shape } - ]) \ No newline at end of file + ]) diff --git a/test/nn/backbones/combinatorial/test_gccn.py b/test/nn/backbones/combinatorial/test_gccn.py index 3a0dcb324..9747a0728 100644 --- a/test/nn/backbones/combinatorial/test_gccn.py +++ b/test/nn/backbones/combinatorial/test_gccn.py @@ -56,7 +56,7 @@ def create_mock_complex_batch(): x_0 = torch.randn(3, 16) # 3 nodes x_1 = torch.randn(3, 16) # 3 edges x_2 = torch.randn(1, 16) # 1 face - + batch = Data(x_0=x_0, x_1=x_1, x_2=x_2) # Incidence matrices @@ -100,7 +100,7 @@ def create_mock_complex_batch(): ).coalesce() batch["up_adjacency-2"] = adjacency_2 - cell_statistics = torch.tensor([[3, 3, 1]]) + cell_statistics = torch.tensor([[3, 3, 1]]) batch["cell_statistics"] = cell_statistics return batch @@ -147,7 +147,7 @@ def test_topotune(): batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) neighborhoods = OmegaConf.create(["up_adjacency-0", "up_adjacency-1", "down_incidence-1", "down_incidence-2"])#[[[0, 0], "adjacency"], [[1, 1], "adjacency"], [[1, 0], "boundary"], [[2, 1], "boundary"]]) - + auto_test = ModifiedNNModuleAutoTest([ { "module": TopoTune, @@ -191,7 +191,7 @@ def test_topotune_methods(): # Test intrarank_gnn_forward output = topotune.intrarank_gnn_forward(expanded, 0, 0) - assert output.shape == (3, 16) + assert output.shape == (3, 16) # Test interrank_expand membership = topotune.generate_membership_vectors(batch) @@ -202,7 +202,7 @@ def test_topotune_methods(): # Test interrank_gnn_forward output = topotune.interrank_gnn_forward(expanded, 0, 0, 3) - assert output.shape == (3, 16) + assert output.shape == (3, 16) # Test aggregate_inter_nbhd x_out_per_route = {0: torch.randn(3, 16), 1: torch.randn(3, 16)} @@ -215,9 +215,9 @@ def test_interrank_boundary_index(): x_src = torch.randn(15, 16) boundary_index = [torch.randint(0, 10, (30,)), torch.randint(0, 15, (30,))] n_dst_nodes = 10 - + edge_index, edge_attr = interrank_boundary_index(x_src, boundary_index, n_dst_nodes) - + assert edge_index.shape == (2, 30) assert edge_attr.shape == (30, 16) @@ -225,10 +225,10 @@ def test_get_activation(): """Test the get_activation function.""" relu_func = get_activation("relu") assert callable(relu_func) - + relu_module = get_activation("relu", return_module=True) assert issubclass(relu_module, torch.nn.Module) - + with pytest.raises(NotImplementedError): get_activation("invalid_activation") @@ -245,7 +245,7 @@ def test_topotune_different_activations(activation): """ batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) - + neighborhoods = OmegaConf.create(["up_adjacency-0", "down_incidence-1"]) model = TopoTune( GNN=gnn, @@ -271,7 +271,7 @@ def test_topotune_use_edge_attr_true(): """ batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) - + # Add more complex neighborhoods to ensure both interrank and intrarank expansions neighborhoods = OmegaConf.create([ "up_adjacency-0", # intrarank route rank=0->0 @@ -305,7 +305,7 @@ def test_topotune_single_node_per_rank(): # Create a batch with just 1 node, 1 edge, 1 face batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) - + neighborhoods = OmegaConf.create(["up_adjacency-0", "down_incidence-1"]) model = TopoTune( GNN=gnn, @@ -329,7 +329,7 @@ def test_topotune_multiple_layers(): """ batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) - + neighborhoods = OmegaConf.create(["up_adjacency-0", "down_incidence-1"]) model = TopoTune( GNN=gnn, @@ -376,4 +376,3 @@ def test_topotune_src_rank_larger_than_dst_rank(): for rank in [0, 1, 2]: assert rank in output assert output[rank].shape == getattr(batch, f"x_{rank}").shape - diff --git a/test/nn/backbones/combinatorial/test_gccn_onehasse.py b/test/nn/backbones/combinatorial/test_gccn_onehasse.py index efe0f6bf8..9e0ef20ae 100644 --- a/test/nn/backbones/combinatorial/test_gccn_onehasse.py +++ b/test/nn/backbones/combinatorial/test_gccn_onehasse.py @@ -43,7 +43,7 @@ def forward(self, x, edge_index): Output of the GCN layer. """ return self.conv(x, edge_index) - + class MockGNNWithLinear(MockGNN): """ @@ -92,7 +92,7 @@ def create_mock_complex_batch(): x_0 = torch.randn(3, 16) # 3 nodes x_1 = torch.randn(3, 16) # 3 edges x_2 = torch.randn(1, 16) # 1 face - + batch = Data(x_0=x_0, x_1=x_1, x_2=x_2) # Incidence matrices @@ -136,7 +136,7 @@ def create_mock_complex_batch(): ).coalesce() batch["up_adjacency-2"] = adjacency_2 - cell_statistics = torch.tensor([[3, 3, 1]]) + cell_statistics = torch.tensor([[3, 3, 1]]) batch["cell_statistics"] = cell_statistics return batch @@ -183,7 +183,7 @@ def test_topotune_onehasse(): batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) neighborhoods = OmegaConf.create(["up_adjacency-0", "up_adjacency-1", "down_incidence-1", "down_incidence-2"])#[[[0, 0], "adjacency"], [[1, 1], "adjacency"], [[1, 0], "boundary"], [[2, 1], "boundary"]]) - + auto_test = ModifiedNNModuleAutoTest([ { "module": TopoTune_OneHasse, @@ -247,10 +247,10 @@ def test_get_activation(): """Test the get_activation function.""" relu_func = get_activation("relu") assert callable(relu_func) - + relu_module = get_activation("relu", return_module=True) assert issubclass(relu_module, torch.nn.Module) - + with pytest.raises(NotImplementedError): get_activation("invalid_activation") @@ -262,10 +262,10 @@ def test_topotune_onehasse_early_return_x2_zero(): batch = create_mock_complex_batch() batch.x_2 = torch.zeros((0, 16)) # Force x_2 to have 0 faces gnn = MockGNN(16, 32, 16) - + # Define any neighborhoods; they won't matter since x_2=0 triggers early return neighborhoods = OmegaConf.create(["up_adjacency-0", "down_incidence-2"]) - + model = TopoTune_OneHasse( GNN=gnn, neighborhoods=neighborhoods, @@ -333,7 +333,7 @@ def test_topotune_onehasse_unsupported_src_rank_raises(bad_neighborhood, expecte """ batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) - + neighborhoods = OmegaConf.create([bad_neighborhood]) model = TopoTune_OneHasse( GNN=gnn, @@ -359,7 +359,7 @@ def test_topotune_onehasse_indexerror_in_aggregate_inter_nbhd(mocker): batch = create_mock_complex_batch() gnn = MockGNN(16, 32, 16) neighborhoods = OmegaConf.create(["up_adjacency-0", "down_incidence-1"]) - + model = TopoTune_OneHasse( GNN=gnn, neighborhoods=neighborhoods, @@ -381,7 +381,7 @@ def fake_generate_membership_vectors(b): ---------- b : torch_geometric.data.Data The input batch data. - + Returns ------- dict of {int: torch.Tensor} @@ -400,9 +400,9 @@ def fake_generate_membership_vectors(b): def create_special_batch(): """ Create a batch with shapes adjusted to trigger certain corner cases. - + For instance: - - 2 faces (x_2 of size [2, *]) + - 2 faces (x_2 of size [2, *]) - Non-square adjacency or incidence to see if it leads to certain expansions or error-handling in all_nbhds_expand. @@ -562,4 +562,4 @@ def test_activation_id(): # The identity activation should result in no nonlinearity being applied # beyond the raw linear transform. for rank_id in [0, 1, 2]: - assert rank_id in out \ No newline at end of file + assert rank_id in out diff --git a/test/nn/backbones/graph/__init__.py b/test/nn/backbones/graph/__init__.py index 2bbda54e8..db251c98c 100644 --- a/test/nn/backbones/graph/__init__.py +++ b/test/nn/backbones/graph/__init__.py @@ -1 +1 @@ -"""Init file for custom graph models.""" \ No newline at end of file +"""Init file for custom graph models.""" diff --git a/test/nn/backbones/graph/test_gps.py b/test/nn/backbones/graph/test_gps.py index f615f09eb..2807bcbb7 100644 --- a/test/nn/backbones/graph/test_gps.py +++ b/test/nn/backbones/graph/test_gps.py @@ -24,7 +24,7 @@ def test_initialization(self): num_layers=2, attn_type="multihead" ) - + redraw = RedrawProjection(model, redraw_interval=10) assert redraw.model == model assert redraw.redraw_interval == 10 @@ -37,7 +37,7 @@ def test_initialization_no_interval(self): hidden_dim=self.hidden_dim, num_layers=2 ) - + redraw = RedrawProjection(model, redraw_interval=None) assert redraw.redraw_interval is None assert redraw.num_last_redraw == 0 @@ -51,12 +51,12 @@ def test_redraw_projections_not_training(self): attn_type="performer" ) model.eval() - + redraw = RedrawProjection(model, redraw_interval=1) initial_count = redraw.num_last_redraw - + redraw.redraw_projections() - + # Count should not change when not training assert redraw.num_last_redraw == initial_count @@ -68,12 +68,12 @@ def test_redraw_projections_no_interval(self): num_layers=2 ) model.train() - + redraw = RedrawProjection(model, redraw_interval=None) initial_count = redraw.num_last_redraw - + redraw.redraw_projections() - + assert redraw.num_last_redraw == initial_count def test_redraw_projections_increment(self): @@ -85,9 +85,9 @@ def test_redraw_projections_increment(self): attn_type="performer" ) model.train() - + redraw = RedrawProjection(model, redraw_interval=5) - + # Call multiple times for i in range(4): redraw.redraw_projections() @@ -106,14 +106,14 @@ def setup_method(self): def _prepare_features(self, num_nodes, feat_dim=None): """Helper to create features matching the model's expected dimension. - + Parameters ---------- num_nodes : int Number of nodes in the graph. feat_dim : int, optional Feature dimension. If None, uses self.hidden_dim. - + Returns ------- torch.Tensor @@ -129,7 +129,7 @@ def test_initialization_default(self): input_dim=self.input_dim, hidden_dim=self.hidden_dim ) - + assert model.input_dim == self.input_dim assert model.hidden_dim == self.hidden_dim assert model.num_layers == 4 # default @@ -149,7 +149,7 @@ def test_initialization_custom(self): attn_type="performer", local_conv_type="gin" ) - + assert model.num_layers == 3 assert model.heads == 8 assert model.dropout == 0.2 @@ -164,7 +164,7 @@ def test_initialization_pna_conv(self): num_layers=2, local_conv_type="pna" ) - + assert len(model.convs) == 2 def test_initialization_invalid_conv_type(self): @@ -178,7 +178,7 @@ def test_initialization_invalid_conv_type(self): def test_forward_basic(self, simple_graph_0): """Test basic forward pass. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -186,85 +186,85 @@ def test_forward_basic(self, simple_graph_0): """ # GPS expects input features to match hidden_dim (no initial projection layer) x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not torch.isnan(out).any() assert not torch.isinf(out).any() def test_forward_no_batch(self, simple_graph_0): """Test forward pass without batch vector. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_with_edge_attr(self, simple_graph_0): """Test forward pass with edge attributes. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2, use_edge_attr=True ) - + # Create dummy edge attributes edge_attr = torch.randn(simple_graph_0.edge_index.shape[1], 4) - + out = model( x=x, edge_index=simple_graph_0.edge_index, edge_attr=edge_attr, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_multihead_attention(self, simple_graph_0): """Test forward pass with multihead attention. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -272,25 +272,25 @@ def test_forward_multihead_attention(self, simple_graph_0): heads=4, attn_type="multihead" ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_performer_attention(self, simple_graph_0): """Test forward pass with Performer attention. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -299,77 +299,77 @@ def test_forward_performer_attention(self, simple_graph_0): attn_type="performer", redraw_interval=5 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.redraw_projection.redraw_interval == 5 def test_forward_pna_conv(self, simple_graph_0): """Test forward pass with PNA local conv. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2, local_conv_type="pna" ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_num_layers(self, simple_graph_0): """Test forward pass with different number of layers. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for num_layers in [1, 2, 4, 8]: model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=num_layers ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert len(model.convs) == num_layers def test_forward_different_heads(self, simple_graph_0): """Test forward pass with different number of attention heads. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for heads in [1, 2, 4, 8]: model = GPSEncoder( input_dim=self.hidden_dim, @@ -377,25 +377,25 @@ def test_forward_different_heads(self, simple_graph_0): num_layers=2, heads=heads ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_dropout(self, simple_graph_0): """Test forward pass with different dropout rates. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for dropout in [0.0, 0.1, 0.3, 0.5]: model = GPSEncoder( input_dim=self.hidden_dim, @@ -403,25 +403,25 @@ def test_forward_different_dropout(self, simple_graph_0): num_layers=2, dropout=dropout ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_training_mode(self, simple_graph_0): """Test forward pass in training mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -429,26 +429,26 @@ def test_training_mode(self, simple_graph_0): attn_type="performer" ) model.train() - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.training def test_eval_mode(self, simple_graph_0): """Test forward pass in evaluation mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -456,19 +456,19 @@ def test_eval_mode(self, simple_graph_0): attn_type="performer" ) model.eval() - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not model.training def test_backward_pass(self, simple_graph_0): """Test backward pass and gradient computation. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -479,18 +479,18 @@ def test_backward_pass(self, simple_graph_0): hidden_dim=self.hidden_dim, num_layers=2 ) - + x = self._prepare_features(simple_graph_0.num_nodes).requires_grad_(True) out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + # Compute loss and backward loss = out.sum() loss.backward() - + # Check that gradients exist for input (note: GPS has a bug where it doesn't properly # propagate through all layers, so we only check that backward() runs without error) assert x.grad is not None @@ -500,7 +500,7 @@ def test_backward_pass(self, simple_graph_0): def test_batched_graphs(self, simple_graph_0, simple_graph_1): """Test forward pass with batched graphs. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -510,22 +510,22 @@ def test_batched_graphs(self, simple_graph_0, simple_graph_1): """ expected_nodes = simple_graph_0.num_nodes + simple_graph_1.num_nodes x = self._prepare_features(expected_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + # Create batch batch_data = Batch.from_data_list([simple_graph_0, simple_graph_1]) - + out = model( x=x, edge_index=batch_data.edge_index, batch=batch_data.batch ) - + assert out.shape == (expected_nodes, self.hidden_dim) def test_empty_graph(self): @@ -537,13 +537,13 @@ def test_empty_graph(self): ) # Set to eval mode to avoid batch norm issues with single node model.eval() - + x = self._prepare_features(1) edge_index = torch.empty((2, 0), dtype=torch.long) batch = torch.zeros(1, dtype=torch.long) - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.shape == (1, self.hidden_dim) def test_large_graph(self): @@ -553,56 +553,56 @@ def test_large_graph(self): hidden_dim=self.hidden_dim, num_layers=2 ) - + num_nodes = 100 num_edges = 300 x = self._prepare_features(num_nodes) edge_index = torch.randint(0, num_nodes, (2, num_edges)) batch = torch.zeros(num_nodes, dtype=torch.long) - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.shape == (num_nodes, self.hidden_dim) def test_attn_kwargs(self, simple_graph_0): """Test forward pass with attention kwargs. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + attn_kwargs = { "dropout": 0.2 } - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2, attn_kwargs=attn_kwargs ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_deterministic_output_eval_mode(self, simple_graph_0): """Test that output is deterministic in eval mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -610,32 +610,32 @@ def test_deterministic_output_eval_mode(self, simple_graph_0): dropout=0.5 ) model.eval() - + # Run forward pass twice out1 = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + out2 = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert torch.allclose(out1, out2) def test_redraw_projection_performer(self, simple_graph_0): """Test that redraw projection works with Performer attention. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, @@ -644,7 +644,7 @@ def test_redraw_projection_performer(self, simple_graph_0): redraw_interval=2 ) model.train() - + # Call forward multiple times for i in range(5): out = model( @@ -656,7 +656,7 @@ def test_redraw_projection_performer(self, simple_graph_0): def test_different_hidden_dims(self, simple_graph_0): """Test with different hidden dimensions. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -664,24 +664,24 @@ def test_different_hidden_dims(self, simple_graph_0): """ for hidden_dim in [16, 32, 64, 128]: x = self._prepare_features(simple_graph_0.num_nodes, hidden_dim) - + model = GPSEncoder( input_dim=hidden_dim, hidden_dim=hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, hidden_dim) def test_model_device_consistency(self, simple_graph_0): """Test that model respects device placement. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -689,27 +689,27 @@ def test_model_device_consistency(self, simple_graph_0): """ if not torch.cuda.is_available(): pytest.skip("CUDA not available") - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2 ) model = model.cuda() - + x = self._prepare_features(simple_graph_0.num_nodes).cuda() edge_index = simple_graph_0.edge_index.cuda() batch = torch.zeros(simple_graph_0.num_nodes, dtype=torch.long).cuda() - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.is_cuda assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) @pytest.mark.parametrize("num_layers", [1, 2, 4, 6]) def test_parametrized_num_layers(self, simple_graph_0, num_layers): """Parametrized test for different number of layers. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -718,26 +718,26 @@ def test_parametrized_num_layers(self, simple_graph_0, num_layers): Number of GPS layers to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=num_layers ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert len(model.convs) == num_layers @pytest.mark.parametrize("attn_type", ["multihead", "performer"]) def test_parametrized_attn_type(self, simple_graph_0, attn_type): """Parametrized test for different attention types. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -746,26 +746,26 @@ def test_parametrized_attn_type(self, simple_graph_0, attn_type): Type of attention mechanism to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2, attn_type=attn_type ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) @pytest.mark.parametrize("local_conv_type", ["gin", "pna"]) def test_parametrized_local_conv(self, simple_graph_0, local_conv_type): """Parametrized test for different local conv types. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -774,38 +774,38 @@ def test_parametrized_local_conv(self, simple_graph_0, local_conv_type): Type of local convolution to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2, local_conv_type=local_conv_type ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_kwargs_ignored(self, simple_graph_0): """Test that additional kwargs are ignored gracefully. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = GPSEncoder( input_dim=self.hidden_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, @@ -813,5 +813,5 @@ def test_kwargs_ignored(self, simple_graph_0): unused_kwarg="test", another_unused=123 ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) diff --git a/test/nn/backbones/graph/test_graphmlp.py b/test/nn/backbones/graph/test_graphmlp.py index 72ce03162..2d43f82c6 100644 --- a/test/nn/backbones/graph/test_graphmlp.py +++ b/test/nn/backbones/graph/test_graphmlp.py @@ -8,7 +8,7 @@ def testGraphMLP(random_graph_input): """ Unit test for GraphMLP. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -19,19 +19,17 @@ def testGraphMLP(random_graph_input): model = GraphMLP(x.shape[1], x.shape[1]) wrapper = GraphMLPWrapper(model, **{"out_channels": x.shape[1], "num_cell_dimensions": 1}) loss_fn = GraphMLPLoss() - + _ = wrapper.__repr__() _ = loss_fn.__repr__() - + model_out = wrapper(batch) assert model_out["x_0"].shape == x.shape assert list(model_out["x_dis"].shape) == [8,8] - + loss = loss_fn(model_out, batch) assert loss.item() >= 0 - + model_out["x_dis"] = None loss = loss_fn(model_out, batch) assert loss == torch.tensor(0.0) - - diff --git a/test/nn/backbones/graph/test_nsd.py b/test/nn/backbones/graph/test_nsd.py index b078af9c4..3baa0647f 100644 --- a/test/nn/backbones/graph/test_nsd.py +++ b/test/nn/backbones/graph/test_nsd.py @@ -19,14 +19,14 @@ def setup_method(self): def _prepare_features(self, num_nodes, feat_dim=None): """Helper to create features matching the model's expected dimension. - + Parameters ---------- num_nodes : int Number of nodes in the graph. feat_dim : int, optional Feature dimension. If None, uses self.input_dim. - + Returns ------- torch.Tensor @@ -42,7 +42,7 @@ def test_initialization_default(self): input_dim=self.input_dim, hidden_dim=self.hidden_dim ) - + assert model.input_dim == self.input_dim assert model.hidden_dim == self.hidden_dim assert model.num_layers == 2 # default @@ -63,7 +63,7 @@ def test_initialization_custom(self): sheaf_act="elu", orth="matrix_exp" ) - + assert model.num_layers == 3 assert model.sheaf_type == "bundle" assert model.d == 4 @@ -78,7 +78,7 @@ def test_initialization_diag_sheaf(self): sheaf_type="diag", d=2 ) - + assert model.sheaf_type == "diag" assert model.d == 2 @@ -91,7 +91,7 @@ def test_initialization_bundle_sheaf(self): sheaf_type="bundle", d=4 ) - + assert model.sheaf_type == "bundle" assert model.d == 4 @@ -104,7 +104,7 @@ def test_initialization_general_sheaf(self): sheaf_type="general", d=3 ) - + assert model.sheaf_type == "general" assert model.d == 3 @@ -137,7 +137,7 @@ def test_initialization_diag_d_validation(self): d=1 ) assert model.d == 1 - + # Should fail with d < 1 with pytest.raises(AssertionError): NSDEncoder( @@ -157,7 +157,7 @@ def test_initialization_bundle_d_validation(self): d=2 ) assert model.d == 2 - + # Should fail with d <= 1 with pytest.raises(AssertionError): NSDEncoder( @@ -177,7 +177,7 @@ def test_initialization_general_d_validation(self): d=2 ) assert model.d == 2 - + # Should fail with d <= 1 with pytest.raises(AssertionError): NSDEncoder( @@ -189,119 +189,119 @@ def test_initialization_general_d_validation(self): def test_forward_basic(self, simple_graph_0): """Test basic forward pass. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not torch.isnan(out).any() assert not torch.isinf(out).any() def test_forward_no_batch(self, simple_graph_0): """Test forward pass without batch vector. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_with_edge_attr(self, simple_graph_0): """Test forward pass with edge attributes (should be ignored). - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + # Create dummy edge attributes (should be ignored by NSD) edge_attr = torch.randn(simple_graph_0.edge_index.shape[1], 4) - + out = model( x=x, edge_index=simple_graph_0.edge_index, edge_attr=edge_attr, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_with_edge_weight(self, simple_graph_0): """Test forward pass with edge weights (should be ignored). - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + # Create dummy edge weights (should be ignored by NSD) edge_weight = torch.randn(simple_graph_0.edge_index.shape[1]) - + out = model( x=x, edge_index=simple_graph_0.edge_index, edge_weight=edge_weight, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_diag_sheaf(self, simple_graph_0): """Test forward pass with diagonal sheaf. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -309,25 +309,25 @@ def test_forward_diag_sheaf(self, simple_graph_0): sheaf_type="diag", d=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_bundle_sheaf(self, simple_graph_0): """Test forward pass with bundle sheaf. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -335,25 +335,25 @@ def test_forward_bundle_sheaf(self, simple_graph_0): sheaf_type="bundle", d=4 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_general_sheaf(self, simple_graph_0): """Test forward pass with general sheaf. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -361,51 +361,51 @@ def test_forward_general_sheaf(self, simple_graph_0): sheaf_type="general", d=3 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_num_layers(self, simple_graph_0): """Test forward pass with different number of layers. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for num_layers in [1, 2, 4, 6]: model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=num_layers ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.num_layers == num_layers def test_forward_different_d_values(self, simple_graph_0): """Test forward pass with different d values. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for d in [2, 4, 8]: model = NSDEncoder( input_dim=self.input_dim, @@ -413,26 +413,26 @@ def test_forward_different_d_values(self, simple_graph_0): num_layers=2, d=d ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.d == d def test_forward_different_dropout(self, simple_graph_0): """Test forward pass with different dropout rates. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for dropout in [0.0, 0.1, 0.3, 0.5]: model = NSDEncoder( input_dim=self.input_dim, @@ -440,25 +440,25 @@ def test_forward_different_dropout(self, simple_graph_0): num_layers=2, dropout=dropout ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_input_dropout(self, simple_graph_0): """Test forward pass with different input dropout rates. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for input_dropout in [0.0, 0.1, 0.3, 0.5]: model = NSDEncoder( input_dim=self.input_dim, @@ -466,25 +466,25 @@ def test_forward_different_input_dropout(self, simple_graph_0): num_layers=2, input_dropout=input_dropout ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_sheaf_activations(self, simple_graph_0): """Test forward pass with different sheaf activations. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for sheaf_act in ["tanh", "elu", "id"]: model = NSDEncoder( input_dim=self.input_dim, @@ -492,25 +492,25 @@ def test_forward_different_sheaf_activations(self, simple_graph_0): num_layers=2, sheaf_act=sheaf_act ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_forward_different_orth_methods(self, simple_graph_0): """Test forward pass with different orthogonalization methods. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + for orth in ["cayley", "matrix_exp"]: model = NSDEncoder( input_dim=self.input_dim, @@ -518,70 +518,70 @@ def test_forward_different_orth_methods(self, simple_graph_0): num_layers=2, orth=orth ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_training_mode(self, simple_graph_0): """Test forward pass in training mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) model.train() - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.training def test_eval_mode(self, simple_graph_0): """Test forward pass in evaluation mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) model.eval() - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not model.training def test_backward_pass(self, simple_graph_0): """Test backward pass and gradient computation. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -592,18 +592,18 @@ def test_backward_pass(self, simple_graph_0): hidden_dim=self.hidden_dim, num_layers=2 ) - + x = self._prepare_features(simple_graph_0.num_nodes).requires_grad_(True) out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + # Compute loss and backward loss = out.sum() loss.backward() - + # Check that gradients exist assert x.grad is not None # At least some parameters should have gradients @@ -612,7 +612,7 @@ def test_backward_pass(self, simple_graph_0): def test_batched_graphs(self, simple_graph_0, simple_graph_1): """Test forward pass with batched graphs. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -622,22 +622,22 @@ def test_batched_graphs(self, simple_graph_0, simple_graph_1): """ expected_nodes = simple_graph_0.num_nodes + simple_graph_1.num_nodes x = self._prepare_features(expected_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + # Create batch batch_data = Batch.from_data_list([simple_graph_0, simple_graph_1]) - + out = model( x=x, edge_index=batch_data.edge_index, batch=batch_data.batch ) - + assert out.shape == (expected_nodes, self.hidden_dim) def test_empty_graph(self): @@ -649,13 +649,13 @@ def test_empty_graph(self): ) # Set to eval mode to avoid batch norm issues with single node model.eval() - + x = self._prepare_features(1) edge_index = torch.empty((2, 0), dtype=torch.long) batch = torch.zeros(1, dtype=torch.long) - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.shape == (1, self.hidden_dim) def test_large_graph(self): @@ -665,11 +665,11 @@ def test_large_graph(self): hidden_dim=self.hidden_dim, num_layers=2 ) - + num_nodes = 100 num_edges = 300 x = self._prepare_features(num_nodes) - + # Create edges in one direction only, then to_undirected will handle making it bidirectional # This ensures proper pairing that NSD expects edge_list = [] @@ -681,32 +681,32 @@ def test_large_graph(self): src, tgt = tgt, src if src != tgt: # Avoid self-loops edge_list.append([src, tgt]) - + # Remove duplicates edge_list = list(set(tuple(e) for e in edge_list)) - + if len(edge_list) > 0: edge_index = torch.tensor(edge_list, dtype=torch.long).t() else: # Fallback: create at least one edge edge_index = torch.tensor([[0], [1]], dtype=torch.long) - + batch = torch.zeros(num_nodes, dtype=torch.long) - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.shape == (num_nodes, self.hidden_dim) def test_deterministic_output_eval_mode(self, simple_graph_0): """Test that output is deterministic in eval mode. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -714,25 +714,25 @@ def test_deterministic_output_eval_mode(self, simple_graph_0): dropout=0.5 ) model.eval() - + # Run forward pass twice out1 = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + out2 = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert torch.allclose(out1, out2) def test_different_hidden_dims(self, simple_graph_0): """Test with different hidden dimensions. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -740,24 +740,24 @@ def test_different_hidden_dims(self, simple_graph_0): """ for hidden_dim in [16, 32, 64, 128]: x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, hidden_dim) def test_different_input_dims(self, simple_graph_0): """Test with different input dimensions. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -765,24 +765,24 @@ def test_different_input_dims(self, simple_graph_0): """ for input_dim in [8, 16, 32, 64]: x = self._prepare_features(simple_graph_0.num_nodes, input_dim) - + model = NSDEncoder( input_dim=input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_model_device_consistency(self, simple_graph_0): """Test that model respects device placement. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -790,7 +790,7 @@ def test_model_device_consistency(self, simple_graph_0): """ if not torch.cuda.is_available(): pytest.skip("CUDA not available") - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -798,20 +798,20 @@ def test_model_device_consistency(self, simple_graph_0): device="cuda" ) model = model.cuda() - + x = self._prepare_features(simple_graph_0.num_nodes).cuda() edge_index = simple_graph_0.edge_index.cuda() batch = torch.zeros(simple_graph_0.num_nodes, dtype=torch.long).cuda() - + out = model(x=x, edge_index=edge_index, batch=batch) - + assert out.is_cuda assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) @pytest.mark.parametrize("num_layers", [1, 2, 4, 6]) def test_parametrized_num_layers(self, simple_graph_0, num_layers): """Parametrized test for different number of layers. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -820,26 +820,26 @@ def test_parametrized_num_layers(self, simple_graph_0, num_layers): Number of NSD layers to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=num_layers ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.num_layers == num_layers @pytest.mark.parametrize("sheaf_type", ["diag", "bundle", "general"]) def test_parametrized_sheaf_type(self, simple_graph_0, sheaf_type): """Parametrized test for different sheaf types. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -848,12 +848,12 @@ def test_parametrized_sheaf_type(self, simple_graph_0, sheaf_type): Type of sheaf to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + # Use d=2 for all types (minimum valid for all) d = 2 if sheaf_type != "diag" else 1 if sheaf_type in ["bundle", "general"]: d = 4 # Use larger d for non-diag types - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -861,20 +861,20 @@ def test_parametrized_sheaf_type(self, simple_graph_0, sheaf_type): sheaf_type=sheaf_type, d=d ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.sheaf_type == sheaf_type @pytest.mark.parametrize("d", [2, 4, 8]) def test_parametrized_d_values(self, simple_graph_0, d): """Parametrized test for different d values. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -883,27 +883,27 @@ def test_parametrized_d_values(self, simple_graph_0, d): Stalk dimension to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2, d=d ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert model.d == d @pytest.mark.parametrize("orth", ["cayley", "matrix_exp"]) def test_parametrized_orth_method(self, simple_graph_0, orth): """Parametrized test for different orthogonalization methods. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -912,38 +912,38 @@ def test_parametrized_orth_method(self, simple_graph_0, orth): Orthogonalization method to test. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2, orth=orth ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_kwargs_ignored(self, simple_graph_0): """Test that additional kwargs are ignored gracefully. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, @@ -951,7 +951,7 @@ def test_kwargs_ignored(self, simple_graph_0): unused_kwarg="test", another_unused=123 ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) def test_get_sheaf_model(self): @@ -961,9 +961,9 @@ def test_get_sheaf_model(self): hidden_dim=self.hidden_dim, num_layers=2 ) - + sheaf_model = model.get_sheaf_model() - + assert sheaf_model is not None assert sheaf_model == model.sheaf_model @@ -979,9 +979,9 @@ def test_sheaf_config_correctness(self): sheaf_act="elu", orth="matrix_exp" ) - + config = model.sheaf_config - + assert config["d"] == 4 assert config["layers"] == 3 assert config["hidden_channels"] == 64 // 4 # hidden_dim // d @@ -994,21 +994,21 @@ def test_sheaf_config_correctness(self): def test_multiple_forward_passes_same_graph(self, simple_graph_0): """Test multiple forward passes on the same graph. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, num_layers=2 ) model.eval() - + # Multiple forward passes for _ in range(5): out = model( @@ -1022,7 +1022,7 @@ def test_multiple_forward_passes_same_graph(self, simple_graph_0): def test_gradient_flow(self, simple_graph_0): """Test that gradients flow through the entire network. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -1034,38 +1034,38 @@ def test_gradient_flow(self, simple_graph_0): num_layers=2 ) model.train() - + x = self._prepare_features(simple_graph_0.num_nodes).requires_grad_(True) out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + # Compute loss loss = out.mean() loss.backward() - + # Check gradients in different parts of the model param_grads = [p.grad for p in model.parameters() if p.grad is not None] assert len(param_grads) > 0, "No parameters have gradients" - + # Check that gradients are not all zeros non_zero_grads = [g for g in param_grads if g.abs().sum() > 0] assert len(non_zero_grads) > 0, "All gradients are zero" def test_bundle_sheaf_with_matrix_exp(self, simple_graph_0): """Test bundle sheaf with matrix_exp orthogonalization. - + This tests the orthogonal.py matrix_exp path (line 39). - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -1074,29 +1074,29 @@ def test_bundle_sheaf_with_matrix_exp(self, simple_graph_0): d=4, orth="matrix_exp" ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not torch.isnan(out).any() assert not torch.isinf(out).any() def test_general_sheaf_with_matrix_exp(self, simple_graph_0): """Test general sheaf with matrix_exp orthogonalization. - + This ensures complete coverage of matrix_exp path in orthogonal.py. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data Test graph fixture. """ x = self._prepare_features(simple_graph_0.num_nodes) - + model = NSDEncoder( input_dim=self.input_dim, hidden_dim=self.hidden_dim, @@ -1105,22 +1105,22 @@ def test_general_sheaf_with_matrix_exp(self, simple_graph_0): d=3, orth="matrix_exp" ) - + out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + assert out.shape == (simple_graph_0.num_nodes, self.hidden_dim) assert not torch.isnan(out).any() assert not torch.isinf(out).any() def test_bundle_sheaf_gradient_flow(self, simple_graph_0): """Test gradient flow through bundle sheaf. - + This tests the bundle laplacian builder normalise method (lines 153-176). - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -1134,18 +1134,18 @@ def test_bundle_sheaf_gradient_flow(self, simple_graph_0): d=4 ) model.train() - + x = self._prepare_features(simple_graph_0.num_nodes).requires_grad_(True) out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + # Compute loss and backward loss = out.sum() loss.backward() - + # Check that gradients exist assert x.grad is not None has_grad = any(param.grad is not None for param in model.parameters() if param.requires_grad) @@ -1153,9 +1153,9 @@ def test_bundle_sheaf_gradient_flow(self, simple_graph_0): def test_general_sheaf_gradient_flow(self, simple_graph_0): """Test gradient flow through general sheaf. - + This ensures coverage of general sheaf laplacian methods. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -1169,18 +1169,18 @@ def test_general_sheaf_gradient_flow(self, simple_graph_0): d=3 ) model.train() - + x = self._prepare_features(simple_graph_0.num_nodes).requires_grad_(True) out = model( x=x, edge_index=simple_graph_0.edge_index, batch=torch.zeros(simple_graph_0.num_nodes, dtype=torch.long) ) - + # Compute loss and backward loss = out.sum() loss.backward() - + # Check that gradients exist assert x.grad is not None has_grad = any(param.grad is not None for param in model.parameters() if param.requires_grad) diff --git a/test/nn/backbones/hypergraph/__init__.py b/test/nn/backbones/hypergraph/__init__.py index 72138ba52..f83740a4d 100644 --- a/test/nn/backbones/hypergraph/__init__.py +++ b/test/nn/backbones/hypergraph/__init__.py @@ -1 +1 @@ -"""Init file for hypergraph backbones.""" \ No newline at end of file +"""Init file for hypergraph backbones.""" diff --git a/test/nn/backbones/hypergraph/test_edgnn.py b/test/nn/backbones/hypergraph/test_edgnn.py index 9d076d4c0..67e80beaa 100644 --- a/test/nn/backbones/hypergraph/test_edgnn.py +++ b/test/nn/backbones/hypergraph/test_edgnn.py @@ -15,7 +15,7 @@ def test_EDGNN(random_graph_input): """ Unit test for EDGNN. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -24,38 +24,38 @@ def test_EDGNN(random_graph_input): x, x_1, x_2, edges_1, edges_2 = random_graph_input auto_test = NNModuleAutoTest([ { - "module" : EDGNN, + "module" : EDGNN, "init": (x.shape[1], ), "forward": (x, edges_1), "assert_shape": x.shape }, ]) auto_test.run() - - indices = torch.nonzero(edges_1, as_tuple=False).T + + indices = torch.nonzero(edges_1, as_tuple=False).T values = edges_1[indices[0], indices[1]] sparse_edges_1 = torch.sparse_coo_tensor(indices, values, edges_1.size()) auto_test2 = NNModuleAutoTest([ { - "module" : EDGNN, - "init": {"num_features": x.shape[1], + "module" : EDGNN, + "init": {"num_features": x.shape[1], "edconv_type": "JumpLink"}, "forward": (x, sparse_edges_1), "assert_shape": x.shape }, ]) auto_test2.run() - + model = EDGNN(x.shape[1], edconv_type="MeanDeg") model.reset_parameters() - + with pytest.raises(ValueError): model = EDGNN(x.shape[1], edconv_type="Invalid") def test_edgnn_MLP(random_graph_input): """ Unit test for edgnn_MLP. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -64,13 +64,13 @@ def test_edgnn_MLP(random_graph_input): x, x_1, x_2, edges_1, edges_2 = random_graph_input hid_channels = 4 out_channels = 10 - + for num_layers in [1, 2, 3]: for Normalization in ['bn', 'ln', 'None']: for InputNorm in [True, False]: auto_test = NNModuleAutoTest([ { - "module" : edgnn_MLP, + "module" : edgnn_MLP, "init": (x.shape[1], hid_channels, out_channels, num_layers, 0.5, Normalization, InputNorm), "forward": (x, ), "assert_shape": (x.shape[0], out_channels) @@ -81,10 +81,10 @@ def test_edgnn_MLP(random_graph_input): model = edgnn_MLP(x.shape[1], hid_channels, out_channels, num_layers, 0.5, 'bn', True) model.reset_parameters() model.flops(x) - + def test_PlainMLP(random_graph_input): """ Unit test for PlainMLP. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -98,20 +98,20 @@ def test_PlainMLP(random_graph_input): auto_test = NNModuleAutoTest([ { - "module" : PlainMLP, + "module" : PlainMLP, "init": (x.shape[1], hid_channels, out_channels, num_layers), "forward": (x, ), "assert_shape": (num_nodes, out_channels) }, ]) auto_test.run() - + model = PlainMLP(x.shape[1], hid_channels, out_channels, num_layers) model.reset_parameters() def test_EquivSetConv(random_graph_input): """ Unit test for EquivSetConv. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -125,7 +125,7 @@ def test_EquivSetConv(random_graph_input): auto_test = NNModuleAutoTest([ { - "module" : EquivSetConv, + "module" : EquivSetConv, "init": (x.shape[1], x.shape[1]), "forward": (x, edges_1[0], edges_1[1], x), "assert_shape": x.shape @@ -136,7 +136,7 @@ def test_EquivSetConv(random_graph_input): def test_JumpLinkConv(random_graph_input): """ Unit test for JumpLinkConv. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -150,7 +150,7 @@ def test_JumpLinkConv(random_graph_input): auto_test = NNModuleAutoTest([ { - "module" : JumpLinkConv, + "module" : JumpLinkConv, "init": (x.shape[1], x.shape[1]), "forward": (x, edges_1[0], edges_1[1], x), "assert_shape": x.shape @@ -160,7 +160,7 @@ def test_JumpLinkConv(random_graph_input): def test_JumpLinkConv(random_graph_input): """ Unit test for JumpLinkConv. - + Parameters ---------- random_graph_input : Tuple[torch.Tensor, torch.Tensor, torch.Tensor, Tuple[torch.Tensor, torch.Tensor], Tuple[torch.Tensor, torch.Tensor]] @@ -174,11 +174,10 @@ def test_JumpLinkConv(random_graph_input): auto_test = NNModuleAutoTest([ { - "module" : JumpLinkConv, + "module" : JumpLinkConv, "init": (x.shape[1], x.shape[1]), "forward": (x, edges_1[0], edges_1[1], x), "assert_shape": x.shape }, ]) auto_test.run() - diff --git a/test/nn/backbones/simplicial/__init__.py b/test/nn/backbones/simplicial/__init__.py index d88d66b1e..f54621b96 100644 --- a/test/nn/backbones/simplicial/__init__.py +++ b/test/nn/backbones/simplicial/__init__.py @@ -1 +1 @@ -"""Init file for simplicial backbones.""" \ No newline at end of file +"""Init file for simplicial backbones.""" diff --git a/test/nn/backbones/simplicial/test_sccnn.py b/test/nn/backbones/simplicial/test_sccnn.py index e0608bea8..c6f425c2b 100644 --- a/test/nn/backbones/simplicial/test_sccnn.py +++ b/test/nn/backbones/simplicial/test_sccnn.py @@ -12,7 +12,7 @@ def test_SCCNNCustom(simple_graph_1): """Test SCCNNCustom. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data @@ -37,7 +37,7 @@ def test_SCCNNCustom(simple_graph_1): auto_test = NNModuleAutoTest([ { - "module" : SCCNNCustom, + "module" : SCCNNCustom, "init": ((data.x.shape[1], data.x_1.shape[1], data.x_2.shape[1]), (out_dim, out_dim, out_dim), conv_order, sc_order), "forward": ((data.x, data.x_1, data.x_2), laplacian_all, incidence_all), "assert_shape": expected_shapes @@ -49,7 +49,7 @@ def test_SCCNNCustom(simple_graph_1): @pytest.fixture def create_sample_data(): """Create sample data for testing. - + Returns ------- dict @@ -59,17 +59,17 @@ def create_sample_data(): x = torch.randn(num_nodes, 3) # 3 node features x_1 = torch.randn(8, 4) # 8 edges with 4 features x_2 = torch.randn(6, 5) # 6 faces with 5 features - + # Create sample Laplacians and incidence matrices hodge_laplacian_0 = torch.sparse_coo_tensor(size=(num_nodes, num_nodes)) down_laplacian_1 = torch.sparse_coo_tensor(size=(8, 8)) up_laplacian_1 = torch.sparse_coo_tensor(size=(8, 8)) down_laplacian_2 = torch.sparse_coo_tensor(size=(6, 6)) up_laplacian_2 = torch.sparse_coo_tensor(size=(6, 6)) - + incidence_1 = torch.sparse_coo_tensor(size=(num_nodes, 8)) incidence_2 = torch.sparse_coo_tensor(size=(8, 6)) - + return { 'x': x, 'x_1': x_1, @@ -82,7 +82,7 @@ def test_sccnn_basic_initialization(): """Test basic initialization of SCCNNCustom.""" in_channels = (3, 4, 5) hidden_channels = (6, 6, 6) - + # Test basic initialization model = SCCNNCustom( in_channels_all=in_channels, @@ -91,7 +91,7 @@ def test_sccnn_basic_initialization(): sc_order=3 ) assert model is not None - + # Verify layer structure assert len(model.layers) == 2 # Default n_layers is 2 assert hasattr(model, 'in_linear_0') @@ -102,7 +102,7 @@ def test_update_functions(): """Test different update functions in the SCCNN.""" in_channels = (3, 4, 5) hidden_channels = (6, 6, 6) - + # Test sigmoid update function model = SCCNNCustom( in_channels_all=in_channels, @@ -112,7 +112,7 @@ def test_update_functions(): update_func="sigmoid" ) assert model is not None - + # Test ReLU update function model = SCCNNCustom( in_channels_all=in_channels, @@ -125,14 +125,14 @@ def test_update_functions(): def test_aggr_norm(create_sample_data): """Test aggregation normalization functionality. - + Parameters ---------- create_sample_data : dict Sample data for testing. """ data = create_sample_data - + model = SCCNNCustom( in_channels_all=(3, 4, 5), hidden_channels_all=(6, 6, 6), @@ -140,14 +140,14 @@ def test_aggr_norm(create_sample_data): sc_order=3, aggr_norm=True ) - + # Forward pass with aggregation normalization output = model( (data['x'], data['x_1'], data['x_2']), data['laplacian_all'], data['incidence_all'] ) - + assert len(output) == 3 assert all(torch.isfinite(out).all() for out in output) @@ -155,7 +155,7 @@ def test_different_conv_orders(): """Test SCCNN with different convolution orders.""" in_channels = (3, 4, 5) hidden_channels = (6, 6, 6) - + # Test with conv_order = 1 model1 = SCCNNCustom( in_channels_all=in_channels, @@ -164,7 +164,7 @@ def test_different_conv_orders(): sc_order=3 ) assert model1 is not None - + # Test with conv_order = 3 model2 = SCCNNCustom( in_channels_all=in_channels, @@ -173,7 +173,7 @@ def test_different_conv_orders(): sc_order=3 ) assert model2 is not None - + # Test invalid conv_order with pytest.raises(AssertionError): model = SCCNNCustom( @@ -187,7 +187,7 @@ def test_different_sc_orders(): """Test SCCNN with different simplicial complex orders.""" in_channels = (3, 4, 5) hidden_channels = (6, 6, 6) - + # Test with sc_order = 2 model1 = SCCNNCustom( in_channels_all=in_channels, @@ -196,7 +196,7 @@ def test_different_sc_orders(): sc_order=2 ) assert model1 is not None - + # Test with sc_order > 2 model2 = SCCNNCustom( in_channels_all=in_channels, @@ -208,27 +208,27 @@ def test_different_sc_orders(): def test_forward_shapes(create_sample_data): """Test output shapes for different input configurations. - + Parameters ---------- create_sample_data : dict Sample data for testing. """ data = create_sample_data - + model = SCCNNCustom( in_channels_all=(3, 4, 5), hidden_channels_all=(6, 6, 6), conv_order=2, sc_order=3 ) - + output = model( (data['x'], data['x_1'], data['x_2']), data['laplacian_all'], data['incidence_all'] ) - + assert output[0].shape == (data['x'].shape[0], 6) assert output[1].shape == (data['x_1'].shape[0], 6) assert output[2].shape == (data['x_2'].shape[0], 6) @@ -237,7 +237,7 @@ def test_n_layers(): """Test SCCNN with different numbers of layers.""" in_channels = (3, 4, 5) hidden_channels = (6, 6, 6) - + # Test with 1 layer model1 = SCCNNCustom( in_channels_all=in_channels, @@ -247,7 +247,7 @@ def test_n_layers(): n_layers=1 ) assert len(model1.layers) == 1 - + # Test with 3 layers model2 = SCCNNCustom( in_channels_all=in_channels, @@ -256,4 +256,4 @@ def test_n_layers(): sc_order=3, n_layers=3 ) - assert len(model2.layers) == 3 \ No newline at end of file + assert len(model2.layers) == 3 diff --git a/test/nn/encoders/test_dgm.py b/test/nn/encoders/test_dgm.py index 4c73d4ccc..aaf160c4b 100644 --- a/test/nn/encoders/test_dgm.py +++ b/test/nn/encoders/test_dgm.py @@ -10,38 +10,38 @@ class TestDGMStructureFeatureEncoder: """Test suite for the DGMStructureFeatureEncoder class. - + This test class covers various aspects of the DGMStructureFeatureEncoder, - including initialization, forward pass, selective encoding, and + including initialization, forward pass, selective encoding, and configuration settings. """ @pytest.fixture def sample_data(self): """Create a sample PyG Data object for testing. - + Returns ------- torch_geometric.data.Data A data object with simulated multi-dimensional features and batch information. """ data = torch_geometric.data.Data() - + # Simulate multi-dimensional features data.x_0 = torch.randn(10, 5) # 10 nodes, 5 features data.x_1 = torch.randn(10, 7) # 10 nodes, 7 features data.x_2 = torch.randn(10, 9) # 10 nodes, 9 features - + # Add batch information data.batch_0 = torch.zeros(10, dtype=torch.long) data.batch_1 = torch.zeros(10, dtype=torch.long) data.batch_2 = torch.zeros(10, dtype=torch.long) - + return data def test_initialization(self, sample_data): """Test encoder initialization with different configurations. - + Parameters ---------- sample_data : torch_geometric.data.Data @@ -52,10 +52,10 @@ def test_initialization(self, sample_data): in_channels=[5, 7, 9], out_channels=64 ) - + # Test __repr__ method repr_str = encoder.__repr__() - + # Check basic attributes assert encoder.in_channels == [5, 7, 9] assert encoder.out_channels == 64 @@ -63,7 +63,7 @@ def test_initialization(self, sample_data): def test_forward_pass(self, sample_data): """Test forward pass of the encoder. - + Parameters ---------- sample_data : torch_geometric.data.Data @@ -74,26 +74,26 @@ def test_forward_pass(self, sample_data): out_channels=64, selected_dimensions=[0, 1, 2] ) - + # Perform forward pass output_data = encoder(sample_data) - + # Check output attributes for i in [0, 1, 2]: # Check encoded features exist assert hasattr(output_data, f'x_{i}') assert output_data[f'x_{i}'].shape[1] == 64 - + # Check auxiliary attributes assert hasattr(output_data, f'x_aux_{i}') assert hasattr(output_data, f'logprobs_{i}') - + # Check edges index exists assert 'edges_index' in output_data def test_selective_encoding(self, sample_data): """Test encoding only specific dimensions. - + Parameters ---------- sample_data : torch_geometric.data.Data @@ -104,10 +104,10 @@ def test_selective_encoding(self, sample_data): out_channels=64, selected_dimensions=[0, 1] # Only encode the first two dimensions ) - + # Perform forward pass output_data = encoder(sample_data) - + # Verify encoding for selected dimensions assert hasattr(output_data, 'x_1') assert output_data['x_0'].shape[1] == 64 @@ -122,7 +122,7 @@ def test_dropout_configuration(self): out_channels=64, proj_dropout=0.5 ) - + # Check dropout value for i in encoder.dimensions: encoder_module = getattr(encoder, f'encoder_{i}') @@ -136,7 +136,7 @@ def test_dropout_configuration(self): ]) def test_variable_input_dimensions(self, sample_data, in_channels): """Test encoder with varying input dimensions. - + Parameters ---------- sample_data : torch_geometric.data.Data @@ -148,16 +148,16 @@ def test_variable_input_dimensions(self, sample_data, in_channels): in_channels=in_channels, out_channels=64 ) - + # Prepare data dynamically data = torch_geometric.data.Data() for i, channel in enumerate(in_channels): setattr(data, f'x_{i}', torch.randn(10, channel)) setattr(data, f'batch_{i}', torch.zeros(10, dtype=torch.long)) - + # Perform forward pass output_data = encoder(data) - + # Verify encoding for each dimension for i in range(len(in_channels)): assert hasattr(output_data, f'x_{i}') @@ -165,8 +165,8 @@ def test_variable_input_dimensions(self, sample_data, in_channels): def pytest_configure(): """Custom pytest configuration. - + Sets up default configuration values for testing. """ pytest.in_channels = [5, 7, 9] - pytest.out_channels = 64 \ No newline at end of file + pytest.out_channels = 64 diff --git a/test/nn/readouts/__init__.py b/test/nn/readouts/__init__.py index 44ecb2267..15638b43b 100644 --- a/test/nn/readouts/__init__.py +++ b/test/nn/readouts/__init__.py @@ -1 +1 @@ -"""Init file for readouts.""" \ No newline at end of file +"""Init file for readouts.""" diff --git a/test/nn/readouts/test_identical.py b/test/nn/readouts/test_identical.py index 9529f3a8d..67362b2ea 100644 --- a/test/nn/readouts/test_identical.py +++ b/test/nn/readouts/test_identical.py @@ -12,7 +12,7 @@ class TestNoReadOut: @pytest.fixture def base_kwargs(self): """Fixture providing the required base parameters. - + Returns ------- dict @@ -27,12 +27,12 @@ def base_kwargs(self): @pytest.fixture def readout_layer(self, base_kwargs): """Fixture to create a NoReadOut instance for testing. - + Parameters ---------- base_kwargs : dict A fixture providing the required base parameters. - + Returns ------- NoReadOut @@ -43,7 +43,7 @@ def readout_layer(self, base_kwargs): @pytest.fixture def sample_model_output(self): """Fixture to create a sample model output dictionary. - + Returns ------- dict @@ -58,7 +58,7 @@ def sample_model_output(self): @pytest.fixture def sample_batch(self): """Fixture to create a sample batch of graph data. - + Returns ------- torch_geometric.data.Data @@ -72,7 +72,7 @@ def sample_batch(self): def test_initialization(self, base_kwargs): """Test that NoReadOut initializes correctly with required parameters. - + Parameters ---------- base_kwargs : dict @@ -84,7 +84,7 @@ def test_initialization(self, base_kwargs): def test_forward_pass_returns_unchanged_output(self, readout_layer, sample_model_output, sample_batch): """Test that forward pass returns the model output without modifications. - + Parameters ---------- readout_layer : NoReadOut @@ -96,7 +96,7 @@ def test_forward_pass_returns_unchanged_output(self, readout_layer, sample_model """ original_output = sample_model_output.copy() output = readout_layer(sample_model_output, sample_batch) - + # The output should contain the original data plus the computed logits for key in original_output: assert key in output @@ -105,7 +105,7 @@ def test_forward_pass_returns_unchanged_output(self, readout_layer, sample_model def test_invalid_task_level(self, base_kwargs): """Test that initialization fails with invalid task_level. - + Parameters ---------- base_kwargs : dict @@ -118,7 +118,7 @@ def test_invalid_task_level(self, base_kwargs): def test_repr(self, readout_layer): """Test the string representation of the NoReadOut layer. - + Parameters ---------- readout_layer : NoReadOut @@ -129,7 +129,7 @@ def test_repr(self, readout_layer): def test_forward_pass_with_different_batch_sizes(self, readout_layer): """Test that forward pass works with different batch sizes. - + Parameters ---------- readout_layer : NoReadOut @@ -147,7 +147,7 @@ def test_forward_pass_with_different_batch_sizes(self, readout_layer): } result = readout_layer(single_output, single_batch) assert 'logits' in result - + # Test with multiple graphs multi_batch = tg_data.Data( x=torch.randn(15, 32), @@ -163,7 +163,7 @@ def test_forward_pass_with_different_batch_sizes(self, readout_layer): def test_kwargs_handling(self, base_kwargs): """Test that the layer correctly handles both required and additional keyword arguments. - + Parameters ---------- base_kwargs : dict @@ -176,4 +176,4 @@ def test_kwargs_handling(self, base_kwargs): } kwargs = {**base_kwargs, **additional_kwargs} readout = NoReadOut(**kwargs) - assert isinstance(readout, NoReadOut) \ No newline at end of file + assert isinstance(readout, NoReadOut) diff --git a/test/nn/readouts/test_mlp_readout.py b/test/nn/readouts/test_mlp_readout.py index 9381ebd98..7b08f794f 100644 --- a/test/nn/readouts/test_mlp_readout.py +++ b/test/nn/readouts/test_mlp_readout.py @@ -10,11 +10,11 @@ class TestMLPReadout: """Tests for the MLPReadout layer.""" - + @pytest.fixture def base_kwargs(self): """Fixture providing the required base parameters. - + Returns ------- dict @@ -30,7 +30,7 @@ def base_kwargs(self): @pytest.fixture def graph_level_kwargs(self): """Fixture providing parameters for graph-level tasks. - + Returns ------- dict @@ -47,12 +47,12 @@ def graph_level_kwargs(self): @pytest.fixture def readout_layer(self, base_kwargs): """Fixture to create a MLPReadout instance for testing. - + Parameters ---------- base_kwargs : dict A fixture providing the required base parameters. - + Returns ------- MLPReadout @@ -63,12 +63,12 @@ def readout_layer(self, base_kwargs): @pytest.fixture def graph_readout_layer(self, graph_level_kwargs): """Fixture to create a graph-level MLPReadout instance. - + Parameters ---------- graph_level_kwargs : dict A fixture providing parameters for graph-level tasks. - + Returns ------- MLPReadout @@ -79,7 +79,7 @@ def graph_readout_layer(self, graph_level_kwargs): @pytest.fixture def sample_model_output(self): """Fixture to create a sample model output dictionary. - + Returns ------- dict @@ -93,7 +93,7 @@ def sample_model_output(self): @pytest.fixture def sample_batch(self): """Fixture to create a sample batch of graph data. - + Returns ------- dict @@ -106,7 +106,7 @@ def sample_batch(self): @pytest.fixture def multi_graph_batch(self): """Fixture to create a batch with multiple graphs. - + Returns ------- dict @@ -118,7 +118,7 @@ def multi_graph_batch(self): def test_initialization_node_level(self, base_kwargs): """Test that MLPReadout initializes correctly for node-level tasks. - + Parameters ---------- base_kwargs : dict @@ -131,7 +131,7 @@ def test_initialization_node_level(self, base_kwargs): def test_initialization_graph_level(self, graph_level_kwargs): """Test that MLPReadout initializes correctly for graph-level tasks. - + Parameters ---------- graph_level_kwargs : dict @@ -146,7 +146,7 @@ def test_initialization_graph_level(self, graph_level_kwargs): def test_pooling_type_parameter(self, base_kwargs): """Test that different pooling types are correctly set. - + Parameters ---------- base_kwargs : dict @@ -159,7 +159,7 @@ def test_pooling_type_parameter(self, base_kwargs): def test_forward_node_level(self, readout_layer, sample_model_output, sample_batch): """Test forward pass for node-level tasks. - + Parameters ---------- readout_layer : MLPReadout @@ -170,14 +170,14 @@ def test_forward_node_level(self, readout_layer, sample_model_output, sample_bat A fixture to create a sample batch of graph data. """ output = readout_layer.forward(sample_model_output.copy(), sample_batch) - + assert 'x_0' in output assert output['x_0'].shape[0] == 10 # Same number of nodes assert output['x_0'].shape[1] == 8 # out_channels def test_forward_graph_level(self, graph_readout_layer, sample_model_output, sample_batch): """Test forward pass for graph-level tasks. - + Parameters ---------- graph_readout_layer : MLPReadout @@ -188,14 +188,14 @@ def test_forward_graph_level(self, graph_readout_layer, sample_model_output, sam A fixture to create a sample batch of graph data. """ output = graph_readout_layer.forward(sample_model_output.copy(), sample_batch) - + assert 'x_0' in output assert output['x_0'].shape[0] == 1 # Single graph assert output['x_0'].shape[1] == 8 # out_channels def test_forward_graph_level_multiple_graphs(self, graph_readout_layer, multi_graph_batch): """Test forward pass for multiple graphs in a batch. - + Parameters ---------- graph_readout_layer : MLPReadout @@ -205,14 +205,14 @@ def test_forward_graph_level_multiple_graphs(self, graph_readout_layer, multi_gr """ model_output = {'x_0': torch.randn(10, 64)} output = graph_readout_layer.forward(model_output, multi_graph_batch) - + assert 'x_0' in output assert output['x_0'].shape[0] == 2 # Two graphs assert output['x_0'].shape[1] == 8 # out_channels def test_call_method_adds_logits(self, readout_layer, sample_model_output, sample_batch): """Test that __call__ method adds logits to output. - + Parameters ---------- readout_layer : MLPReadout @@ -223,14 +223,14 @@ def test_call_method_adds_logits(self, readout_layer, sample_model_output, sampl A fixture to create a sample batch of graph data. """ output = readout_layer(sample_model_output.copy(), sample_batch) - + assert 'logits' in output assert 'x_0' in output assert torch.equal(output['logits'], output['x_0']) def test_different_pooling_types_graph_level(self, graph_level_kwargs): """Test that different pooling types work correctly for graph-level tasks. - + Parameters ---------- graph_level_kwargs : dict @@ -238,18 +238,18 @@ def test_different_pooling_types_graph_level(self, graph_level_kwargs): """ model_output = {'x_0': torch.randn(10, 64)} batch = {'batch_0': torch.zeros(10, dtype=torch.long)} - + for pooling in ['sum', 'mean', 'max']: kwargs = {**graph_level_kwargs, 'pooling_type': pooling} readout = MLPReadout(**kwargs) output = readout(model_output.copy(), batch) - + assert 'x_0' in output assert output['x_0'].shape == (1, 8) def test_dropout_parameter(self, base_kwargs): """Test that dropout parameter is correctly set. - + Parameters ---------- base_kwargs : dict @@ -261,7 +261,7 @@ def test_dropout_parameter(self, base_kwargs): def test_normalization_parameter(self, base_kwargs): """Test that normalization parameter is correctly set. - + Parameters ---------- base_kwargs : dict @@ -273,7 +273,7 @@ def test_normalization_parameter(self, base_kwargs): def test_activation_parameters(self, base_kwargs): """Test that activation function parameters are correctly set. - + Parameters ---------- base_kwargs : dict @@ -294,16 +294,16 @@ def test_empty_hidden_layers(self): } readout = MLPReadout(**kwargs) assert isinstance(readout, MLPReadout) - + model_output = {'x_0': torch.randn(10, 64)} batch = {'batch_0': torch.zeros(10, dtype=torch.long)} output = readout(model_output, batch) - + assert output['x_0'].shape == (1, 8) def test_output_shape_consistency(self, readout_layer, sample_batch): """Test that output shapes are consistent across different input sizes. - + Parameters ---------- readout_layer : MLPReadout @@ -315,13 +315,13 @@ def test_output_shape_consistency(self, readout_layer, sample_batch): model_output = {'x_0': torch.randn(num_nodes, 64)} batch = {'batch_0': torch.zeros(num_nodes, dtype=torch.long)} output = readout_layer(model_output, batch) - + assert output['x_0'].shape[0] == num_nodes assert output['x_0'].shape[1] == 8 def test_preserves_other_keys_in_model_output(self, readout_layer, sample_batch): """Test that forward pass preserves other keys in model_out. - + Parameters ---------- readout_layer : MLPReadout @@ -335,8 +335,8 @@ def test_preserves_other_keys_in_model_output(self, readout_layer, sample_batch) 'extra_data': torch.randn(10, 32) } output = readout_layer(model_output, sample_batch) - + assert 'edge_indices' in output assert 'extra_data' in output assert torch.equal(output['edge_indices'], model_output['edge_indices']) - assert torch.equal(output['extra_data'], model_output['extra_data']) \ No newline at end of file + assert torch.equal(output['extra_data'], model_output['extra_data']) diff --git a/test/nn/readouts/test_propagate_signal_down.py b/test/nn/readouts/test_propagate_signal_down.py index e0034a9cb..27cd56098 100644 --- a/test/nn/readouts/test_propagate_signal_down.py +++ b/test/nn/readouts/test_propagate_signal_down.py @@ -12,7 +12,7 @@ class TestPropagateSignalDown: @pytest.fixture def base_kwargs(self): """Fixture providing the required base parameters. - + Returns ------- dict @@ -29,12 +29,12 @@ def base_kwargs(self): @pytest.fixture def readout_layer(self, base_kwargs): """Fixture to create a PropagateSignalDown instance for testing. - + Parameters ---------- base_kwargs : dict A fixture providing the required base parameters. - + Returns ------- PropagateSignalDown @@ -47,7 +47,7 @@ def readout_layer(self, base_kwargs): @pytest.fixture def create_sparse_incidence_matrix(self): """Helper fixture to create sparse incidence matrices. - + Returns ------- callable @@ -55,7 +55,7 @@ def create_sparse_incidence_matrix(self): """ def _create_matrix(num_source, num_target, sparsity=0.3): """Create a sparse incidence matrix. - + Parameters ---------- num_source : int @@ -64,7 +64,7 @@ def _create_matrix(num_source, num_target, sparsity=0.3): The number of target nodes. sparsity : float The sparsity of the matrix. - + Returns ------- torch.sparse.FloatTensor @@ -73,32 +73,32 @@ def _create_matrix(num_source, num_target, sparsity=0.3): num_entries = int(num_source * num_target * sparsity) indices = torch.zeros((2, num_entries), dtype=torch.long) values = torch.ones(num_entries) - + for i in range(num_entries): source = torch.randint(0, num_source, (1,)) target = torch.randint(0, num_target, (1,)) indices[0, i] = source indices[1, i] = target values[i] = torch.randint(0, 2, (1,)) * 2 - 1 # {-1, 1} values - + sparse_matrix = torch.sparse_coo_tensor( indices=torch.stack([indices[1], indices[0]]), values=values, size=(num_target, num_source) ).coalesce() - + return sparse_matrix return _create_matrix @pytest.fixture def sample_batch(self, create_sparse_incidence_matrix): """Fixture to create a sample batch with required incidence matrices. - + Parameters ---------- create_sparse_incidence_matrix : callable A fixture to create sparse incidence matrices. - + Returns ------- torch_geometric.data.Data @@ -106,7 +106,7 @@ def sample_batch(self, create_sparse_incidence_matrix): """ num_nodes = 10 num_edges = 15 - + return tg_data.Data( x=torch.randn(num_nodes, 64), edge_index=torch.randint(0, num_nodes, (2, num_edges)), @@ -117,22 +117,22 @@ def sample_batch(self, create_sparse_incidence_matrix): @pytest.fixture def sample_model_output(self, sample_batch): """Fixture to create a sample model output with cell embeddings. - + Parameters ---------- sample_batch : torch_geometric.data.Data A fixture to create a sample batch with required incidence matrices. - + Returns ------- dict A sample model output with cell embeddings. """ hidden_dim = 64 - + num_nodes = sample_batch.x.size(0) num_edges = sample_batch.edge_index.size(1) - + return { 'logits': torch.randn(num_nodes, hidden_dim), 'x_0': torch.randn(num_nodes, hidden_dim), @@ -141,7 +141,7 @@ def sample_model_output(self, sample_batch): def test_forward_propagation(self, readout_layer, sample_model_output, sample_batch): """Test the forward pass with detailed assertions. - + Parameters ---------- readout_layer : PropagateSignalDown @@ -153,13 +153,13 @@ def test_forward_propagation(self, readout_layer, sample_model_output, sample_ba """ initial_output = {k: v.clone() for k, v in sample_model_output.items()} sample_model_output['x_0'] = sample_model_output['logits'] - + output = readout_layer(sample_model_output, sample_batch) - + assert 'x_0' in output assert output['x_0'].shape == initial_output['logits'].shape assert output['x_0'].dtype == torch.float32 - + assert 'x_1' in output assert output['x_1'].shape == initial_output['x_1'].shape assert output['x_1'].dtype == torch.float32 @@ -167,7 +167,7 @@ def test_forward_propagation(self, readout_layer, sample_model_output, sample_ba @pytest.mark.parametrize('missing_key', ['incidence_1']) def test_missing_incidence_matrix(self, readout_layer, sample_model_output, sample_batch, missing_key): """Test handling of missing incidence matrices. - + Parameters ---------- readout_layer : PropagateSignalDown @@ -181,14 +181,14 @@ def test_missing_incidence_matrix(self, readout_layer, sample_model_output, samp """ invalid_batch = tg_data.Data(**{k: v for k, v in sample_batch.items() if k != missing_key}) sample_model_output['x_0'] = sample_model_output['logits'] - + with pytest.raises(KeyError): readout_layer(sample_model_output, invalid_batch) @pytest.mark.parametrize('missing_key', ['x_1']) # Changed to only test x_1 def test_missing_cell_features(self, readout_layer, sample_model_output, sample_batch, missing_key): """Test handling of missing cell features. - + Parameters ---------- readout_layer : PropagateSignalDown @@ -202,13 +202,13 @@ def test_missing_cell_features(self, readout_layer, sample_model_output, sample_ """ invalid_output = {k: v for k, v in sample_model_output.items() if k != missing_key} invalid_output['x_0'] = invalid_output['logits'] # Always map logits to x_0 - + with pytest.raises(KeyError): readout_layer(invalid_output, sample_batch) def test_gradient_flow(self, readout_layer, sample_model_output, sample_batch): """Test gradient flow through the network. - + Parameters ---------- readout_layer : PropagateSignalDown @@ -221,19 +221,19 @@ def test_gradient_flow(self, readout_layer, sample_model_output, sample_batch): # Create a copy of logits tensor to track gradients properly logits = sample_model_output['logits'].clone().detach().requires_grad_(True) x_1 = sample_model_output['x_1'].clone().detach().requires_grad_(True) - + model_output = { 'logits': logits, 'x_0': logits, # Share the same tensor 'x_1': x_1 } - + output = readout_layer(model_output, sample_batch) loss = output['x_0'].sum() loss.backward() - + # Check gradient flow assert logits.grad is not None assert not torch.allclose(logits.grad, torch.zeros_like(logits.grad)) assert x_1.grad is not None - assert not torch.allclose(x_1.grad, torch.zeros_like(x_1.grad)) \ No newline at end of file + assert not torch.allclose(x_1.grad, torch.zeros_like(x_1.grad)) diff --git a/test/nn/wrappers/__init__.py b/test/nn/wrappers/__init__.py index f1ec7c310..9640e87cb 100644 --- a/test/nn/wrappers/__init__.py +++ b/test/nn/wrappers/__init__.py @@ -1 +1 @@ -"""Init file for the wrappers module.""" \ No newline at end of file +"""Init file for the wrappers module.""" diff --git a/test/nn/wrappers/cell/__init__.py b/test/nn/wrappers/cell/__init__.py index 377f92169..8872a0291 100644 --- a/test/nn/wrappers/cell/__init__.py +++ b/test/nn/wrappers/cell/__init__.py @@ -1 +1 @@ -"""Init file for tests for cell wrappers.""" \ No newline at end of file +"""Init file for tests for cell wrappers.""" diff --git a/test/nn/wrappers/cell/test_cell_wrappers.py b/test/nn/wrappers/cell/test_cell_wrappers.py index 6d4b2018c..12d11f0cc 100644 --- a/test/nn/wrappers/cell/test_cell_wrappers.py +++ b/test/nn/wrappers/cell/test_cell_wrappers.py @@ -24,7 +24,7 @@ class TestCellWrappers: """Test cell model wrappers.""" def test_CCCNWrapper(self, sg1_clique_lifted): """Test CCCNWrapper. - + Parameters ---------- sg1_clique_lifted : torch_geometric.data.Data @@ -37,8 +37,8 @@ def test_CCCNWrapper(self, sg1_clique_lifted): wrapper = CCCNWrapper( CCCN( data.x_1.shape[1] - ), - out_channels=out_channels, + ), + out_channels=out_channels, num_cell_dimensions=num_cell_dimensions ) out = wrapper(data) @@ -48,7 +48,7 @@ def test_CCCNWrapper(self, sg1_clique_lifted): def test_CCXNWrapper(self, sg1_cell_lifted): """Test CCXNWrapper. - + Parameters ---------- sg1_cell_lifted : torch_geometric.data.Data @@ -61,8 +61,8 @@ def test_CCXNWrapper(self, sg1_cell_lifted): wrapper = CCXNWrapper( CCXN( data.x_0.shape[1], data.x_1.shape[1], out_channels - ), - out_channels=out_channels, + ), + out_channels=out_channels, num_cell_dimensions=num_cell_dimensions ) out = wrapper(data) @@ -72,7 +72,7 @@ def test_CCXNWrapper(self, sg1_cell_lifted): def test_CWNWrapper(self, sg1_cell_lifted): """Test CWNWrapper. - + Parameters ---------- sg1_cell_lifted : torch_geometric.data.Data @@ -86,12 +86,11 @@ def test_CWNWrapper(self, sg1_cell_lifted): wrapper = CWNWrapper( CWN( data.x_0.shape[1], data.x_1.shape[1], data.x_2.shape[1], hid_channels, 2 - ), - out_channels=out_channels, + ), + out_channels=out_channels, num_cell_dimensions=num_cell_dimensions ) out = wrapper(data) for key in ["labels", "batch_0", "x_0", "x_1", "x_2"]: assert key in out - diff --git a/test/nn/wrappers/pointcloud/test_PointcloudWrapper.py b/test/nn/wrappers/pointcloud/test_PointcloudWrapper.py index 3e4969533..f369ef0a5 100644 --- a/test/nn/wrappers/pointcloud/test_PointcloudWrapper.py +++ b/test/nn/wrappers/pointcloud/test_PointcloudWrapper.py @@ -16,7 +16,7 @@ class TestPointcloudWrapper: @pytest.fixture def mock_backbone(self): """Fixture to create a mock backbone model. - + Returns ------- Mock @@ -29,7 +29,7 @@ def mock_backbone(self): @pytest.fixture def wrapper_kwargs(self): """Fixture providing the required parameters for PointcloudWrapper. - + Returns ------- dict @@ -43,14 +43,14 @@ def wrapper_kwargs(self): @pytest.fixture def pointcloud_wrapper(self, mock_backbone, wrapper_kwargs): """Fixture to create a PointcloudWrapper instance. - + Parameters ---------- mock_backbone : Mock A fixture providing a mock backbone model. wrapper_kwargs : dict A fixture providing required initialization parameters. - + Returns ------- PointcloudWrapper @@ -65,7 +65,7 @@ def pointcloud_wrapper(self, mock_backbone, wrapper_kwargs): @pytest.fixture def sample_batch(self): """Fixture to create a sample batch of pointcloud data. - + Returns ------- Mock @@ -80,7 +80,7 @@ def sample_batch(self): @pytest.fixture def multi_graph_batch(self): """Fixture to create a batch with multiple pointclouds. - + Returns ------- Mock @@ -94,7 +94,7 @@ def multi_graph_batch(self): def test_initialization(self, mock_backbone, wrapper_kwargs): """Test that PointcloudWrapper initializes correctly. - + Parameters ---------- mock_backbone : Mock @@ -111,7 +111,7 @@ def test_initialization(self, mock_backbone, wrapper_kwargs): def test_forward_returns_dict(self, pointcloud_wrapper, sample_batch): """Test that forward method returns a dictionary. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -124,7 +124,7 @@ def test_forward_returns_dict(self, pointcloud_wrapper, sample_batch): def test_forward_contains_required_keys(self, pointcloud_wrapper, sample_batch): """Test that forward output contains required keys. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -133,14 +133,14 @@ def test_forward_contains_required_keys(self, pointcloud_wrapper, sample_batch): A fixture to create a sample batch of pointcloud data. """ output = pointcloud_wrapper.forward(sample_batch) - + assert "x_0" in output assert "labels" in output assert "batch_0" in output def test_forward_calls_backbone(self, pointcloud_wrapper, sample_batch): """Test that forward method calls the backbone with correct arguments. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -149,12 +149,12 @@ def test_forward_calls_backbone(self, pointcloud_wrapper, sample_batch): A fixture to create a sample batch of pointcloud data. """ pointcloud_wrapper.forward(sample_batch) - + pointcloud_wrapper.backbone.assert_called_once_with(sample_batch.x_0) def test_forward_output_x_0_shape(self, mock_backbone, wrapper_kwargs, sample_batch): """Test that x_0 output has the correct shape. - + Parameters ---------- mock_backbone : Mock @@ -166,19 +166,19 @@ def test_forward_output_x_0_shape(self, mock_backbone, wrapper_kwargs, sample_ba """ expected_output = torch.randn(10, 64) mock_backbone.return_value = expected_output - + wrapper = PointcloudWrapper( backbone=mock_backbone, **wrapper_kwargs ) output = wrapper.forward(sample_batch) - + assert output["x_0"].shape == expected_output.shape assert torch.equal(output["x_0"], expected_output) def test_forward_preserves_labels(self, pointcloud_wrapper, sample_batch): """Test that forward method preserves labels from batch. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -187,12 +187,12 @@ def test_forward_preserves_labels(self, pointcloud_wrapper, sample_batch): A fixture to create a sample batch of pointcloud data. """ output = pointcloud_wrapper.forward(sample_batch) - + assert torch.equal(output["labels"], sample_batch.y) def test_forward_preserves_batch_0(self, pointcloud_wrapper, sample_batch): """Test that forward method preserves batch_0 from batch. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -201,12 +201,12 @@ def test_forward_preserves_batch_0(self, pointcloud_wrapper, sample_batch): A fixture to create a sample batch of pointcloud data. """ output = pointcloud_wrapper.forward(sample_batch) - + assert torch.equal(output["batch_0"], sample_batch.batch_0) def test_forward_with_multiple_pointclouds(self, pointcloud_wrapper, multi_graph_batch): """Test forward pass with multiple pointclouds in a batch. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -215,7 +215,7 @@ def test_forward_with_multiple_pointclouds(self, pointcloud_wrapper, multi_graph A fixture to create a batch with multiple pointclouds. """ output = pointcloud_wrapper.forward(multi_graph_batch) - + assert "x_0" in output assert "labels" in output assert "batch_0" in output @@ -223,7 +223,7 @@ def test_forward_with_multiple_pointclouds(self, pointcloud_wrapper, multi_graph def test_forward_with_different_feature_dimensions(self, mock_backbone, wrapper_kwargs): """Test forward pass with different input feature dimensions. - + Parameters ---------- mock_backbone : Mock @@ -235,22 +235,22 @@ def test_forward_with_different_feature_dimensions(self, mock_backbone, wrapper_ backbone=mock_backbone, **wrapper_kwargs ) - + for input_dim in [3, 6, 128]: batch = Mock() batch.x_0 = torch.randn(10, input_dim) batch.y = torch.randint(0, 5, (10,)) batch.batch_0 = torch.zeros(10, dtype=torch.long) - + mock_backbone.return_value = torch.randn(10, 64) output = wrapper.forward(batch) - + assert output["x_0"].shape == (10, 64) mock_backbone.assert_called_with(batch.x_0) def test_forward_with_different_num_points(self, mock_backbone, wrapper_kwargs): """Test forward pass with different numbers of points. - + Parameters ---------- mock_backbone : Mock @@ -262,23 +262,23 @@ def test_forward_with_different_num_points(self, mock_backbone, wrapper_kwargs): backbone=mock_backbone, **wrapper_kwargs ) - + for num_points in [5, 10, 50, 100]: batch = Mock() batch.x_0 = torch.randn(num_points, 3) batch.y = torch.randint(0, 5, (num_points,)) batch.batch_0 = torch.zeros(num_points, dtype=torch.long) - + mock_backbone.return_value = torch.randn(num_points, 64) output = wrapper.forward(batch) - + assert output["x_0"].shape[0] == num_points assert len(output["labels"]) == num_points assert len(output["batch_0"]) == num_points def test_forward_backbone_exception_propagates(self, mock_backbone, wrapper_kwargs, sample_batch): """Test that exceptions from backbone are propagated. - + Parameters ---------- mock_backbone : Mock @@ -293,13 +293,13 @@ def test_forward_backbone_exception_propagates(self, mock_backbone, wrapper_kwar backbone=mock_backbone, **wrapper_kwargs ) - + with pytest.raises(RuntimeError, match="Backbone error"): wrapper.forward(sample_batch) def test_forward_with_empty_batch(self, mock_backbone, wrapper_kwargs): """Test forward pass with an empty batch (edge case). - + Parameters ---------- mock_backbone : Mock @@ -311,15 +311,15 @@ def test_forward_with_empty_batch(self, mock_backbone, wrapper_kwargs): backbone=mock_backbone, **wrapper_kwargs ) - + batch = Mock() batch.x_0 = torch.randn(0, 3) # Empty pointcloud batch.y = torch.tensor([]) batch.batch_0 = torch.tensor([], dtype=torch.long) - + mock_backbone.return_value = torch.randn(0, 64) output = wrapper.forward(batch) - + assert output["x_0"].shape == (0, 64) assert len(output["labels"]) == 0 assert len(output["batch_0"]) == 0 @@ -331,7 +331,7 @@ def test_inheritance_from_abstract_wrapper(self): def test_forward_output_consistency(self, pointcloud_wrapper, sample_batch): """Test that multiple forward passes produce consistent structure. - + Parameters ---------- pointcloud_wrapper : PointcloudWrapper @@ -341,13 +341,13 @@ def test_forward_output_consistency(self, pointcloud_wrapper, sample_batch): """ output1 = pointcloud_wrapper.forward(sample_batch) output2 = pointcloud_wrapper.forward(sample_batch) - + assert set(output1.keys()) == set(output2.keys()) assert output1["x_0"].shape == output2["x_0"].shape def test_forward_with_real_tensors(self, mock_backbone, wrapper_kwargs): """Test forward pass with realistic tensor values. - + Parameters ---------- mock_backbone : Mock @@ -359,7 +359,7 @@ def test_forward_with_real_tensors(self, mock_backbone, wrapper_kwargs): backbone=mock_backbone, **wrapper_kwargs ) - + # Create a realistic batch with actual PyTorch tensors batch = Mock() batch.x_0 = torch.tensor([ @@ -369,17 +369,17 @@ def test_forward_with_real_tensors(self, mock_backbone, wrapper_kwargs): ]) batch.y = torch.tensor([0, 1, 2]) batch.batch_0 = torch.tensor([0, 0, 0], dtype=torch.long) - + mock_backbone.return_value = torch.randn(3, 64) output = wrapper.forward(batch) - + assert output["x_0"].dtype == torch.float32 assert output["labels"].dtype == torch.long assert output["batch_0"].dtype == torch.long def test_residual_connections_flag(self, mock_backbone): """Test initialization with residual_connections parameter. - + Parameters ---------- mock_backbone : Mock @@ -392,7 +392,7 @@ def test_residual_connections_flag(self, mock_backbone): residual_connections=True ) assert wrapper_with_residual.residual_connections is True - + wrapper_without_residual = PointcloudWrapper( backbone=mock_backbone, out_channels=64, diff --git a/test/nn/wrappers/simplicial/__init__.py b/test/nn/wrappers/simplicial/__init__.py index 5a9bdf7b1..bb2e806df 100644 --- a/test/nn/wrappers/simplicial/__init__.py +++ b/test/nn/wrappers/simplicial/__init__.py @@ -1 +1 @@ -"""Init file for tests for simplicial wrappers.""" \ No newline at end of file +"""Init file for tests for simplicial wrappers.""" diff --git a/test/nn/wrappers/simplicial/test_SCCNNWrapper.py b/test/nn/wrappers/simplicial/test_SCCNNWrapper.py index ca6db06f9..bf59ea44f 100644 --- a/test/nn/wrappers/simplicial/test_SCCNNWrapper.py +++ b/test/nn/wrappers/simplicial/test_SCCNNWrapper.py @@ -20,7 +20,7 @@ class TestSimplicialWrappers: def test_SCCNNWrapper(self, sg1_clique_lifted): """Test SCCNNWrapper. - + Parameters ---------- sg1_clique_lifted : torch_geometric.data.Data @@ -33,8 +33,8 @@ def test_SCCNNWrapper(self, sg1_clique_lifted): init_args = (data.x_0.shape[1], data.x_1.shape[1], data.x_2.shape[1]), (out_dim, out_dim, out_dim), conv_order, sc_order wrapper = SCCNNWrapper( - SCCNNCustom(*init_args), - out_channels=out_dim, + SCCNNCustom(*init_args), + out_channels=out_dim, num_cell_dimensions=3 ) out = wrapper(data) @@ -44,7 +44,7 @@ def test_SCCNNWrapper(self, sg1_clique_lifted): def test_SANWarpper(self, sg1_clique_lifted): """Test SANWarpper. - + Parameters ---------- sg1_clique_lifted : torch_geometric.data.Data @@ -55,8 +55,8 @@ def test_SANWarpper(self, sg1_clique_lifted): hidden_channels = data.x_0.shape[1] wrapper = SANWrapper( - SAN(data.x_0.shape[1], hidden_channels), - out_channels=out_dim, + SAN(data.x_0.shape[1], hidden_channels), + out_channels=out_dim, num_cell_dimensions=3 ) out = wrapper(data) @@ -66,7 +66,7 @@ def test_SANWarpper(self, sg1_clique_lifted): def test_SCNWrapper(self, sg1_clique_lifted): """Test SCNWrapper. - + Parameters ---------- sg1_clique_lifted : torch_geometric.data.Data @@ -76,8 +76,8 @@ def test_SCNWrapper(self, sg1_clique_lifted): out_dim = data.x_0.shape[1] wrapper = SCNWrapper( - SCN2(data.x_0.shape[1], data.x_1.shape[1], data.x_2.shape[1]), - out_channels=out_dim, + SCN2(data.x_0.shape[1], data.x_1.shape[1], data.x_2.shape[1]), + out_channels=out_dim, num_cell_dimensions=3 ) out = wrapper(data) @@ -87,7 +87,7 @@ def test_SCNWrapper(self, sg1_clique_lifted): def test_SCCNWrapper(self, sg1_clique_lifted): """Test SCCNWrapper. - + Parameters ---------- sg1_clique_lifted : torch_geometric.data.Data @@ -98,12 +98,11 @@ def test_SCCNWrapper(self, sg1_clique_lifted): max_rank = 2 wrapper = SCCNWrapper( - SCCN(data.x_0.shape[1], max_rank), - out_channels=out_dim, + SCCN(data.x_0.shape[1], max_rank), + out_channels=out_dim, num_cell_dimensions=3 ) out = wrapper(data) # Assert keys in output for key in ["labels", "batch_0", "x_0", "x_1", "x_2"]: assert key in out - diff --git a/test/optimizer/test_optimizer.py b/test/optimizer/test_optimizer.py index ae003a845..8f5ba7986 100644 --- a/test/optimizer/test_optimizer.py +++ b/test/optimizer/test_optimizer.py @@ -8,7 +8,7 @@ class TestTBOptimizer: """Test the TBOptimizer class.""" - + def setup_method(self): """Setup method.""" self.optimizer_config_with_scheduler = { @@ -29,10 +29,9 @@ def test_configure_optimizer(self): out = optimizer.configure_optimizer(self.params) assert "optimizer" in out assert "lr_scheduler" in out - + # Check without scheduler optimizer = TBOptimizer(**self.optimizer_config_without_scheduler) out = optimizer.configure_optimizer(self.params) assert "optimizer" in out assert "lr_scheduler" not in out - \ No newline at end of file diff --git a/test/pipeline/test_pipeline.py b/test/pipeline/test_pipeline.py index 785987159..d41ebb9c6 100644 --- a/test/pipeline/test_pipeline.py +++ b/test/pipeline/test_pipeline.py @@ -14,7 +14,7 @@ class TestPipeline: def setup_method(self): """Setup method.""" hydra.core.global_hydra.GlobalHydra.instance().clear() - + def test_pipeline(self): """Test pipeline.""" with hydra.initialize(config_path="../../configs", job_name="job"): @@ -32,4 +32,4 @@ def test_pipeline(self): ], return_hydra_config=True ) - run(cfg) \ No newline at end of file + run(cfg) diff --git a/test/test_tutorials.py b/test/test_tutorials.py index 1c2b63a7a..757bbd1a1 100644 --- a/test/test_tutorials.py +++ b/test/test_tutorials.py @@ -9,7 +9,7 @@ def _exec_tutorial(path): """Execute a tutorial notebook. - + Parameters ---------- path : str @@ -37,10 +37,10 @@ def _exec_tutorial(path): @pytest.mark.parametrize("path", paths) def test_tutorial(path): """Run the test of the tutorials. - + Parameters ---------- path : str The path to the tutorial. """ - _exec_tutorial(path) \ No newline at end of file + _exec_tutorial(path) diff --git a/test/test_uv_env_setup.py b/test/test_uv_env_setup.py new file mode 100644 index 000000000..6ef79e1b9 --- /dev/null +++ b/test/test_uv_env_setup.py @@ -0,0 +1,26 @@ +"""Tests for uv_env_setup.sh error handling.""" + +import subprocess +from pathlib import Path + + +def test_source_invalid_platform_does_not_kill_shell(): + """Verify sourcing with invalid platform does not kill the shell. + + If the script uses bare `exit 1`, sourcing it kills the shell + (subshell exits). If it uses `return 1`, the subshell survives + and the sentinel `STILL_ALIVE` is printed. + """ + script = Path(__file__).parents[1] / "uv_env_setup.sh" + result = subprocess.run( + [ + "bash", + "-c", + f"source {script} INVALID 2>/dev/null; echo STILL_ALIVE", + ], + capture_output=True, + text=True, + ) + assert "STILL_ALIVE" in result.stdout, ( + "Shell was killed by `exit 1` instead of `return 1`" + ) diff --git a/test/transforms/data_manipulations/test_CombinedPSEs.py b/test/transforms/data_manipulations/test_CombinedPSEs.py index 697491d45..c8a8d7ed5 100644 --- a/test/transforms/data_manipulations/test_CombinedPSEs.py +++ b/test/transforms/data_manipulations/test_CombinedPSEs.py @@ -117,7 +117,7 @@ def test_encoding_order(self): "LapPE": {"max_pe_dim": 2, "concat_to_x": True}, "RWSE": {"max_pe_dim": 3, "concat_to_x": True} } - + # Test LapPE first, then RWSE transform1 = CombinedPSEs(encodings=["LapPE", "RWSE"], parameters=params) data1 = Data(x=self.x.clone(), edge_index=self.edge_index, num_nodes=self.num_nodes) @@ -235,7 +235,7 @@ def test_large_graph(self): "RWSE": {"max_pe_dim": 8, "concat_to_x": True} } transform = CombinedPSEs(encodings=["LapPE", "RWSE"], parameters=params) - + # Create a larger graph num_nodes = 50 num_edges = 150 @@ -309,7 +309,7 @@ def test_device_consistency(self): "RWSE": {"max_pe_dim": 4, "concat_to_x": True} } transform = CombinedPSEs(encodings=["LapPE", "RWSE"], parameters=params) - + edge_index = self.edge_index.cuda() x = self.x.cuda() data = Data(x=x, edge_index=edge_index, num_nodes=self.num_nodes) @@ -327,7 +327,7 @@ def test_backward_compatibility(self): "RWSE": {"max_pe_dim": 4, "concat_to_x": True} } transform = CombinedPSEs(encodings=["LapPE", "RWSE"], parameters=params) - + y = torch.tensor([0, 1, 0]) custom_attr = torch.tensor([10, 20, 30]) data = Data( @@ -365,10 +365,10 @@ def test_multiple_applications(self): assert hasattr(transformed1, "RWSE") assert hasattr(transformed2, "LapPE") assert hasattr(transformed2, "RWSE") - + # RWSE should be deterministic assert torch.allclose(transformed1.RWSE, transformed2.RWSE) - + # LapPE may vary due to numerical solver, but shapes should match assert transformed1.LapPE.shape == transformed2.LapPE.shape @@ -445,7 +445,7 @@ def test_case_sensitive_encoding_names(self): @pytest.mark.parametrize("encoding", ["LapPE", "RWSE"]) def test_parametrized_single_encodings(self, encoding): """Parametrized test for single encodings. - + Parameters ---------- encoding : str @@ -465,7 +465,7 @@ def test_parametrized_single_encodings(self, encoding): @pytest.mark.parametrize("max_pe_dim", [2, 4, 8, 16]) def test_parametrized_dimensions(self, max_pe_dim): """Parametrized test for different PE dimensions. - + Parameters ---------- max_pe_dim : int @@ -506,7 +506,7 @@ def test_complete_graph(self): edges.append([i, j]) edge_index = torch.tensor(edges).t() x = torch.randn(4, 2) - + params = { "LapPE": {"max_pe_dim": 4, "concat_to_x": True}, "RWSE": {"max_pe_dim": 4, "concat_to_x": True} diff --git a/test/transforms/data_manipulations/test_ConnectivityTransforms.py b/test/transforms/data_manipulations/test_ConnectivityTransforms.py index 05ffc8659..6ceb6bb6d 100644 --- a/test/transforms/data_manipulations/test_ConnectivityTransforms.py +++ b/test/transforms/data_manipulations/test_ConnectivityTransforms.py @@ -43,4 +43,4 @@ def test_radius_connectivity(self): data = self.infer_by_radius(self.data.clone()) assert "edge_index" in data, "No edges in Data object" assert data.edge_index.size(0) == 2 - assert data.edge_index.size(1) > 0 \ No newline at end of file + assert data.edge_index.size(1) > 0 diff --git a/test/transforms/data_manipulations/test_DataFieldTransforms.py b/test/transforms/data_manipulations/test_DataFieldTransforms.py index 5ef68e67a..520ca538e 100644 --- a/test/transforms/data_manipulations/test_DataFieldTransforms.py +++ b/test/transforms/data_manipulations/test_DataFieldTransforms.py @@ -32,7 +32,7 @@ def setup_method(self): def test_keep_selected_data_fields(self): """Test keeping selected fields. - + Verifies that only specified fields are kept and others are removed. """ data = self.keep_selected_fields(self.data.clone()) @@ -40,4 +40,4 @@ def test_keep_selected_data_fields(self): self.keep_selected_fields.parameters["base_fields"] + self.keep_selected_fields.parameters["preserved_fields"] ) - assert set(data.keys()) == expected_fields, "Some fields are not deleted" \ No newline at end of file + assert set(data.keys()) == expected_fields, "Some fields are not deleted" diff --git a/test/transforms/data_manipulations/test_EqualGausFeatures.py b/test/transforms/data_manipulations/test_EqualGausFeatures.py index a27e121bf..cee15489d 100644 --- a/test/transforms/data_manipulations/test_EqualGausFeatures.py +++ b/test/transforms/data_manipulations/test_EqualGausFeatures.py @@ -59,13 +59,13 @@ def test_forward_basic(self): edge_index=torch.tensor([[0, 1], [1, 0]]), num_nodes=2 ) - + transformed = self.transform(data) - + # Check output dimensions assert transformed.x.size() == (2, self.num_features) # num_nodes x num_features assert transformed.num_nodes == 2 - + # Check other attributes are preserved assert torch.equal(transformed.edge_index, data.edge_index) @@ -75,25 +75,25 @@ def test_feature_consistency(self): x=torch.tensor([[1.0], [2.0]]), num_nodes=2 ) - + # Transform same data twice result1 = self.transform(data) result2 = self.transform(data) - + # Should get exactly same features since using same feature vector assert torch.equal(result1.x, result2.x) def test_different_node_counts(self): """Test with different numbers of nodes.""" node_counts = [1, 10, 100] - + for n in node_counts: data = Data( x=torch.randn(n, 2), edge_index=torch.zeros((2, 0)), num_nodes=n ) - + transformed = self.transform(data) assert transformed.x.size() == (n, self.num_features) assert transformed.num_nodes == n @@ -105,7 +105,7 @@ def test_empty_graph(self): edge_index=torch.tensor([[],[]]), num_nodes=0 ) - + transformed = self.transform(data) assert transformed.x.size() == (0, self.num_features) assert transformed.num_nodes == 0 @@ -117,14 +117,14 @@ def test_feature_values(self): x=torch.randn(num_nodes, 1), num_nodes=num_nodes ) - + transformed = self.transform(data) features = transformed.x - + # Basic value checks assert not torch.isnan(features).any() assert not torch.isinf(features).any() - + # Approximate distribution checks (should be loose enough) mean_diff = torch.abs(features.mean() - self.mean) std_diff = torch.abs(features.std() - self.std) @@ -136,7 +136,7 @@ def test_attribute_preservation(self): edge_index = torch.tensor([[0, 1], [1, 0]]) edge_attr = torch.tensor([[1.0], [1.0]]) custom_attr = "test" - + data = Data( x=torch.tensor([[1.0], [2.0]]), edge_index=edge_index, @@ -144,9 +144,9 @@ def test_attribute_preservation(self): custom_attr=custom_attr, num_nodes=2 ) - + transformed = self.transform(data) - + assert torch.equal(transformed.edge_index, edge_index) assert torch.equal(transformed.edge_attr, edge_attr) - assert transformed.custom_attr == custom_attr \ No newline at end of file + assert transformed.custom_attr == custom_attr diff --git a/test/transforms/data_manipulations/test_FeatureTransforms.py b/test/transforms/data_manipulations/test_FeatureTransforms.py index 21fafc11b..d48fc507d 100644 --- a/test/transforms/data_manipulations/test_FeatureTransforms.py +++ b/test/transforms/data_manipulations/test_FeatureTransforms.py @@ -51,4 +51,4 @@ def test_one_hot_degree_features(self): [0, 1, 0, 0], [1, 0, 0, 0], ]) - assert (data.one_hot_degree == expected_vals).all() \ No newline at end of file + assert (data.one_hot_degree == expected_vals).all() diff --git a/test/transforms/data_manipulations/test_GroupHomophily.py b/test/transforms/data_manipulations/test_GroupHomophily.py index 1c2355e00..34b573f0b 100644 --- a/test/transforms/data_manipulations/test_GroupHomophily.py +++ b/test/transforms/data_manipulations/test_GroupHomophily.py @@ -38,17 +38,17 @@ def test_simple_hypergraph(self): incidence[0:2, 0] = 1 # Second hyperedge contains nodes 2,3 incidence[2:4, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 0, 1, 1]) # Two classes ) transformed = self.transform(data) - + assert "group_combinatorial_homophily" in transformed result = transformed["group_combinatorial_homophily"] - + # Should have entry for hyperedges of size 2 assert "he_card=2" in result # Check matrices exist @@ -67,7 +67,7 @@ def test_mixed_size_hyperedges(self): incidence[2:5, 1] = 1 # Size 2 hyperedge incidence[1:3, 2] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 0, 1, 1, 1]) @@ -75,7 +75,7 @@ def test_mixed_size_hyperedges(self): transformed = self.transform(data) result = transformed["group_combinatorial_homophily"] - + # Should capture the two most frequent sizes assert len(result) <= self.transform.top_k assert any("he_card=2" in key for key in result.keys()) @@ -85,7 +85,7 @@ def test_single_class(self): incidence = torch.zeros((3, 2)) incidence[0:2, 0] = 1 incidence[1:3, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 0, 0]) # All same class @@ -93,7 +93,7 @@ def test_single_class(self): transformed = self.transform(data) result = transformed["group_combinatorial_homophily"] - + for size_data in result.values(): assert size_data["Dt"].shape[0] == 1 # Only one class assert not torch.isnan(size_data["Dt"]).any() @@ -116,7 +116,7 @@ def test_affinity_score(self): x_mod = 5 # nodes in class t = 2 # type-t degree k = 3 # hyperedge size - + score = self.transform.calculate_affinity_score(n_nodes, x_mod, t, k) assert isinstance(score, float) assert 0 <= score <= 1 # Should be a probability @@ -128,22 +128,22 @@ def test_matrix_calculations(self): incidence[0:2, 0] = 1 incidence[2:4, 1] = 1 labels = torch.tensor([0, 0, 1, 1]) - + unique_labels = {0: 2, 1: 2} # Two nodes of each class class_node_idxs = { 0: torch.tensor([0, 1]), 1: torch.tensor([2, 3]) } he_cardinalities = torch.tensor([2, 2]) - + Dt, D = self.transform.calculate_D_matrix( - incidence, + incidence, labels, he_cardinalities, unique_labels, class_node_idxs ) - + assert not torch.isnan(Dt).any() assert not torch.isnan(D).any() assert Dt.shape[1] == max(he_cardinalities) @@ -154,7 +154,7 @@ def test_attribute_preservation(self): incidence = torch.zeros((3, 2)) incidence[0:2, 0] = 1 incidence[1:3, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 1, 0]), @@ -163,4 +163,4 @@ def test_attribute_preservation(self): transformed = self.transform(data) assert transformed.custom_attr == "test" - assert torch.equal(transformed.y, data.y) \ No newline at end of file + assert torch.equal(transformed.y, data.y) diff --git a/test/transforms/data_manipulations/test_IdentityTransform.py b/test/transforms/data_manipulations/test_IdentityTransform.py index 45449d460..a760bb036 100644 --- a/test/transforms/data_manipulations/test_IdentityTransform.py +++ b/test/transforms/data_manipulations/test_IdentityTransform.py @@ -44,7 +44,7 @@ def test_forward_simple_graph(self): data = Data(x=x, edge_index=edge_index, num_nodes=2) transformed = self.transform(data) - + # Check that all attributes are equal assert torch.equal(transformed.edge_index, data.edge_index) assert torch.equal(transformed.x, data.x) @@ -70,7 +70,7 @@ def test_forward_with_attributes(self): ) transformed = self.transform(data) - + # Check all attributes remain equal assert torch.equal(transformed.x, data.x) assert torch.equal(transformed.edge_index, data.edge_index) @@ -87,9 +87,9 @@ def test_forward_empty_graph(self): edge_index=torch.tensor([[],[]]), num_nodes=0 ) - + transformed = self.transform(data) - + assert transformed.num_nodes == 0 assert transformed.edge_index.size() == data.edge_index.size() assert transformed.x.size() == data.x.size() @@ -100,11 +100,11 @@ def test_forward_large_graph(self): """Test transform on a large graph.""" num_nodes = 1000 num_edges = 5000 - + x = torch.randn(num_nodes, 10) # 10 features per node edge_index = torch.randint(0, num_nodes, (2, num_edges)) edge_attr = torch.randn(num_edges, 5) # 5 features per edge - + data = Data( x=x, edge_index=edge_index, @@ -113,7 +113,7 @@ def test_forward_large_graph(self): ) transformed = self.transform(data) - + assert torch.equal(transformed.x, data.x) assert torch.equal(transformed.edge_index, data.edge_index) assert torch.equal(transformed.edge_attr, data.edge_attr) @@ -126,9 +126,9 @@ def test_data_consistency(self): edge_index=torch.tensor([[0, 1], [1, 0]]), num_nodes=2 ) - + transformed = self.transform(data) - + # Check key attributes remain equal for key in data.keys(): # Changed from data.keys to data.keys() assert hasattr(transformed, key) @@ -147,10 +147,10 @@ def test_with_different_dtypes(self): ) transformed = self.transform(data) - + assert transformed.x.dtype == torch.long assert transformed.y.dtype == torch.float assert transformed.z.dtype == torch.bool assert torch.equal(transformed.x, data.x) assert torch.equal(transformed.y, data.y) - assert torch.equal(transformed.z, data.z) \ No newline at end of file + assert torch.equal(transformed.z, data.z) diff --git a/test/transforms/data_manipulations/test_LaplacianEncodings.py b/test/transforms/data_manipulations/test_LaplacianEncodings.py index 21ceac9c3..d2e2850ed 100644 --- a/test/transforms/data_manipulations/test_LaplacianEncodings.py +++ b/test/transforms/data_manipulations/test_LaplacianEncodings.py @@ -66,7 +66,7 @@ def test_forward_no_concat(self): # Check that LapPE is stored separately assert hasattr(transformed, "LapPE") assert transformed.LapPE.shape == (3, 8) - + # Check that original x is unchanged assert torch.equal(transformed.x, x) @@ -240,7 +240,7 @@ def test_sign_consistency(self): # Check that for each eigenvector column, the maximum absolute value # element has consistent sign (the sign convention used in the code) lappe = transformed.LapPE - + for col in range(lappe.shape[1]): if lappe[:, col].abs().sum() > 1e-6: # Skip zero columns (padding) max_idx = torch.argmax(lappe[:, col].abs()) @@ -346,7 +346,7 @@ def test_backward_compatibility(self): x = torch.tensor([[1.0], [2.0], [3.0]]) y = torch.tensor([0, 1, 0]) custom_attr = torch.tensor([10, 20, 30]) - + data = Data( x=x, edge_index=edge_index, @@ -465,7 +465,7 @@ def test_max_pe_dim_larger_than_graph(self): @pytest.mark.parametrize("max_pe_dim", [1, 2, 4, 8, 16]) def test_parametrized_dimensions(self, max_pe_dim): """Parametrized test for different LapPE dimensions. - + Parameters ---------- max_pe_dim : int @@ -485,7 +485,7 @@ def test_parametrized_dimensions(self, max_pe_dim): @pytest.mark.parametrize("include_first", [True, False]) def test_parametrized_include_first(self, include_first): """Parametrized test for include_first parameter. - + Parameters ---------- include_first : bool diff --git a/test/transforms/data_manipulations/test_MessagePassingHomophily.py b/test/transforms/data_manipulations/test_MessagePassingHomophily.py index 97bb921b4..a65d97d7a 100644 --- a/test/transforms/data_manipulations/test_MessagePassingHomophily.py +++ b/test/transforms/data_manipulations/test_MessagePassingHomophily.py @@ -24,7 +24,7 @@ def test_initialization(self): # Test custom initialization custom_transform = MessagePassingHomophily( - num_steps=5, + num_steps=5, incidence_field="incidence_1" ) assert custom_transform.num_steps == 5 @@ -49,26 +49,26 @@ def test_simple_hypergraph(self): incidence[0:2, 0] = 1 # Second hyperedge contains nodes 2,3 incidence[2:4, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 0, 1, 1]) # Two classes ) transformed = self.transform(data) - + # Check output structure assert "mp_homophily" in transformed result = transformed["mp_homophily"] - + # Check Ep and Np matrices assert "Ep" in result assert "Np" in result - + # Check dimensions assert result["Ep"].shape == (2, 2, 2) # num_steps x num_edges x num_classes assert result["Np"].shape == (2, 4, 2) # num_steps x num_nodes x num_classes - + # Check probability distributions sum to 1 assert torch.allclose(result["Ep"].sum(dim=2), torch.ones(2, 2)) assert torch.allclose(result["Np"].sum(dim=2), torch.ones(2, 4)) @@ -78,7 +78,7 @@ def test_different_incidence_fields(self): incidence = torch.zeros((4, 2)) incidence[0:2, 0] = 1 incidence[2:4, 1] = 1 - + data = Data( incidence_0=incidence.to_sparse(), incidence_1=incidence.to_sparse(), @@ -91,7 +91,7 @@ def test_different_incidence_fields(self): transform = MessagePassingHomophily(incidence_field=field) transformed = transform(data) result = transformed["mp_homophily"] - + assert result["Ep"].shape[1] == 2 # num_edges assert result["Np"].shape[1] == 4 # num_nodes @@ -100,7 +100,7 @@ def test_single_class(self): incidence = torch.zeros((3, 2)) incidence[0:2, 0] = 1 incidence[1:3, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 0, 0]) # All same class @@ -108,11 +108,11 @@ def test_single_class(self): transformed = self.transform(data) result = transformed["mp_homophily"] - + # Check dimensions assert result["Ep"].shape == (2, 2, 1) # Single class assert result["Np"].shape == (2, 3, 1) # Single class - + # All probabilities should be 1 since there's only one class assert torch.all(result["Ep"] == 1.0) assert torch.all(result["Np"] == 1.0) @@ -122,7 +122,7 @@ def test_structured_hypergraph(self): n_nodes = 6 n_edges = 3 n_classes = 2 - + # Create structured incidence matrix incidence = torch.zeros((n_nodes, n_edges)) # First hyperedge: nodes 0,1,2 (class 0) @@ -131,10 +131,10 @@ def test_structured_hypergraph(self): incidence[2:5, 1] = 1 # Third hyperedge: nodes 3,4,5 (class 1) incidence[3:6, 2] = 1 - + # Define class labels labels = torch.tensor([0, 0, 0, 1, 1, 1]) - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=labels @@ -142,11 +142,11 @@ def test_structured_hypergraph(self): transformed = self.transform(data) result = transformed["mp_homophily"] - + # Check dimensions assert result["Ep"].shape == (2, n_edges, n_classes) assert result["Np"].shape == (2, n_nodes, n_classes) - + # Check first step probabilities for first hyperedge (all class 0) expected_ep_step0_edge0 = torch.tensor([1.0, 0.0]) assert torch.allclose(result["Ep"][0, 0], expected_ep_step0_edge0) @@ -160,7 +160,7 @@ def test_empty_hypergraph(self): transformed = self.transform(data) result = transformed["mp_homophily"] - + assert result["Ep"].shape[0] == 2 # num_steps assert result["Np"].shape[0] == 2 # num_steps assert result["Ep"].shape[1] == 0 # no edges @@ -171,7 +171,7 @@ def test_attribute_preservation(self): incidence = torch.zeros((3, 2)) incidence[0:2, 0] = 1 incidence[1:3, 1] = 1 - + data = Data( incidence_hyperedges=incidence.to_sparse(), y=torch.tensor([0, 1, 0]), @@ -180,8 +180,8 @@ def test_attribute_preservation(self): ) transformed = self.transform(data) - + # Check original attributes are preserved assert transformed.custom_attr == "test" assert torch.equal(transformed.y, data.y) - assert torch.equal(transformed.edge_attr, data.edge_attr) \ No newline at end of file + assert torch.equal(transformed.edge_attr, data.edge_attr) diff --git a/test/transforms/data_manipulations/test_OnlyConnectedComponent.py b/test/transforms/data_manipulations/test_OnlyConnectedComponent.py index a838c7371..d6250d6a0 100644 --- a/test/transforms/data_manipulations/test_OnlyConnectedComponent.py +++ b/test/transforms/data_manipulations/test_OnlyConnectedComponent.py @@ -20,7 +20,7 @@ def test_initialization(self): """Test initialization of the transform.""" assert self.transform.type == "keep_connected_component" assert self.transform.parameters["num_components"] == self.num_components - + # Test default initialization transform = KeepOnlyConnectedComponent() assert transform.type == "keep_connected_component" @@ -73,7 +73,7 @@ def test_equal_size_components(self): def test_multiple_num_components(self): """Test transform with num_components > 1.""" transform = KeepOnlyConnectedComponent(num_components=2) - + # Create a graph with three components edge_index = torch.tensor([ [0, 1, 2, 3, 4, 5], # Three components: (0,1), (2,3), (4,5) @@ -124,4 +124,4 @@ def test_attributes_preservation(self): transformed = self.transform(data.clone()) assert hasattr(transformed, "edge_attr") assert hasattr(transformed, "test_attr") - assert transformed.test_attr == "test" \ No newline at end of file + assert transformed.test_attr == "test" diff --git a/test/transforms/data_manipulations/test_RandomWalkEncodings.py b/test/transforms/data_manipulations/test_RandomWalkEncodings.py index 7fcffae6c..aee0e9a7f 100644 --- a/test/transforms/data_manipulations/test_RandomWalkEncodings.py +++ b/test/transforms/data_manipulations/test_RandomWalkEncodings.py @@ -56,7 +56,7 @@ def test_forward_no_concat(self): # Check that RWSE is stored separately assert hasattr(transformed, "RWSE") assert transformed.RWSE.shape == (3, 8) - + # Check that original x is unchanged assert torch.equal(transformed.x, x) @@ -244,7 +244,7 @@ def test_backward_compatibility(self): x = torch.tensor([[1.0], [2.0], [3.0]]) y = torch.tensor([0, 1, 0]) custom_attr = torch.tensor([10, 20, 30]) - + data = Data( x=x, edge_index=edge_index, @@ -293,7 +293,7 @@ def test_batch_processing(self): @pytest.mark.parametrize("max_pe_dim", [1, 2, 4, 8, 16]) def test_parametrized_dimensions(self, max_pe_dim): """Parametrized test for different RWSE dimensions. - + Parameters ---------- max_pe_dim : int diff --git a/test/transforms/data_manipulations/test_RedefineSimplicialNeighbourhoods.py b/test/transforms/data_manipulations/test_RedefineSimplicialNeighbourhoods.py index 442493fef..9d056c27b 100644 --- a/test/transforms/data_manipulations/test_RedefineSimplicialNeighbourhoods.py +++ b/test/transforms/data_manipulations/test_RedefineSimplicialNeighbourhoods.py @@ -65,7 +65,7 @@ def test_forward_simple_graph(self): assert torch.equal(initial[key].to_dense(), transformed[key].to_dense()), f"Mismatch in tensor values for key: {key}" except AttributeError as e: pytest.fail(f"Tensor conversion to dense failed for key: {key}. Error: {e}") - + def test_repr(self): """Test the string representation of the transformation class. @@ -96,6 +96,3 @@ def test_repr(self): # Ensure all keys appear in the representation for key in transforms_config["RedefineSimplicialNeighbourhoods"]: assert key in repr_str, f"Missing key '{key}' in __repr__ output." - - - \ No newline at end of file diff --git a/test/transforms/data_manipulations/test_SimplicialCurvature.py b/test/transforms/data_manipulations/test_SimplicialCurvature.py index ff38a93ba..6835b0456 100644 --- a/test/transforms/data_manipulations/test_SimplicialCurvature.py +++ b/test/transforms/data_manipulations/test_SimplicialCurvature.py @@ -11,7 +11,7 @@ class TestSimplicialCurvature: def test_simplicial_curvature(self, simple_graph_1): """Test simplicial curvature calculation. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data @@ -34,6 +34,6 @@ def test_simplicial_curvature(self, simple_graph_1): torch.sum(data['incidence_3'], dim=1).to_dense(), dim=1 ) - + res = simplicial_curvature(data) - assert isinstance(res, Data) \ No newline at end of file + assert isinstance(res, Data) diff --git a/test/transforms/feature_liftings/test_Concatenation.py b/test/transforms/feature_liftings/test_Concatenation.py index c05460120..0fffa6e12 100644 --- a/test/transforms/feature_liftings/test_Concatenation.py +++ b/test/transforms/feature_liftings/test_Concatenation.py @@ -19,7 +19,7 @@ def setup_method(self): def test_lift_features(self, simple_graph_0, simple_graph_1): """Test the lift_features method. - + Parameters ---------- simple_graph_0 : torch_geometric.data.Data @@ -27,12 +27,12 @@ def test_lift_features(self, simple_graph_0, simple_graph_1): simple_graph_1 : torch_geometric.data.Data A simple graph data object. """ - + data = simple_graph_0 # Test the lift_features method lifted_data = self.lifting.forward(data.clone()) assert lifted_data.x_2.shape == torch.Size([0, 6]) - + data = simple_graph_1 # Test the lift_features method lifted_data = self.lifting.forward(data.clone()) diff --git a/test/transforms/feature_liftings/test_ProjectionSum.py b/test/transforms/feature_liftings/test_ProjectionSum.py index 1f76f42f4..cef8b690b 100644 --- a/test/transforms/feature_liftings/test_ProjectionSum.py +++ b/test/transforms/feature_liftings/test_ProjectionSum.py @@ -19,7 +19,7 @@ def setup_method(self): def test_lift_features(self, simple_graph_1): """Test the lift_features method. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data diff --git a/test/transforms/feature_liftings/test_SetLifting.py b/test/transforms/feature_liftings/test_SetLifting.py index a8112a23e..0283f3f2c 100644 --- a/test/transforms/feature_liftings/test_SetLifting.py +++ b/test/transforms/feature_liftings/test_SetLifting.py @@ -19,7 +19,7 @@ def setup_method(self): def test_lift_features(self, simple_graph_1): """Test the lift_features method. - + Parameters ---------- simple_graph_1 : torch_geometric.data.Data diff --git a/test/transforms/liftings/graph2cell/test_cycle_lifting.py b/test/transforms/liftings/graph2cell/test_cycle_lifting.py index f7b28eb0e..7e0120b1f 100644 --- a/test/transforms/liftings/graph2cell/test_cycle_lifting.py +++ b/test/transforms/liftings/graph2cell/test_cycle_lifting.py @@ -14,7 +14,7 @@ def setup_method(self): def test_lift_topology(self, simple_graph_1): """Test the lift_topology method. - + Parameters ---------- simple_graph_1 : Data diff --git a/test/transforms/liftings/graph2cell/test_discrete_configuration_complex_lifting.py b/test/transforms/liftings/graph2cell/test_discrete_configuration_complex_lifting.py index b79a2aa59..8bb58d481 100644 --- a/test/transforms/liftings/graph2cell/test_discrete_configuration_complex_lifting.py +++ b/test/transforms/liftings/graph2cell/test_discrete_configuration_complex_lifting.py @@ -8,7 +8,7 @@ @pytest.fixture def mock_data(): """Create a mock data object for testing. - + Returns ------- MagicMock @@ -30,7 +30,7 @@ def test_discrete_configuration_complex_lifting_init(): def test_discrete_configuration_complex_lifting_forward(mock_data): """Test the forward method of the DiscreteConfigurationComplexLifting class. - + Parameters ---------- mock_data : MagicMock @@ -43,7 +43,7 @@ def test_discrete_configuration_complex_lifting_forward(mock_data): def test_discrete_configuration_complex_lifting_lift_topology(mock_data): """Test the lift_topology method of the DiscreteConfigurationComplexLifting class. - + Parameters ---------- mock_data : MagicMock @@ -66,19 +66,19 @@ def setup_method(self): def test_lift_topology(self, simple_graph_1): """Test the lift_topology method. - + Parameters ---------- simple_graph_1 : Data A simple graph used for testing. """ data = simple_graph_1 - + assert self.lifting_concat.forward(data.clone()).incidence_1.shape[1] == 156, "Something is wrong with incidence_1." assert self.lifting_sum.forward(data.clone()).incidence_1.shape[1] == 156, "Something is wrong with incidence_1." assert self.lifting_mean.forward(data.clone()).incidence_1.shape[1] == 156, "Something is wrong with incidence_1." - + # import torch diff --git a/test/transforms/liftings/graph2combinatorial/test_graph_induced_cc.py b/test/transforms/liftings/graph2combinatorial/test_graph_induced_cc.py index 658c80c7c..f973d39a5 100644 --- a/test/transforms/liftings/graph2combinatorial/test_graph_induced_cc.py +++ b/test/transforms/liftings/graph2combinatorial/test_graph_induced_cc.py @@ -46,7 +46,7 @@ def test_lift_topology(self): abs(expected_incidence_1) == lifted_data.incidence_1.to_dense() ).all(), "Something is wrong with incidence_1 (nodes to edges)." - + expected_incidence_2 = torch.tensor( [ [0., 0.], @@ -74,9 +74,9 @@ def test_lift_topology(self): abs(expected_incidence_2) == lifted_data.incidence_2.to_dense() ).all(), "Something is wrong with incidence_2 (edges to faces)." - + expected_incidence_3 = torch.tensor([[1.],[1.]]) assert ( abs(expected_incidence_3) == lifted_data.incidence_3.to_dense() - ).all(), "Something is wrong with incidence_3 (faces to 3-cells)." \ No newline at end of file + ).all(), "Something is wrong with incidence_3 (faces to 3-cells)." diff --git a/test/transforms/liftings/graph2hypergraph/test_kernel_lifting.py b/test/transforms/liftings/graph2hypergraph/test_kernel_lifting.py index df4311404..8a9fc25f9 100644 --- a/test/transforms/liftings/graph2hypergraph/test_kernel_lifting.py +++ b/test/transforms/liftings/graph2hypergraph/test_kernel_lifting.py @@ -18,7 +18,7 @@ def cos_sim(A, B, dim=1): """Compute the cosine similarity between two matrices. - + Parameters ---------- A : torch.Tensor @@ -27,7 +27,7 @@ def cos_sim(A, B, dim=1): The second matrix. dim : int, optional The dimension along which to compute the cosine similarity, by default 1. - + Returns ------- torch.Tensor @@ -41,7 +41,7 @@ def cos_sim(A, B, dim=1): def rbf_kernel(x1, x2, gamma=1.0): """ Compute the RBF kernel between two matrices. - + Parameters ---------- x1 : torch.Tensor @@ -50,7 +50,7 @@ def rbf_kernel(x1, x2, gamma=1.0): The second matrix. gamma : float, optional The gamma parameter of the RBF kernel, by default 1.0. - + Returns ------- torch.Tensor @@ -138,7 +138,7 @@ def test_get_combination(self): def reset_lifting(self, **kwargs): """Reset the HypergraphKernelLifting class with new parameters. - + Parameters ---------- **kwargs : dict diff --git a/test/transforms/liftings/graph2hypergraph/test_khop_lifting.py b/test/transforms/liftings/graph2hypergraph/test_khop_lifting.py index 07e9f2f41..4e3cadb3e 100644 --- a/test/transforms/liftings/graph2hypergraph/test_khop_lifting.py +++ b/test/transforms/liftings/graph2hypergraph/test_khop_lifting.py @@ -19,7 +19,7 @@ def setup_method(self): def test_lift_topology(self, simple_graph_2): """ Test the lift_topology method. - + Parameters ---------- simple_graph_2 : Data @@ -78,7 +78,7 @@ def test_lift_topology(self, simple_graph_2): assert ( expected_n_hyperedges == lifted_data_k2.num_hyperedges ), "Something is wrong with the number of hyperedges (k=2)." - + self.data_edge_attr = simple_graph_2 edge_attributes = torch.rand((self.data_edge_attr.edge_index.shape[1], 2)) self.data_edge_attr.edge_attr = edge_attributes diff --git a/test/transforms/liftings/graph2hypergraph/test_knn_lifting.py b/test/transforms/liftings/graph2hypergraph/test_knn_lifting.py index aab1496bb..27737e7e8 100644 --- a/test/transforms/liftings/graph2hypergraph/test_knn_lifting.py +++ b/test/transforms/liftings/graph2hypergraph/test_knn_lifting.py @@ -13,7 +13,7 @@ class TestHypergraphKNNLifting: def setup_method(self): """Set up test fixtures before each test method. - + Creates instances of HypergraphKNNLifting with different k values and loop settings. """ @@ -35,7 +35,7 @@ def test_initialization(self): def test_lift_topology_k2(self, simple_graph_2): """Test the lift_topology method with k=2. - + Parameters ---------- simple_graph_2 : torch_geometric.data.Data @@ -60,13 +60,13 @@ def test_lift_topology_k2(self, simple_graph_2): lifted_data_k2["incidence_hyperedges"].to_dense(), expected_incidence_1 ), "Incorrect incidence_hyperedges for k=2" - + assert lifted_data_k2["num_hyperedges"] == expected_n_hyperedges assert torch.equal(lifted_data_k2["x_0"], simple_graph_2.x) def test_lift_topology_k3(self, simple_graph_2): """Test the lift_topology method with k=3. - + Parameters ---------- simple_graph_2 : torch_geometric.data.Data @@ -91,20 +91,20 @@ def test_lift_topology_k3(self, simple_graph_2): lifted_data_k3["incidence_hyperedges"].to_dense(), expected_incidence_1 ), "Incorrect incidence_hyperedges for k=3" - + assert lifted_data_k3["num_hyperedges"] == expected_n_hyperedges assert torch.equal(lifted_data_k3["x_0"], simple_graph_2.x) def test_lift_topology_no_loop(self, simple_graph_2): """Test the lift_topology method with loop=False. - + Parameters ---------- simple_graph_2 : torch_geometric.data.Data A simple graph fixture with 9 nodes arranged in a line pattern. """ lifted_data = self.lifting_no_loop.lift_topology(simple_graph_2.clone()) - + # Verify no self-loops in the incidence matrix incidence_matrix = lifted_data["incidence_hyperedges"].to_dense() diagonal = torch.diag(incidence_matrix) @@ -117,9 +117,9 @@ def test_lift_topology_with_equal_features(self): x=torch.tensor([[1.0], [1.0], [2.0], [2.0]]), edge_index=torch.tensor([[0, 1, 2, 3], [1, 0, 3, 2]]) ) - + lifted_data = self.lifting_k2.lift_topology(data) - + # Verify the shape of the output assert lifted_data["incidence_hyperedges"].size() == (4, 4) assert lifted_data["num_hyperedges"] == 4 @@ -128,7 +128,7 @@ def test_lift_topology_with_equal_features(self): @pytest.mark.parametrize("k_value", [1, 2, 3, 4]) def test_different_k_values(self, k_value, simple_graph_2): """Test lift_topology with different k values. - + Parameters ---------- k_value : int @@ -138,11 +138,11 @@ def test_different_k_values(self, k_value, simple_graph_2): """ lifting = HypergraphKNNLifting(k_value=k_value, loop=True) lifted_data = lifting.lift_topology(simple_graph_2.clone()) - + # Verify basic properties assert lifted_data["num_hyperedges"] == simple_graph_2.x.size(0) incidence_matrix = lifted_data["incidence_hyperedges"].to_dense() - + # Check that each node is connected to at most k nodes assert torch.all(incidence_matrix.sum(dim=1) <= k_value), \ f"Some nodes are connected to more than {k_value} neighbors" @@ -212,4 +212,4 @@ def test_invalid_initialization(self): # Test with non-boolean loop with pytest.raises(TypeError, match="loop must be a boolean"): - HypergraphKNNLifting(k_value=1, loop="True") \ No newline at end of file + HypergraphKNNLifting(k_value=1, loop="True") diff --git a/test/transforms/liftings/graph2simplicial/test_clique_lifting.py b/test/transforms/liftings/graph2simplicial/test_clique_lifting.py index e2470d9ae..8da8438ee 100644 --- a/test/transforms/liftings/graph2simplicial/test_clique_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_clique_lifting.py @@ -22,7 +22,7 @@ def setup_method(self): def test_lift_topology(self, simple_graph_1): """Test the lift_topology method. - + Parameters ---------- simple_graph_1 : Data @@ -210,7 +210,7 @@ def test_lift_topology(self, simple_graph_1): def test_lifted_features_signed(self, simple_graph_1): """Test the lift_features method in signed incidence cases. - + Parameters ---------- simple_graph_1 : Data @@ -258,7 +258,7 @@ def test_lifted_features_signed(self, simple_graph_1): def test_lifted_features_unsigned(self, simple_graph_1): """Test the lift_features method in unsigned incidence cases. - + Parameters ---------- simple_graph_1 : Data diff --git a/test/transforms/liftings/graph2simplicial/test_graph_induced_lifting.py b/test/transforms/liftings/graph2simplicial/test_graph_induced_lifting.py index 7523fe6b3..ba1956441 100644 --- a/test/transforms/liftings/graph2simplicial/test_graph_induced_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_graph_induced_lifting.py @@ -217,4 +217,4 @@ def test_lift_topology(self): ), "Something is wrong with unsigned incidence_3 (triangles to tetrahedrons)." assert torch.allclose( expected_incidence_3_singular_values_signed, S_signed, atol=1.0e-04 - ), "Something is wrong with signed incidence_3 (triangles to tetrahedrons)." \ No newline at end of file + ), "Something is wrong with signed incidence_3 (triangles to tetrahedrons)." diff --git a/test/transforms/liftings/graph2simplicial/test_latentclique_lifting.py b/test/transforms/liftings/graph2simplicial/test_latentclique_lifting.py index b7ce043c9..7024407c1 100644 --- a/test/transforms/liftings/graph2simplicial/test_latentclique_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_latentclique_lifting.py @@ -68,9 +68,9 @@ def test_lift_topology(self): edge_prob_single_adj = self.lifting.forward(self.data_test_one.clone()).adjacency_0 ### TEST #1 ### - # if edge_prob == 1 and a the input graph has a single clique, + # if edge_prob == 1 and the input graph has a single clique, # then the 1-skeleton of the inferred latent SC must have a single clique - # (or, equivalently, the SC has a simplex in its facests set if complex_dim = |maximal_clique|-1) + # (or, equivalently, the SC has a simplex in its facets set if complex_dim = |maximal_clique|-1) # Convert adjacency matrix to NetworkX graph G_from_latent_complex = nx.from_numpy_matrix( @@ -108,4 +108,3 @@ def test_lift_topology(self): assert input_edge_set.issubset( latent_edge_set ), "the set of 0-simplices of the inferred latent SC is not contained the set of edges of the input graph" - diff --git a/test/transforms/liftings/graph2simplicial/test_line_lifting.py b/test/transforms/liftings/graph2simplicial/test_line_lifting.py index 497b56a89..d4b8388ef 100644 --- a/test/transforms/liftings/graph2simplicial/test_line_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_line_lifting.py @@ -10,7 +10,7 @@ def create_test_graph(): """Create a simple graph for testing. - + Returns ------- torch_geometric.data @@ -93,9 +93,9 @@ def test_lift_topology(self): assert ( expected_incidence_2 == lifted_data_signed.incidence_2.to_dense() ).all(), "Something is wrong with signed incidence_2 (edges to triangles)." - + lifted_data = self.lifting_unsigned_max_simplices.forward(self.data.clone()) - + expected_incidence = torch.tensor( [[1., 1., 1., 1., 0., 0., 0., 0., 0.], [1., 0., 0., 0., 1., 1., 0., 0., 0.], @@ -107,4 +107,4 @@ def test_lift_topology(self): assert ( abs(expected_incidence) == lifted_data.incidence_1.to_dense() - ).all(), "Something is wrong with incidence (triangles to tetrahedrons)." \ No newline at end of file + ).all(), "Something is wrong with incidence (triangles to tetrahedrons)." diff --git a/test/transforms/liftings/graph2simplicial/test_neighborhood_complex_lifting.py b/test/transforms/liftings/graph2simplicial/test_neighborhood_complex_lifting.py index 59111c004..bd638c381 100644 --- a/test/transforms/liftings/graph2simplicial/test_neighborhood_complex_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_neighborhood_complex_lifting.py @@ -22,17 +22,17 @@ def setup_method(self): self.lifting_unsigned = NeighborhoodComplexLifting(complex_dim=3, signed=False) self.lifting_high = NeighborhoodComplexLifting(complex_dim=7, signed=False) - # Intialize an empty graph for testing purpouses + # Initialize an empty graph for testing purpouses self.empty_graph = nx.empty_graph(10) self.empty_data = from_networkx(self.empty_graph) self.empty_data["x"] = torch.rand((10, 10)) - # Intialize a start graph for testing + # Initialize a start graph for testing self.star_graph = nx.star_graph(5) self.star_data = from_networkx(self.star_graph) self.star_data["x"] = torch.rand((6, 1)) - # Intialize a random graph for testing purpouses + # Initialize a random graph for testing purpouses self.random_graph = nx.fast_gnp_random_graph(5, 0.5) self.random_data = from_networkx(self.random_graph) self.random_data["x"] = torch.rand((5, 1)) @@ -40,12 +40,12 @@ def setup_method(self): def has_neighbour(self, simplex_points: list[set]) -> tuple[bool, set[int]]: """Verify that the maximal simplices of Data representation of a simplicial complex share a neighbour. - + Parameters ---------- simplex_points : list[set] A list of sets representing the maximal simplices of a simplicial complex. - + Returns ------- bool diff --git a/test/transforms/liftings/graph2simplicial/test_simplicialkhop_lifting.py b/test/transforms/liftings/graph2simplicial/test_simplicialkhop_lifting.py index 833fe4721..97684cb65 100644 --- a/test/transforms/liftings/graph2simplicial/test_simplicialkhop_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_simplicialkhop_lifting.py @@ -20,7 +20,7 @@ def setup_method(self): def test_lift_topology(self, simple_graph_1): """ Test the lift_topology method. - + Parameters ---------- simple_graph_1 : Data @@ -1328,7 +1328,7 @@ def test_lift_topology(self, simple_graph_1): def test_lifted_features_signed(self, simple_graph_1): """Test the lift_features method for signed case. - + Parameters ---------- simple_graph_1 : Data @@ -1423,7 +1423,7 @@ def test_lifted_features_signed(self, simple_graph_1): def test_lifted_features_unsigned(self, simple_graph_1): """Test the lift_features method for unsigned case. - + Parameters ---------- simple_graph_1 : Data diff --git a/test/transforms/liftings/graph2simplicial/test_vietoris_rips_lifting.py b/test/transforms/liftings/graph2simplicial/test_vietoris_rips_lifting.py index b93826555..ca1cec95c 100644 --- a/test/transforms/liftings/graph2simplicial/test_vietoris_rips_lifting.py +++ b/test/transforms/liftings/graph2simplicial/test_vietoris_rips_lifting.py @@ -85,4 +85,4 @@ def test_lift_topology(self): ), "Something is wrong with unsigned incidence_3 (triangles to tetrahedrons)." assert torch.allclose( expected_incidence_3_singular_values_signed, S_signed, atol=1.0e-04 - ), "Something is wrong with signed incidence_3 (triangles to tetrahedrons)." \ No newline at end of file + ), "Something is wrong with signed incidence_3 (triangles to tetrahedrons)." diff --git a/test/transforms/liftings/pointcloud2hypergraph/test_mogmst_lifting.py b/test/transforms/liftings/pointcloud2hypergraph/test_mogmst_lifting.py index bb748f483..65f1f9e3b 100644 --- a/test/transforms/liftings/pointcloud2hypergraph/test_mogmst_lifting.py +++ b/test/transforms/liftings/pointcloud2hypergraph/test_mogmst_lifting.py @@ -50,16 +50,16 @@ def test_find_mog(self): and labels[3] != labels[6] and labels[0] != labels[6] ), "Labels have not been assigned correctly" - + labels, num_components, means = self.lifting2.find_mog( self.data.clone().x.numpy() ) - + assert num_components == 4, "Wrong number of components" def test_lift_topology(self): """Test the lift_topology method.""" - + # Test the lift_topology method lifted_data_k = self.lifting.forward(self.data.clone()) diff --git a/test/transforms/liftings/pointcloud2simplicial/test_random_flag_complex_lifting.py b/test/transforms/liftings/pointcloud2simplicial/test_random_flag_complex_lifting.py index 01411bee1..d664f6633 100644 --- a/test/transforms/liftings/pointcloud2simplicial/test_random_flag_complex_lifting.py +++ b/test/transforms/liftings/pointcloud2simplicial/test_random_flag_complex_lifting.py @@ -25,4 +25,4 @@ def setup_method(self): def test_empty(self): """Test that the lifted topology is empty.""" lifted_data = self.lifting_p_0.forward(self.data.clone()) - assert(lifted_data.x_1.size(0) == 0) \ No newline at end of file + assert(lifted_data.x_1.size(0) == 0) diff --git a/test/transforms/liftings/simplicial2combinatorial/test_coface_cc_lifting.py b/test/transforms/liftings/simplicial2combinatorial/test_coface_cc_lifting.py index 1d8b332b0..a80b84488 100644 --- a/test/transforms/liftings/simplicial2combinatorial/test_coface_cc_lifting.py +++ b/test/transforms/liftings/simplicial2combinatorial/test_coface_cc_lifting.py @@ -73,4 +73,4 @@ def test_lift_topology(self): ).all(), "Something is wrong with incidence_3 ." assert ( expected_n_3_cells == lifted_data.x_3.size(0) - ), "Something is wrong with the number of 3-cells." \ No newline at end of file + ), "Something is wrong with the number of 3-cells." diff --git a/test/transforms/liftings/test_AbstractLifting.py b/test/transforms/liftings/test_AbstractLifting.py index cbeb628ce..5682e8a82 100644 --- a/test/transforms/liftings/test_AbstractLifting.py +++ b/test/transforms/liftings/test_AbstractLifting.py @@ -10,37 +10,37 @@ class TestAbstractLifting: def setup_method(self): """Set up test fixtures for each test method. - + Creates a concrete subclass of AbstractLifting for testing purposes. """ class ConcreteLifting(AbstractLifting): """Concrete implementation of AbstractLifting for testing.""" - + def lift_topology(self, data): """Implementation of abstract method that calls parent's method. - + Parameters ---------- data : torch_geometric.data.Data The input data to be lifted. - + Returns ------- dict Empty dictionary as this is just for testing. - + Raises ------ NotImplementedError Always raises this error as it calls the parent's abstract method. """ return super().lift_topology(data) - + self.lifting = ConcreteLifting(feature_lifting=None) - + def test_lift_topology_raises_not_implemented(self): """Test that the abstract lift_topology method raises NotImplementedError. - + Verifies that calling lift_topology on an abstract class implementation raises NotImplementedError as expected. """ @@ -48,6 +48,6 @@ def test_lift_topology_raises_not_implemented(self): x=torch.tensor([[1.0], [2.0]]), edge_index=torch.tensor([[0, 1], [1, 0]]) ) - + with pytest.raises(NotImplementedError): - self.lifting.lift_topology(dummy_data) \ No newline at end of file + self.lifting.lift_topology(dummy_data) diff --git a/test/transforms/liftings/test_GraphLifting.py b/test/transforms/liftings/test_GraphLifting.py index 01cc381b2..8cda876ce 100644 --- a/test/transforms/liftings/test_GraphLifting.py +++ b/test/transforms/liftings/test_GraphLifting.py @@ -7,15 +7,15 @@ class ConcreteGraphLifting(GraphLifting): """Concrete implementation of GraphLifting for testing.""" - + def lift_topology(self, data): """Implement the abstract lift_topology method. - + Parameters ---------- data : torch_geometric.data.Data The input data to be lifted. - + Returns ------- dict @@ -26,20 +26,20 @@ def lift_topology(self, data): class TestGraphLifting: """Test the GraphLifting class.""" - + def setup_method(self): """Set up test fixtures before each test method. - + Creates an instance of ConcreteGraphLifting with default parameters. """ self.lifting = ConcreteGraphLifting( - feature_lifting="ProjectionSum", + feature_lifting="ProjectionSum", preserve_edge_attr=False ) def test_data_has_edge_attr(self): """Test _data_has_edge_attr method with different data configurations.""" - + # Test case 1: Data with edge attributes data_with_edge_attr = Data( x=torch.tensor([[1.0], [2.0]]), @@ -70,7 +70,7 @@ def test_data_has_edge_attr_empty_data(self): def test_data_has_edge_attr_different_edge_formats(self): """Test _data_has_edge_attr method with different edge attribute formats.""" - + # Test with float edge attributes data_float_attr = Data( x=torch.tensor([[1.0], [2.0]]), @@ -98,7 +98,7 @@ def test_data_has_edge_attr_different_edge_formats(self): @pytest.mark.parametrize("preserve_edge_attr", [True, False]) def test_init_preserve_edge_attr(self, preserve_edge_attr): """Test initialization with different preserve_edge_attr values. - + Parameters ---------- preserve_edge_attr : bool @@ -108,4 +108,4 @@ def test_init_preserve_edge_attr(self, preserve_edge_attr): feature_lifting="ProjectionSum", preserve_edge_attr=preserve_edge_attr ) - assert lifting.preserve_edge_attr == preserve_edge_attr \ No newline at end of file + assert lifting.preserve_edge_attr == preserve_edge_attr diff --git a/test/utils/test_config_resolvers.py b/test/utils/test_config_resolvers.py index d06eb9097..4f57601bd 100644 --- a/test/utils/test_config_resolvers.py +++ b/test/utils/test_config_resolvers.py @@ -29,12 +29,12 @@ def setup_method(self): self.cliq_lift_transform = OmegaConf.load("configs/transforms/liftings/graph2simplicial/clique.yaml") self.feature_lift_transform = OmegaConf.load("configs/transforms/feature_liftings/concatenate.yaml") hydra.initialize(version_base="1.3", config_path="../../configs", job_name="job") - + def test_get_default_trainer(self): """Test get_default_trainer.""" out = get_default_trainer() assert isinstance(out, str) - + def test_get_default_metrics(self): """Test get_default_metrics.""" out = get_default_metrics("classification") @@ -50,7 +50,7 @@ def test_get_default_transform(self): """Test get_default_transform.""" out = get_default_transform("graph/MUTAG", "graph/gat") assert out == "no_transform" - + out = get_default_transform("graph/MUTAG", "non_relational/mlp") assert out == "no_transform" @@ -90,12 +90,12 @@ def test_get_required_lifting(self): out = get_required_lifting("graph", "cell/can") assert out == "graph2cell_default" - + def test_get_monitor_metric(self): """Test get_monitor_metric.""" out = get_monitor_metric("classification", "F1") - assert out == "val/F1" - + assert out == "val/F1" + with pytest.raises(ValueError, match="Invalid task") as e: get_monitor_metric("mix", "F1") @@ -103,10 +103,10 @@ def test_get_monitor_mode(self): """Test get_monitor_mode.""" out = get_monitor_mode("regression") assert out == "min" - + out = get_monitor_mode("classification") assert out == "max" - + with pytest.raises(ValueError, match="Invalid task") as e: get_monitor_mode("mix") @@ -114,34 +114,34 @@ def test_infer_in_channels(self): """Test infer_in_channels.""" in_channels = infer_in_channels(self.dataset_config_1, self.cliq_lift_transform) assert in_channels == [7] - + in_channels = infer_in_channels(self.dataset_config_2, None) assert in_channels == [1433] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/MUTAG"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [7,4,4,4] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/MUTAG", "dataset.parameters.preserve_edge_attr_if_lifted=False"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [7,7,7,7] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/MUTAG", "dataset.parameters.preserve_edge_attr_if_lifted=False", "transforms.graph2simplicial_lifting.feature_lifting=Concatenation"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [7,14,42,168] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/MUTAG", "transforms.graph2simplicial_lifting.feature_lifting=Concatenation"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [7,4,4,4] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/cocitation_cora", "transforms.graph2simplicial_lifting.feature_lifting=Concatenation"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [1433,2866,8598,34392] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=simplicial/topotune", "dataset=graph/cocitation_cora"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [1433,1433,1433,1433] - + cfg = hydra.compose(config_name="run.yaml", overrides=["model=graph/gcn", "dataset=simplicial/mantra_orientation"], return_hydra_config=True) in_channels = infer_in_channels(cfg.dataset, cfg.transforms) assert in_channels == [1] @@ -171,12 +171,12 @@ def test_infer_topotune_num_cell_dimensions(self): neighborhoods = ["down_incidence-2"] out = infer_topotune_num_cell_dimensions(neighborhoods) assert out == 3 - + def test_get_default_metrics(self): """Test get_default_metrics.""" out = get_default_metrics("classification", ["accuracy", "precision"]) assert out == ["accuracy", "precision"] - + out = get_default_metrics("classification") assert out == ["accuracy", "precision", "recall", "auroc"] @@ -191,7 +191,7 @@ def test_check_pses_in_transforms_empty(self): transforms = OmegaConf.create({}) result = check_pses_in_transforms(transforms) assert result == 0 - + def test_single_transform_lappe_with_eigenvalues(self): """Test single transform with LapPE including eigenvalues.""" transforms = OmegaConf.create({ @@ -483,7 +483,7 @@ def test_check_pses_in_transforms_complex_scenario(self): ]) def test_check_pses_in_transforms_lappe_parametrized(self, max_pe_dim, include_eigenvalues, expected): """Parametrized test for LapPE with different configurations. - + Parameters ---------- max_pe_dim : int @@ -512,7 +512,7 @@ def test_check_pses_in_transforms_lappe_parametrized(self, max_pe_dim, include_e ]) def test_check_pses_in_transforms_combined_parametrized(self, lappe_dim, rwse_dim, expected): """Parametrized test for CombinedPSEs with different dimension combinations. - + Parameters ---------- lappe_dim : int diff --git a/test/utils/test_instantiators.py b/test/utils/test_instantiators.py index 10476e4f1..8dcfeee6a 100644 --- a/test/utils/test_instantiators.py +++ b/test/utils/test_instantiators.py @@ -15,16 +15,16 @@ def setup_method(self): self.callback = OmegaConf.load("configs/callbacks/model_summary.yaml") self.logger = DictConfig( { - '_target_': 'lightning.pytorch.loggers.wandb.WandbLogger', - 'save_dir': '/', - 'offline': False, - 'id': None, - 'anonymous': None, - 'project': 'None', - 'log_model': False, - 'prefix': '', - 'group': '', - 'tags': [], + '_target_': 'lightning.pytorch.loggers.wandb.WandbLogger', + 'save_dir': '/', + 'offline': False, + 'id': None, + 'anonymous': None, + 'project': 'None', + 'log_model': False, + 'prefix': '', + 'group': '', + 'tags': [], 'job_type': '' } ) @@ -33,9 +33,8 @@ def test_instantiate_callbacks(self): """Test instantiate_callbacks.""" result = instantiate_callbacks(self.callback) assert type(result) == list - + def test_instantiate_loggers(self): """Test instantiate_loggers.""" result = instantiate_loggers(self.logger) assert type(result) == list - \ No newline at end of file diff --git a/test/utils/test_logging_utils.py b/test/utils/test_logging_utils.py index 87af894e5..9673e9b46 100644 --- a/test/utils/test_logging_utils.py +++ b/test/utils/test_logging_utils.py @@ -7,7 +7,7 @@ @patch("topobench.utils.logging_utils.OmegaConf.to_container") def test_log_hyperparameters(mock_to_container, mock_warning): """Test the log_hyperparameters function. - + Parameters ---------- mock_to_container : MagicMock @@ -57,4 +57,4 @@ def test_log_hyperparameters(mock_to_container, mock_warning): mock_warning.assert_called_once_with("Logger not found! Skipping hyperparameter logging...") if __name__ == "__main__": - pytest.main() \ No newline at end of file + pytest.main() diff --git a/test/utils/test_rich_utils.py b/test/utils/test_rich_utils.py index 840d4967f..aff1058c6 100644 --- a/test/utils/test_rich_utils.py +++ b/test/utils/test_rich_utils.py @@ -12,7 +12,7 @@ @patch("topobench.utils.rich_utils.HydraConfig.get") def test_print_config_tree(mock_hydra_config_get, mock_write_text, mock_rich_print, mock_syntax, mock_tree, mock_info): '''Test the print_config_tree function. - + Parameters ---------- mock_hydra_config_get : MagicMock @@ -54,7 +54,7 @@ def test_print_config_tree(mock_hydra_config_get, mock_write_text, mock_rich_pri with pytest.raises(FileNotFoundError): # Call the function with save_to_file=True print_config_tree(mock_cfg, save_to_file=True) - + @patch("topobench.utils.rich_utils.HydraConfig") @patch("topobench.utils.rich_utils.Prompt.ask") @@ -63,7 +63,7 @@ def test_print_config_tree(mock_hydra_config_get, mock_write_text, mock_rich_pri @patch("topobench.utils.rich_utils.rich.print") def test_enforce_tags_no_tags(mock_rich_print, mock_info, mock_warning, mock_prompt_ask, mock_hydra_config): """Test the enforce_tags function when no tags are provided in the config. - + Parameters ---------- mock_rich_print : MagicMock @@ -108,4 +108,4 @@ def test_enforce_tags_no_tags(mock_rich_print, mock_info, mock_warning, mock_pro if __name__ == "__main__": - pytest.main() \ No newline at end of file + pytest.main() diff --git a/test/utils/test_utils.py b/test/utils/test_utils.py index f55061f97..941ec1642 100644 --- a/test/utils/test_utils.py +++ b/test/utils/test_utils.py @@ -26,23 +26,23 @@ def test_get_metric_value(self): with pytest.raises(Exception) as e: get_metric_value(self.metric_dict, "some_metric") - + out = get_metric_value(self.metric_dict, None) assert out is None - + def test_extras(self): """Test extras.""" # extras(self.cfg) extras({}) - - + + def test_task_wrapper(self): """Test task_wrapper.""" d = DictConfig({'paths': {'output_dir': 'logs/'}}) def task_func(cfg: DictConfig): """Task function for testing task_wrapper. - + Parameters ---------- cfg : DictConfig @@ -54,14 +54,13 @@ def task_func(cfg: DictConfig): The metric and object dictionaries. """ return {'accuracy': torch.tensor([90])}, {'model': 'model'} - - + + out = task_wrapper(task_func)(d) - + assert out[0]['accuracy'] == 90., "Metric dictionary not returned correctly." assert out[1]['model'] == 'model', "Object dictionary not returned correctly." - + mock_task_func = MagicMock(side_effect=Exception("Test exception")) with pytest.raises(Exception, match="Test exception"): task_wrapper(mock_task_func)(d) - diff --git a/topobench/data/datasets/mantra_dataset.py b/topobench/data/datasets/mantra_dataset.py index 625aca4e1..5e7b754a8 100644 --- a/topobench/data/datasets/mantra_dataset.py +++ b/topobench/data/datasets/mantra_dataset.py @@ -181,7 +181,7 @@ def process(self) -> None: r"""Handle the data for the dataset. This method loads the JSON file for MANTRA for the specified manifold - dimmension, applies the respective preprocessing if specified and saves + dimension, applies the respective preprocessing if specified and saves the preprocessed data to the appropriate location. """ diff --git a/topobench/data/loaders/graph/ogbg_datasets.py b/topobench/data/loaders/graph/ogbg_datasets.py index fdeb590fd..42eea0bc4 100644 --- a/topobench/data/loaders/graph/ogbg_datasets.py +++ b/topobench/data/loaders/graph/ogbg_datasets.py @@ -44,7 +44,7 @@ def load_dataset(self) -> Dataset: dataset = PygGraphPropPredDataset( name=self.parameters.data_name, root=self.root_data_dir ) - # Conver attributes to float + # Convert attributes to float dataset._data.x = dataset._data.x.to(torch.float) # Squeeze the target tensor dataset._data.y = dataset._data.y.squeeze(1) diff --git a/topobench/data/utils/io_utils.py b/topobench/data/utils/io_utils.py index 372db85e6..bb9381443 100644 --- a/topobench/data/utils/io_utils.py +++ b/topobench/data/utils/io_utils.py @@ -325,14 +325,14 @@ def read_us_county_demos(path, year=2012, y_col="Election"): edge_index ) - # Conver mask to index + # Convert mask to index index = np.arange(mask.size(0))[mask] stat = stat.iloc[index] stat = stat.reset_index(drop=True) # Get new values for FIPS from current index # To understand why please print stat.iloc[[516, 517, 518, 519, 520]] for 2012 year - # Basically the FIPS values has been shifted + # Basically the FIPS values have been shifted stat["FIPS"] = stat.reset_index()["index"] # Create Election variable diff --git a/topobench/dataloader/dataloader.py b/topobench/dataloader/dataloader.py index 1f293b10c..2a3e2c755 100755 --- a/topobench/dataloader/dataloader.py +++ b/topobench/dataloader/dataloader.py @@ -49,7 +49,7 @@ def __init__( ) -> None: super().__init__() - # this line allows to access init params with 'self.hparams' attribute + # this line allows accessing init params with 'self.hparams' attribute # also ensures init params will be stored in ckpt self.save_hyperparameters( logger=False, diff --git a/topobench/model/model.py b/topobench/model/model.py index a7c688b47..2cbbae891 100755 --- a/topobench/model/model.py +++ b/topobench/model/model.py @@ -44,7 +44,7 @@ def __init__( ) -> None: super().__init__() - # This line allows to access init params with 'self.hparams' attribute + # This line allows accessing init params with 'self.hparams' attribute # also ensures init params will be stored in ckpt self.save_hyperparameters( logger=False, ignore=["backbone", "readout", "feature_encoder"] diff --git a/topobench/nn/backbones/combinatorial/gccn.py b/topobench/nn/backbones/combinatorial/gccn.py index 90e85e131..b02ed89dd 100644 --- a/topobench/nn/backbones/combinatorial/gccn.py +++ b/topobench/nn/backbones/combinatorial/gccn.py @@ -103,7 +103,7 @@ def intrarank_expand(self, params, src_rank, nbhd): Parameters ---------- params : dict - The parameters of the batch, containting the complex. + The parameters of the batch, containing the complex. src_rank : int The source rank. nbhd : str @@ -159,7 +159,7 @@ def interrank_expand( Parameters ---------- params : dict - The parameters of the batch, containting the complex. + The parameters of the batch, containing the complex. src_rank : int The source rank. dst_rank : int diff --git a/topobench/nn/backbones/graph/nsd_utils/__init__.py b/topobench/nn/backbones/graph/nsd_utils/__init__.py index e69de29bb..17523f629 100644 --- a/topobench/nn/backbones/graph/nsd_utils/__init__.py +++ b/topobench/nn/backbones/graph/nsd_utils/__init__.py @@ -0,0 +1 @@ +"""Utility modules for the Neural Sheaf Diffusion (NSD) backbone.""" diff --git a/topobench/nn/backbones/graph/nsd_utils/inductive_discrete_models.py b/topobench/nn/backbones/graph/nsd_utils/inductive_discrete_models.py index 30ae553b8..4c2a2dc31 100644 --- a/topobench/nn/backbones/graph/nsd_utils/inductive_discrete_models.py +++ b/topobench/nn/backbones/graph/nsd_utils/inductive_discrete_models.py @@ -64,16 +64,12 @@ def __init__(self, config): ) nn.init.orthogonal_(self.lin_right_weights[-1].weight.data) for _i in range(self.layers): - self.lin_left_weights.append( - nn.Linear(self.d, self.d, bias=False) - ) + self.lin_left_weights.append(nn.Linear(self.d, self.d, bias=False)) nn.init.eye_(self.lin_left_weights[-1].weight.data) self.sheaf_learners = nn.ModuleList() - num_sheaf_learners = min( - self.layers, self.layers - ) + num_sheaf_learners = min(self.layers, self.layers) for _i in range(num_sheaf_learners): self.sheaf_learners.append( LocalConcatSheafLearner( @@ -208,17 +204,13 @@ def __init__(self, config): ) nn.init.orthogonal_(self.lin_right_weights[-1].weight.data) for _i in range(self.layers): - self.lin_left_weights.append( - nn.Linear(self.d, self.d, bias=False) - ) + self.lin_left_weights.append(nn.Linear(self.d, self.d, bias=False)) nn.init.eye_(self.lin_left_weights[-1].weight.data) self.sheaf_learners = nn.ModuleList() self.weight_learners = nn.ModuleList() - num_sheaf_learners = min( - self.layers, self.layers - ) + num_sheaf_learners = min(self.layers, self.layers) for _i in range(num_sheaf_learners): self.sheaf_learners.append( LocalConcatSheafLearner( @@ -397,16 +389,12 @@ def __init__(self, config): ) nn.init.orthogonal_(self.lin_right_weights[-1].weight.data) for _i in range(self.layers): - self.lin_left_weights.append( - nn.Linear(self.d, self.d, bias=False) - ) + self.lin_left_weights.append(nn.Linear(self.d, self.d, bias=False)) nn.init.eye_(self.lin_left_weights[-1].weight.data) self.sheaf_learners = nn.ModuleList() - num_sheaf_learners = min( - self.layers, self.layers - ) + num_sheaf_learners = min(self.layers, self.layers) for _i in range(num_sheaf_learners): self.sheaf_learners.append( LocalConcatSheafLearner( diff --git a/topobench/nn/backbones/graph/nsd_utils/laplace.py b/topobench/nn/backbones/graph/nsd_utils/laplace.py index 55903396f..606311620 100644 --- a/topobench/nn/backbones/graph/nsd_utils/laplace.py +++ b/topobench/nn/backbones/graph/nsd_utils/laplace.py @@ -214,6 +214,7 @@ def compute_learnable_diag_laplacian_indices( return diag_indices, non_diag_indices + def mergesp(index1, value1, index2, value2): """ Merge two sparse matrices with disjoint indices into one. diff --git a/topobench/nn/backbones/graph/nsd_utils/laplacian_builders.py b/topobench/nn/backbones/graph/nsd_utils/laplacian_builders.py index fb6da3ea8..0da64aab6 100644 --- a/topobench/nn/backbones/graph/nsd_utils/laplacian_builders.py +++ b/topobench/nn/backbones/graph/nsd_utils/laplacian_builders.py @@ -103,7 +103,7 @@ def scalar_normalise(self, diag, tril, row, col): assert diag.dim() == 2 d = diag.size(-1) diag_sqrt_inv = (diag + 1).pow(-0.5) - + diag_sqrt_inv = ( diag_sqrt_inv.view(-1, 1, 1) if tril.dim() > 2 @@ -122,6 +122,7 @@ def scalar_normalise(self, diag, tril, row, col): return diag_maps, non_diag_maps + class DiagLaplacianBuilder(LaplacianBuilder): """ Builder for sheaf Laplacian with diagonal restriction maps. @@ -199,6 +200,7 @@ def forward(self, maps): return (edge_index, weights), saved_tril_maps + class NormConnectionLaplacianBuilder(LaplacianBuilder): """ Builder for normalized bundle sheaf Laplacian with orthogonal restriction maps. @@ -254,7 +256,7 @@ def forward(self, map_params): """ assert len(map_params.size()) == 2 assert map_params.size(1) == self.d * (self.d + 1) // 2 - + _, full_right_idx = self.full_left_right_idx left_idx, right_idx = self.left_right_idx tril_row, tril_col = self.vertex_tril_idx @@ -294,6 +296,7 @@ def forward(self, map_params): return (edge_index, weights), saved_tril_maps + class GeneralLaplacianBuilder(LaplacianBuilder): """ Builder for general sheaf Laplacian with full matrix restriction maps. diff --git a/topobench/nn/backbones/graph/nsd_utils/orthogonal.py b/topobench/nn/backbones/graph/nsd_utils/orthogonal.py index 5c1ae697b..25092f617 100644 --- a/topobench/nn/backbones/graph/nsd_utils/orthogonal.py +++ b/topobench/nn/backbones/graph/nsd_utils/orthogonal.py @@ -1,7 +1,7 @@ +"""Orthogonal transformations for sheaf diffusion.""" # Copyright 2022 Twitter, Inc. # SPDX-License-Identifier: Apache-2.0 - import torch from torch import nn diff --git a/topobench/nn/backbones/graph/nsd_utils/sheaf_base.py b/topobench/nn/backbones/graph/nsd_utils/sheaf_base.py index 3914d11de..c6e5b181f 100644 --- a/topobench/nn/backbones/graph/nsd_utils/sheaf_base.py +++ b/topobench/nn/backbones/graph/nsd_utils/sheaf_base.py @@ -1,3 +1,4 @@ +"""Base classes for sheaf neural network layers.""" # Copyright 2022 Twitter, Inc. # SPDX-License-Identifier: Apache-2.0 diff --git a/topobench/nn/backbones/graph/nsd_utils/sheaf_models.py b/topobench/nn/backbones/graph/nsd_utils/sheaf_models.py index 239a7926f..352d64dff 100644 --- a/topobench/nn/backbones/graph/nsd_utils/sheaf_models.py +++ b/topobench/nn/backbones/graph/nsd_utils/sheaf_models.py @@ -1,3 +1,5 @@ +"""Sheaf diffusion model implementations.""" + from abc import abstractmethod import numpy as np diff --git a/topobench/nn/backbones/hypergraph/edgnn.py b/topobench/nn/backbones/hypergraph/edgnn.py index b00915946..a454a0e5e 100644 --- a/topobench/nn/backbones/hypergraph/edgnn.py +++ b/topobench/nn/backbones/hypergraph/edgnn.py @@ -159,7 +159,7 @@ class customMLP(nn.Module): Number of layers. dropout : float, optional Dropout rate. Defaults to 0.5. - Normalization : str, optiona + Normalization : str, optional Normalization method. Defaults to 'bn'. InputNorm : bool, optional Whether to normalize input features. Defaults to False. diff --git a/topobench/transforms/data_manipulations/equal_gaus_features.py b/topobench/transforms/data_manipulations/equal_gaus_features.py index 066d115f8..2a8fea14e 100644 --- a/topobench/transforms/data_manipulations/equal_gaus_features.py +++ b/topobench/transforms/data_manipulations/equal_gaus_features.py @@ -20,7 +20,7 @@ def __init__(self, **kwargs): super().__init__() self.type = "generate_non_informative_features" - # Torch generate feature vector from gaus distribution + # Torch generate feature vector from Gaussian distribution self.mean = kwargs["mean"] self.std = kwargs["std"] self.feature_vector = kwargs["num_features"] diff --git a/topobench/transforms/data_manipulations/group_homophily.py b/topobench/transforms/data_manipulations/group_homophily.py index 1a7f60871..733a1e267 100644 --- a/topobench/transforms/data_manipulations/group_homophily.py +++ b/topobench/transforms/data_manipulations/group_homophily.py @@ -59,7 +59,7 @@ def forward(self, data: torch_geometric.data.Data): zip(unique_labels.numpy(), count_labels.numpy(), strict=False) ) - # Enhancment: avoid to_dense + # Enhancement: avoid to_dense H = data.incidence_hyperedges.to_dense() he_cardinalities = H.sum(0) @@ -191,7 +191,7 @@ def calculate_D_matrix( node_idxs = class_node_idxs[unique_class] # Extract from D matrix only rows corresponding to nodes belonging to current 'unique_class' - # Transfose to be alligned with paper + # Transpose to be aligned with paper D_class = D[node_idxs, :].T # Denominator of (1) diff --git a/topobench/transforms/data_manipulations/redefine_simplicial_neighbourhoods.py b/topobench/transforms/data_manipulations/redefine_simplicial_neighbourhoods.py index ca35ee6eb..202ef9165 100644 --- a/topobench/transforms/data_manipulations/redefine_simplicial_neighbourhoods.py +++ b/topobench/transforms/data_manipulations/redefine_simplicial_neighbourhoods.py @@ -1,4 +1,4 @@ -"""An transform that redifines simplicial complex neighbourhood.""" +"""An transform that redefines simplicial complex neighbourhood.""" import torch_geometric @@ -9,7 +9,7 @@ class RedefineSimplicialNeighbourhoods( torch_geometric.transforms.BaseTransform ): - r"""An transform that redifines simplicial complex neighbourhood. + r"""An transform that redefines simplicial complex neighbourhood. Parameters ---------- diff --git a/topobench/transforms/liftings/graph2cell/discrete_configuration_complex_lifting.py b/topobench/transforms/liftings/graph2cell/discrete_configuration_complex_lifting.py index f53b5aa39..38c8af75a 100644 --- a/topobench/transforms/liftings/graph2cell/discrete_configuration_complex_lifting.py +++ b/topobench/transforms/liftings/graph2cell/discrete_configuration_complex_lifting.py @@ -20,7 +20,7 @@ class DiscreteConfigurationComplexLifting(Graph2CellLifting): Lift graphs to cell complexes by generating the k-th discrete configuration complex D_k(G) of the graph. This is a cube complex, which is similar to a simplicial complex except each n-dimensional cell is homeomorphic to a n-dimensional cube rather than an n-dimensional simplex. - The discrete configuration complex of order k consists of all sets of k unique edges or vertices of G, with the additional constraint that if an edge e is in a cell, then neither of the endpoints of e are in the cell. For examples of different graphs and their configuration complexes, see the tutorial. + The discrete configuration complex of order k consists of all sets of k unique edges or vertices of G, with the additional constraint that if an edge e is in a cell, then neither of the endpoints of e is in the cell. For examples of different graphs and their configuration complexes, see the tutorial. Note that since TopoNetx only supports cell complexes of dimension 2, if you generate a configuration complex of order k > 2 this will only produce the 2-skeleton. diff --git a/topobench/transforms/liftings/graph2combinatorial/graph_induced_cc.py b/topobench/transforms/liftings/graph2combinatorial/graph_induced_cc.py index 5a06d02e1..c0f9387ed 100644 --- a/topobench/transforms/liftings/graph2combinatorial/graph_induced_cc.py +++ b/topobench/transforms/liftings/graph2combinatorial/graph_induced_cc.py @@ -100,12 +100,12 @@ def _sorted_hyperedge_indices(self, hyperedges): Create a list of pairs with the starts and lengths of hyperedges in ascending order of hyperedge size. Parameters - ------------ + ---------- hyperedges : torch.tensor A tensor with two rows: the first one for hyperedge indices, the second one for node indices. Returns - -------- + ------- list A list of pairs (start, length) sorted according to length (ascending). """ @@ -141,7 +141,7 @@ def build_paths(overlap_pairs): Returns ------- list - List of sequences of overlaping triangles. + List of sequences of overlapping triangles. """ parent = {} @@ -207,7 +207,7 @@ def find_overlapping_paths(lists): Returns ------- list - List of sequences of overlaping triangles. + List of sequences of overlapping triangles. """ one_element_overlap = [] two_elements_overlap = [] diff --git a/topobench/transforms/liftings/graph2hypergraph/forman_ricci_curvature_lifting.py b/topobench/transforms/liftings/graph2hypergraph/forman_ricci_curvature_lifting.py index 24cee031c..dbbc9cba6 100644 --- a/topobench/transforms/liftings/graph2hypergraph/forman_ricci_curvature_lifting.py +++ b/topobench/transforms/liftings/graph2hypergraph/forman_ricci_curvature_lifting.py @@ -15,7 +15,7 @@ class HypergraphFormanRicciCurvatureLifting(Graph2HypergraphLifting): """Lift graphs to hypergraph domain using Forman-Ricci curvature based backbone estimation. - This lifting identifies a network's structure-preserving, coarse geometry, i.e. its backbones, which lend themselves specifically to model information flows across wide areas of the network via hyperedges. To identify this coarse geometry we apply Forman-Ricci curvature to the original graph. Forman-Ricci curvature defines an edge-based network characteristic that reveals properties of a graph's community structure. In particular high absolute Forman-Ricci curvature exhibits a network's backbone, a coarse, structure preserving graph geometry that forms connections between major communities, most suitable to form hyperedges. In addition, Forman-Ricci curvature was found to be especially useful for network analysis since its intuitive notion allows for efficient computation that scales to large networks sizes. + This lifting identifies a network's structure-preserving, coarse geometry, i.e. its backbones, which lend themselves specifically to model information flows across wide areas of the network via hyperedges. To identify this coarse geometry we apply Forman-Ricci curvature to the original graph. Forman-Ricci curvature defines an edge-based network characteristic that reveals properties of a graph's community structure. In particular high absolute Forman-Ricci curvature exhibits a network's backbone, a coarse, structure preserving graph geometry that forms connections between major communities, most suitable to form hyperedges. In addition, Forman-Ricci curvature was found to be especially useful for network analysis since its intuitive notion allows for efficient computation that scales to large network sizes. Parameters ---------- diff --git a/topobench/transforms/liftings/graph2hypergraph/kernel_lifting.py b/topobench/transforms/liftings/graph2hypergraph/kernel_lifting.py index 50379778d..5d5e21c1a 100755 --- a/topobench/transforms/liftings/graph2hypergraph/kernel_lifting.py +++ b/topobench/transforms/liftings/graph2hypergraph/kernel_lifting.py @@ -299,7 +299,7 @@ def lift_topology( The lifted topology. Raises - ------- + ------ ValueError: if the input is incomplete or in incorrect format. """ if not torch_geometric.utils.is_undirected(data.edge_index): diff --git a/topobench/transforms/liftings/graph2hypergraph/mapper_lifting.py b/topobench/transforms/liftings/graph2hypergraph/mapper_lifting.py index dc46948b6..2bf8ddfca 100644 --- a/topobench/transforms/liftings/graph2hypergraph/mapper_lifting.py +++ b/topobench/transforms/liftings/graph2hypergraph/mapper_lifting.py @@ -23,7 +23,7 @@ class MapperCover: resolution : int, optional The number of intervals in the MapperCover. Default is 10. gain : float, optional - The proportion of overlap between consectutive intervals + The proportion of overlap between consecutive intervals in the MapperCover and should be value between 0 and 0.5. Default is 0.3. @@ -106,7 +106,7 @@ def _verify_cover_parameters(self): f"Resolution should be greater than 0. Currently, resolution is {self.resolution}." ) assert float(self.resolution).is_integer(), ( - f"Resolution must be an integer value. Currenly, resolution is {self.resolution}." + f"Resolution must be an integer value. Currently, resolution is {self.resolution}." ) @@ -142,7 +142,7 @@ class MapperLifting(Graph2HypergraphLifting): The number of intervals to construct the MapperCover. Default is 10. gain : float, optional - The percentage of overlap between consectutive intervals + The percentage of overlap between consecutive intervals in MapperCover and should be a value between 0 and 0.5. Default is 0.3. filter_func : object, optional diff --git a/topobench/transforms/liftings/graph2simplicial/latentclique_lifting.py b/topobench/transforms/liftings/graph2simplicial/latentclique_lifting.py index 55ac1c406..f8e6b2dae 100755 --- a/topobench/transforms/liftings/graph2simplicial/latentclique_lifting.py +++ b/topobench/transforms/liftings/graph2simplicial/latentclique_lifting.py @@ -605,7 +605,8 @@ def splitmerge(self): if clique_i == clique_j: clique_size = self.Z[clique_i].sum() - if clique_size <= 2: return # noqa + if clique_size <= 2: + return # noqa Z_prop = self.Z.copy() Z_prop = np.delete(Z_prop, clique_i, 0) @@ -853,7 +854,7 @@ def _get_beta_params(mean, var): # shape = (K, Ncols) # Z = csr_matrix((data, (rowidx, colidx)), shape).todense() -# # delte empty cliques +# # delete empty cliques # return Z[np.where(Z.sum(1) > 1)[0]] diff --git a/topobench/transforms/liftings/graph2simplicial/line_lifting.py b/topobench/transforms/liftings/graph2simplicial/line_lifting.py index 2bb81c01b..3f66a9a59 100644 --- a/topobench/transforms/liftings/graph2simplicial/line_lifting.py +++ b/topobench/transforms/liftings/graph2simplicial/line_lifting.py @@ -40,7 +40,7 @@ def lift_topology(self, data: torch_geometric.data.Data) -> dict: The input data to be lifted. Returns - ---------- + ------- dict The lifted topology. """ diff --git a/topobench/transforms/liftings/hypergraph2combinatorial/universal_strict_lifting.py b/topobench/transforms/liftings/hypergraph2combinatorial/universal_strict_lifting.py index 82da64e2f..fc1ed7c92 100644 --- a/topobench/transforms/liftings/hypergraph2combinatorial/universal_strict_lifting.py +++ b/topobench/transforms/liftings/hypergraph2combinatorial/universal_strict_lifting.py @@ -12,7 +12,7 @@ class UniversalStrictLifting(Hypergraph2CombinatorialLifting): r"""Lift hypergraphs to combinatorial complexes. - It works by assinging the smallest rank values such that subcells of any cell have strictly smaller rank. + It works by assigning the smallest rank values such that subcells of any cell have strictly smaller rank. Parameters ---------- @@ -100,12 +100,12 @@ def _sorted_hyperedge_indices(self, hyperedges): Create a list of pairs with the starts and lengths of hyperedges in ascending order of hyperedge size. Parameters - ------------ + ---------- hyperedges : torch.tensor A tensor with two rows: the first one for hyperedge indices, the second one for node indices. Returns - -------- + ------- list A list of pairs (start, length) sorted according to length (ascending). """ diff --git a/topobench/transforms/liftings/pointcloud2simplicial/base.py b/topobench/transforms/liftings/pointcloud2simplicial/base.py index a50df5ece..714a56c92 100755 --- a/topobench/transforms/liftings/pointcloud2simplicial/base.py +++ b/topobench/transforms/liftings/pointcloud2simplicial/base.py @@ -33,7 +33,7 @@ def _get_lifted_topology( The simplicial complex. Returns - --------- + ------- dict The lifted topology. """ diff --git a/topobench/transforms/liftings/pointcloud2simplicial/random_flag_complex.py b/topobench/transforms/liftings/pointcloud2simplicial/random_flag_complex.py index 23e45411e..e3d3bfcb1 100644 --- a/topobench/transforms/liftings/pointcloud2simplicial/random_flag_complex.py +++ b/topobench/transforms/liftings/pointcloud2simplicial/random_flag_complex.py @@ -136,7 +136,7 @@ def _get_lifted_topology( lifted_topology = get_complex_connectivity( simplicial_complex, self.complex_dim, signed=False ) - # Computing the persitence to obtain the Betti numbers + # Computing the persistence to obtain the Betti numbers st.compute_persistence(persistence_dim_max=True) # Save the Betti numbers in the Data object diff --git a/topobench/transforms/liftings/simplicial2combinatorial/base.py b/topobench/transforms/liftings/simplicial2combinatorial/base.py index b5e24df6c..e26cdc1c7 100644 --- a/topobench/transforms/liftings/simplicial2combinatorial/base.py +++ b/topobench/transforms/liftings/simplicial2combinatorial/base.py @@ -8,7 +8,7 @@ class Simplicial2CombinatorialLifting(SimplicialLifting): Parameters ---------- - **kwargs : optiona""l + **kwargs : optional Additional arguments for the class. """ diff --git a/topobench/utils/config_resolvers.py b/topobench/utils/config_resolvers.py index 5c3adf07e..e797b0c23 100644 --- a/topobench/utils/config_resolvers.py +++ b/topobench/utils/config_resolvers.py @@ -386,7 +386,9 @@ def check_for_type_feature_lifting(transforms, lifting): else: # ProjectionSum feature lifting by default - return [num_features[0]] * (transforms[lifting].complex_dim + 1) + return [num_features[0]] * ( + transforms[lifting].complex_dim + 1 + ) # If preserve_edge_attr == True else: return list(num_features) + [num_features[1]] * ( diff --git a/uv_env_setup.sh b/uv_env_setup.sh index e4d31eb1e..d49d86fac 100644 --- a/uv_env_setup.sh +++ b/uv_env_setup.sh @@ -30,7 +30,7 @@ elif [ "$PLATFORM" == "cu121" ]; then PYG_URL="https://data.pyg.org/whl/torch-${TORCH_VER}+cu121.html" else echo "❌ Error: Invalid platform '$PLATFORM'. Use: cpu, cu118, or cu121." - exit 1 + return 1 2>/dev/null || exit 1 fi echo "⚙️ Updating pyproject.toml..." @@ -79,4 +79,4 @@ echo "=======================================================" echo "🎉 Setup Complete!" echo "=======================================================" python -c "import sys; import torch; print(f'✅ Python Ver : {sys.version.split()[0]}'); print(f'✅ Torch Version : {torch.__version__}'); print(f'✅ CUDA Available: {torch.cuda.is_available()}'); print(f'✅ CUDA Version : {torch.version.cuda}')" -echo "=======================================================" \ No newline at end of file +echo "======================================================="