Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,776 changes: 5,776 additions & 0 deletions 2026_tdl_challenge/outputs/2026-07-29_17-16-55/results.json

Large diffs are not rendered by default.

19 changes: 11 additions & 8 deletions 2026_tdl_challenge/run_evaluation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"id": "53c1d2fb",
"metadata": {},
"outputs": [],
Expand Down Expand Up @@ -98,13 +98,13 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": null,
"id": "config_cell",
"metadata": {},
"outputs": [],
"source": [
"# Your model configuration (e.g., \"graph/gcn\", \"graph/gin\", \"graph/gat\")\n",
"MODEL_CONFIG = \"graph/gin\""
"MODEL_CONFIG = \"simplicial/ctnn_diag\""
]
},
{
Expand All @@ -119,7 +119,7 @@
},
{
"cell_type": "code",
"execution_count": 51,
"execution_count": null,
"id": "f52c90bd",
"metadata": {},
"outputs": [],
Expand All @@ -141,6 +141,7 @@
"import json\n",
"import warnings\n",
"\n",
"\n",
"def hash_remaining_cells(\n",
" notebook_path: str, marker_string: str = \"# UNIQUE_HASH_MARKER\"\n",
") -> str:\n",
Expand Down Expand Up @@ -176,7 +177,9 @@
" )\n",
"\n",
" # Join with an explicit separator between cells so a cell boundary is never ambiguous with in-cell content\n",
" cell_sources = [\"\".join(cell.get(\"source\", [])) for cell in cells[start_index:]]\n",
" cell_sources = [\n",
" \"\".join(cell.get(\"source\", [])) for cell in cells[start_index:]\n",
" ]\n",
" content_to_hash = \"\\n\".join(cell_sources)\n",
"\n",
" normalized = \" \".join(content_to_hash.split())\n",
Expand All @@ -193,7 +196,7 @@
" stacklevel=2,\n",
" )\n",
"else:\n",
" print(\"Notebook content is verified.\")\n"
" print(\"Notebook content is verified.\")"
]
},
{
Expand Down Expand Up @@ -295,7 +298,7 @@
],
"metadata": {
"kernelspec": {
"display_name": "tb",
"display_name": "topobench (3.11.11)",
"language": "python",
"name": "python3"
},
Expand All @@ -309,7 +312,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.3"
"version": "3.11.11"
}
},
"nbformat": 4,
Expand Down
82 changes: 82 additions & 0 deletions configs/model/simplicial/ctnn.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copresheaf Topological Neural Network (CTNN) with SheafFC transport maps.
# Hajij et al., "Copresheaf Topological Neural Networks: A Generalized Deep
# Learning Framework", NeurIPS 2025 (https://arxiv.org/abs/2505.21251).
#
# Layer. Definition 10 of the paper, instantiated with copresheaf attention as
# the message function (Definition 11 within a rank, Definition 16 across
# ranks) and a residual MLP with normalisation as the update. This is the
# Copresheaf Cellular Transformer that Appendix H.5 evaluates on simplicial
# complexes.
#
# Transport maps. `sheaf_fc` is the SheafFC map of Table 18, which Appendix
# H.5 instantiates: rho = Id + tanh(W [q_x; k_y]) with W zero-initialised, so
# every map starts at the identity. The two other entries of the catalogue that
# the paper applies to topological domains ship as the sibling configs
# `simplicial/ctnn_spd` and `simplicial/ctnn_diag`.
_target_: topobench.model.TBModel

model_name: ctnn
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}}
out_channels: 64
proj_dropout: 0.0
selected_dimensions:
- 0
- 1
- 2

backbone:
_target_: topobench.nn.backbones.CopresheafTNN
channels: ${model.feature_encoder.out_channels}
# The collection N = {N_k} of Definition 10. Every entry names one copresheaf
# adjacency or incidence matrix (Definition 8) to transport along. These are
# the three message paths of the layer displayed in Appendix H.5, all of them
# into rank 0: 0 <- 0, 0 <- 1, and 0 <- 2. The last one is direct, matching
# the paper's `rho_{t->v}^{(0<-2)}` for `t` containing `v`; `2-down_incidence-2`
# is the composite `incidence_1 . incidence_2`, whose support is exactly
# {(v, t) : v in t}. Appendix H.5 has no upward route, so ranks 1 and 2 are
# not updated by the backbone and reach the readout as encoder features.
#
# Deviation: Appendix H.5 defines its 0 <-> 0 adjacency through 2-cells,
# `{w | exists t in X^2 : v, w in t}`, which is `2-up_adjacency-0` here. We
# use `up_adjacency-0` (adjacency through 1-cells) because GraphUniverse
# graphs have average degree 1-5, so triangle-adjacency is nearly empty and
# would silence the 0 <- 0 path on most nodes.
neighborhoods:
- up_adjacency-0
- down_incidence-1
- 2-down_incidence-2
layers: 2 # Section 6.1 and Appendix H.5 both use two transformer layers
# Stalk dimension d = channels / heads = 4. The SheafFC transport carries
# W in R^{2d x d^2} per head (Table 18), so d drives the parameter count
# quadratically: d=16 puts 59% of the backbone's weights in the transport
# maps and overfits GraphUniverse. d=4 is the stalk width the paper uses for
# its classification tasks (Section 6.3, Appendix H.3.1, Appendix H.4); the
# d=16 of Section 6.1 and Appendix H.5 is for physics regression.
heads: 16
copresheaf_map: sheaf_fc # Table 18 SheafFC, the map of Appendix H.5
dropout: 0.2 # Appendix H.5 reports layer norm and grad clipping, no dropout.
# We turn dropout on to avoid overfitting on small datasets.

backbone_wrapper:
_target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name}
_partial_: true
wrapper_name: TuneWrapper
out_channels: ${model.feature_encoder.out_channels}
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}}

readout:
_target_: topobench.nn.readouts.${model.readout.readout_name}
readout_name: PropagateSignalDown # Use <NoReadOut> in case readout is not needed Options: PropagateSignalDown
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}} # The highest order of cell dimensions to consider
hidden_dim: ${model.feature_encoder.out_channels}
out_channels: ${dataset.parameters.num_classes}
task_level: ${define_task_level:${dataset.parameters.task_level},${dataset.split_params.learning_setting}} # Handles the edge case of node-inductive task
pooling_type: sum

# compile model for faster training with pytorch 2.0
compile: false
87 changes: 87 additions & 0 deletions configs/model/simplicial/ctnn_diag.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Copresheaf Topological Neural Network (CTNN) with diagonal transport maps.
# Hajij et al., "Copresheaf Topological Neural Networks: A Generalized Deep
# Learning Framework", NeurIPS 2025 (https://arxiv.org/abs/2505.21251).
#
# Layer. Definition 10 of the paper, instantiated with copresheaf attention as
# the message function (Definition 11 within a rank, Definition 16 across
# ranks) and a residual MLP with normalisation as the update. This is the
# Copresheaf Cellular Transformer that Appendix H.5 evaluates on simplicial
# complexes.
#
# Transport maps. `diagonal` is the Diagonal MLP Map of Table 18,
# rho = diag(sigma(MLP[q_x, k_y])). Confining transport to the diagonal is the
# d^2 -> d reduction that Table 19 notes: it is the cheapest way to run the
# layer on a complex whose higher-rank neighborhoods are dense, and it is the
# parameterisation the copresheaf GCN and GraphSAGE of Section 6.2 use. Unlike
# the SheafFC and SheafSPD maps it carries no identity term, so a message can be
# gated off entirely.
#
# This config differs from `simplicial/ctnn` in exactly one entry,
# `backbone.copresheaf_map`; every other hyperparameter is identical.
_target_: topobench.model.TBModel

model_name: ctnn_diag
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}}
out_channels: 64
proj_dropout: 0.0
selected_dimensions:
- 0
- 1
- 2

backbone:
_target_: topobench.nn.backbones.CopresheafTNN
channels: ${model.feature_encoder.out_channels}
# The collection N = {N_k} of Definition 10. Every entry names one copresheaf
# adjacency or incidence matrix (Definition 8) to transport along. These are
# the three message paths of the layer displayed in Appendix H.5, all of them
# into rank 0: 0 <- 0, 0 <- 1, and 0 <- 2. The last one is direct, matching
# the paper's `rho_{t->v}^{(0<-2)}` for `t` containing `v`; `2-down_incidence-2`
# is the composite `incidence_1 . incidence_2`, whose support is exactly
# {(v, t) : v in t}. Appendix H.5 has no upward route, so ranks 1 and 2 are
# not updated by the backbone and reach the readout as encoder features.
#
# Deviation: Appendix H.5 defines its 0 <-> 0 adjacency through 2-cells,
# `{w | exists t in X^2 : v, w in t}`, which is `2-up_adjacency-0` here. We
# use `up_adjacency-0` (adjacency through 1-cells) because GraphUniverse
# graphs have average degree 1-5, so triangle-adjacency is nearly empty and
# would silence the 0 <- 0 path on most nodes.
neighborhoods:
- up_adjacency-0
- down_incidence-1
- 2-down_incidence-2
layers: 2 # Section 6.1 and Appendix H.5 both use two transformer layers
# Stalk dimension d = channels / heads = 4. Transport parameters scale with d
# faster than linearly for every map of Table 18, so d=16 puts most of the
# backbone's weights in the transport maps and overfits GraphUniverse. d=4 is
# the stalk width the paper uses for its classification tasks (Section 6.3,
# Appendix H.3.1, Appendix H.4); the d=16 of Section 6.1 and Appendix H.5 is
# for physics regression.
heads: 16
copresheaf_map: diagonal # Table 18 Diagonal MLP Map, O(d) instead of O(d^2)
dropout: 0.5 # Appendix H.5 reports layer norm and grad clipping, no dropout.
# We turn dropout on to avoid overfitting on small datasets.

backbone_wrapper:
_target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name}
_partial_: true
wrapper_name: TuneWrapper
out_channels: ${model.feature_encoder.out_channels}
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}}

readout:
_target_: topobench.nn.readouts.${model.readout.readout_name}
readout_name: PropagateSignalDown # Use <NoReadOut> in case readout is not needed Options: PropagateSignalDown
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}} # The highest order of cell dimensions to consider
hidden_dim: ${model.feature_encoder.out_channels}
out_channels: ${dataset.parameters.num_classes}
task_level: ${define_task_level:${dataset.parameters.task_level},${dataset.split_params.learning_setting}} # Handles the edge case of node-inductive task
pooling_type: sum

# compile model for faster training with pytorch 2.0
compile: false
86 changes: 86 additions & 0 deletions configs/model/simplicial/ctnn_spd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Copresheaf Topological Neural Network (CTNN) with SheafSPD transport maps.
# Hajij et al., "Copresheaf Topological Neural Networks: A Generalized Deep
# Learning Framework", NeurIPS 2025 (https://arxiv.org/abs/2505.21251).
#
# Layer. Definition 10 of the paper, instantiated with copresheaf attention as
# the message function (Definition 11 within a rank, Definition 16 across
# ranks) and a residual MLP with normalisation as the update. This is the
# Copresheaf Cellular Transformer that Appendix H.5 evaluates on simplicial
# complexes.
#
# Transport maps. `sheaf_spd` is the SheafSPD map of Table 18, which Appendix
# H.5 offers as the constrained alternative to the SheafFC map of
# `simplicial/ctnn`: rho = Id + Q Q^T is symmetric positive definite with every
# eigenvalue at least one, so transport can stretch a stalk but never reflect
# or contract it. Appendix H.1 credits that alignment with a diffusion tensor
# for the gain on viscous flow.
#
# This config differs from `simplicial/ctnn` in exactly one entry,
# `backbone.copresheaf_map`; every other hyperparameter is identical.
_target_: topobench.model.TBModel

model_name: ctnn_spd
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}}
out_channels: 64
proj_dropout: 0.0
selected_dimensions:
- 0
- 1
- 2

backbone:
_target_: topobench.nn.backbones.CopresheafTNN
channels: ${model.feature_encoder.out_channels}
# The collection N = {N_k} of Definition 10. Every entry names one copresheaf
# adjacency or incidence matrix (Definition 8) to transport along. These are
# the three message paths of the layer displayed in Appendix H.5, all of them
# into rank 0: 0 <- 0, 0 <- 1, and 0 <- 2. The last one is direct, matching
# the paper's `rho_{t->v}^{(0<-2)}` for `t` containing `v`; `2-down_incidence-2`
# is the composite `incidence_1 . incidence_2`, whose support is exactly
# {(v, t) : v in t}. Appendix H.5 has no upward route, so ranks 1 and 2 are
# not updated by the backbone and reach the readout as encoder features.
#
# Deviation: Appendix H.5 defines its 0 <-> 0 adjacency through 2-cells,
# `{w | exists t in X^2 : v, w in t}`, which is `2-up_adjacency-0` here. We
# use `up_adjacency-0` (adjacency through 1-cells) because GraphUniverse
# graphs have average degree 1-5, so triangle-adjacency is nearly empty and
# would silence the 0 <- 0 path on most nodes.
neighborhoods:
- up_adjacency-0
- down_incidence-1
- 2-down_incidence-2
layers: 2 # Section 6.1 and Appendix H.5 both use two transformer layers
# Stalk dimension d = channels / heads = 4. Transport parameters scale with d
# faster than linearly for every map of Table 18, so d=16 puts most of the
# backbone's weights in the transport maps and overfits GraphUniverse. d=4 is
# the stalk width the paper uses for its classification tasks (Section 6.3,
# Appendix H.3.1, Appendix H.4); the d=16 of Section 6.1 and Appendix H.5 is
# for physics regression.
heads: 16
copresheaf_map: sheaf_spd # Table 18 SheafSPD, the constrained option of Appendix H.5
dropout: 0.2 # Appendix H.5 reports layer norm and grad clipping, no dropout.
# We turn dropout on to avoid overfitting on small datasets.

backbone_wrapper:
_target_: topobench.nn.wrappers.${model.backbone_wrapper.wrapper_name}
_partial_: true
wrapper_name: TuneWrapper
out_channels: ${model.feature_encoder.out_channels}
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}}

readout:
_target_: topobench.nn.readouts.${model.readout.readout_name}
readout_name: PropagateSignalDown # Use <NoReadOut> in case readout is not needed Options: PropagateSignalDown
num_cell_dimensions: ${infer_topotune_num_cell_dimensions:${oc.select:model.backbone.neighborhoods}} # The highest order of cell dimensions to consider
hidden_dim: ${model.feature_encoder.out_channels}
out_channels: ${dataset.parameters.num_classes}
task_level: ${define_task_level:${dataset.parameters.task_level},${dataset.split_params.learning_setting}} # Handles the edge case of node-inductive task
pooling_type: sum

# compile model for faster training with pytorch 2.0
compile: false
7 changes: 7 additions & 0 deletions docs/api/topobench.nn.backbones.simplicial.ctnn.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
topobench.nn.backbones.simplicial.ctnn module
=============================================

.. automodule:: topobench.nn.backbones.simplicial.ctnn
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/api/topobench.nn.backbones.simplicial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Submodules
.. toctree::
:maxdepth: 4

topobench.nn.backbones.simplicial.ctnn
topobench.nn.backbones.simplicial.sccnn
Loading
Loading