diff --git a/configs/datasets/toy_point_cloud.yaml b/configs/datasets/toy_point_cloud.yaml new file mode 100644 index 00000000..b1545947 --- /dev/null +++ b/configs/datasets/toy_point_cloud.yaml @@ -0,0 +1,12 @@ +data_domain: point_cloud +data_type: toy_dataset +data_name: toy_point_cloud +data_dir: datasets/${data_domain}/${data_type} + +# Dataset parameters +num_points: 8 +num_classes: 2 + +num_features: 1 +task: classification +loss_type: cross_entropy diff --git a/configs/transforms/liftings/pointcloud2simplicial/witness_lifting.yaml b/configs/transforms/liftings/pointcloud2simplicial/witness_lifting.yaml new file mode 100644 index 00000000..bc8143a0 --- /dev/null +++ b/configs/transforms/liftings/pointcloud2simplicial/witness_lifting.yaml @@ -0,0 +1,3 @@ +transform_type: "lifting" +transform_name: "WitnessLifting" +feature_lifting: ProjectionSum diff --git a/modules/data/load/loaders.py b/modules/data/load/loaders.py index 8ccafb11..4ce265e3 100755 --- a/modules/data/load/loaders.py +++ b/modules/data/load/loaders.py @@ -12,6 +12,7 @@ load_cell_complex_dataset, load_hypergraph_pickle_dataset, load_manual_graph, + load_point_cloud, load_simplicial_dataset, ) @@ -204,3 +205,34 @@ def load( torch_geometric.data.Dataset object containing the loaded data. """ return load_hypergraph_pickle_dataset(self.parameters) + + +class PointCloudLoader(AbstractLoader): + r"""Loader for point-cloud dataset. + Parameters + ---------- + parameters: DictConfig + Configuration parameters + """ + + def __init__(self, parameters: DictConfig): + super().__init__(parameters) + self.parameters = parameters + self.data_dir = self.parameters["data_dir"] + if "num_classes" not in self.cfg: + self.cfg["num_classes"] = 2 + + def load(self) -> torch_geometric.data.Dataset: + r"""Load point-cloud dataset. + Parameters + ---------- + None + Returns + ------- + torch_geometric.data.Dataset + torch_geometric.data.Dataset object containing the loaded data. + """ + data = load_point_cloud( + num_classes=self.cfg["num_classes"], num_points=self.cfg["num_points"] + ) + return CustomDataset([data], self.cfg["data_dir"]) diff --git a/modules/data/utils/utils.py b/modules/data/utils/utils.py index 93ab5021..f1a913bd 100755 --- a/modules/data/utils/utils.py +++ b/modules/data/utils/utils.py @@ -50,16 +50,16 @@ def get_complex_connectivity(complex, max_rank, signed=False): ) except ValueError: # noqa: PERF203 if connectivity_info == "incidence": - connectivity[f"{connectivity_info}_{rank_idx}"] = ( - generate_zero_sparse_connectivity( - m=practical_shape[rank_idx - 1], n=practical_shape[rank_idx] - ) + connectivity[ + f"{connectivity_info}_{rank_idx}" + ] = generate_zero_sparse_connectivity( + m=practical_shape[rank_idx - 1], n=practical_shape[rank_idx] ) else: - connectivity[f"{connectivity_info}_{rank_idx}"] = ( - generate_zero_sparse_connectivity( - m=practical_shape[rank_idx], n=practical_shape[rank_idx] - ) + connectivity[ + f"{connectivity_info}_{rank_idx}" + ] = generate_zero_sparse_connectivity( + m=practical_shape[rank_idx], n=practical_shape[rank_idx] ) connectivity["shape"] = practical_shape return connectivity @@ -283,6 +283,17 @@ def load_hypergraph_pickle_dataset(cfg): return data +def load_point_cloud(num_classes: int = 2, num_points: int = 18, seed: int = 42): + """Create a toy point cloud dataset""" + rng = np.random.default_rng(seed) + + points = torch.tensor(rng.random((num_points, 2)), dtype=torch.float) + classes = torch.tensor(rng.integers(num_classes, size=num_points), dtype=torch.long) + features = torch.tensor(rng.integers(3, size=(num_points, 1)), dtype=torch.float) + + return torch_geometric.data.Data(x=features, y=classes, pos=points) + + def load_manual_graph(): """Create a manual graph for testing purposes.""" # Define the vertices (just 8 vertices) diff --git a/modules/transforms/data_transform.py b/modules/transforms/data_transform.py index 59253ecf..6a950ce8 100755 --- a/modules/transforms/data_transform.py +++ b/modules/transforms/data_transform.py @@ -15,6 +15,9 @@ from modules.transforms.liftings.graph2simplicial.clique_lifting import ( SimplicialCliqueLifting, ) +from modules.transforms.liftings.pointcloud2simplicial.witness_lifting import ( + WitnessLifting, +) TRANSFORMS = { # Graph -> Hypergraph @@ -23,6 +26,8 @@ "SimplicialCliqueLifting": SimplicialCliqueLifting, # Graph -> Cell Complex "CellCycleLifting": CellCycleLifting, + # Point-cloud -> Simplicial Complex + "WitnessLifting": WitnessLifting, # Feature Liftings "ProjectionSum": ProjectionSum, # Data Manipulations diff --git a/modules/transforms/liftings/pointcloud2simplicial/witness_lifting.py b/modules/transforms/liftings/pointcloud2simplicial/witness_lifting.py new file mode 100644 index 00000000..bf843903 --- /dev/null +++ b/modules/transforms/liftings/pointcloud2simplicial/witness_lifting.py @@ -0,0 +1,74 @@ +import gudhi as gd +import torch +import torch_geometric +from toponetx.classes import SimplicialComplex + +from modules.data.utils.utils import get_complex_connectivity +from modules.transforms.liftings.pointcloud2simplicial.base import ( + PointCloud2SimplicialLifting, +) + + +class WitnessLifting(PointCloud2SimplicialLifting): + def __init__( + self, + is_weak=True, + is_euclidian=True, + landmark_proportion: int = 0.8, + max_alpha_square=0.15, + complex_dim=2, + seed=42, + **kwargs, + ): + super().__init__(**kwargs) + self.is_weak = is_weak + self.is_euclidian = is_euclidian + self.landmark_proportion = landmark_proportion + self.max_alpha_square = max_alpha_square + self.complex_dim = complex_dim + self.seed = seed + torch.manual_seed(seed) + + def _get_lifted_topology(self, simplicial_complex: SimplicialComplex) -> dict: + r"""Returns the lifted topology. + Parameters + ---------- + simplicial_complex : SimplicialComplex + The simplicial complex. + Returns + --------- + dict + The lifted topology. + """ + lifted_topology = get_complex_connectivity(simplicial_complex, self.complex_dim) + lifted_topology["x_0"] = torch.stack( + list(simplicial_complex.get_simplex_attributes("features", 0).values()) + ) + + return lifted_topology + + def lift_topology( + self, + witnesses: torch_geometric.data.Data, + ) -> dict: + n = len(witnesses.pos) + + perm = torch.randperm(n) + idx = perm[: round(n * self.landmark_proportion)] + landmarks_position = witnesses.pos[idx] + + if self.is_euclidian: + if self.is_weak: + complex = gd.EuclideanWitnessComplex(witnesses.pos, landmarks_position) + simplex_tree = complex.create_simplex_tree( + self.max_alpha_square, self.complex_dim + ) + + simplicial_complex = SimplicialComplex.from_gudhi(simplex_tree) + else: + pass + + node_features = {i: witnesses.x[i, :] for i in range(witnesses.x.shape[0])} + simplicial_complex.set_simplex_attributes(node_features, name="features") + + return self._get_lifted_topology(simplicial_complex) diff --git a/test/transforms/liftings/pointcloud2simplicial/test_witness_lifting.py b/test/transforms/liftings/pointcloud2simplicial/test_witness_lifting.py new file mode 100644 index 00000000..592955b8 --- /dev/null +++ b/test/transforms/liftings/pointcloud2simplicial/test_witness_lifting.py @@ -0,0 +1,52 @@ +import torch + +from modules.data.utils.utils import load_point_cloud +from modules.transforms.liftings.pointcloud2simplicial.witness_lifting import ( + WitnessLifting, +) + + +class TestWitnessLifting: + """Test the WitnessLifting class.""" + + def setup_method(self): + # Load the point cloud + SEED = 42 + self.data = load_point_cloud(num_points=5, seed=SEED) + + # Initialise the WitnessLifting class + self.lifting_signed = WitnessLifting(signed=True, seed=SEED) + self.lifting_unsigned = WitnessLifting(signed=False, seed=SEED) + + def test_lift_topology(self): + """Test the lift_topology method.""" + + # Test the lift_topology method + lifted_data_signed = self.lifting_signed.forward(self.data.clone()) + lifted_data_unsigned = self.lifting_unsigned.forward(self.data.clone()) + + expected_incidence_1 = torch.tensor( + [ + [1.0, 1.0, 1.0, 0.0, 0.0], + [1.0, 0.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 0.0, 0.0, 1.0], + [0.0, 1.0, 0.0, 1.0, 0.0], + [0.0, 0.0, 1.0, 0.0, 1.0], + ] + ) + + assert ( + abs(expected_incidence_1) == lifted_data_unsigned.incidence_1.to_dense() + ).all(), "Something is wrong with unsigned incidence_1 (nodes to edges)." + assert ( + expected_incidence_1 == lifted_data_signed.incidence_1.to_dense() + ).all(), "Something is wrong with signed incidence_1 (nodes to edges)." + + expected_incidence_2 = torch.tensor([[1.0], [1.0], [0.0], [1.0], [0.0]]) + + assert ( + abs(expected_incidence_2) == lifted_data_unsigned.incidence_2.to_dense() + ).all(), "Something is wrong with unsigned incidence_2 (edges to triangles)." + assert ( + expected_incidence_2 == lifted_data_signed.incidence_2.to_dense() + ).all(), "Something is wrong with signed incidence_2 (edges to triangles)." diff --git a/tutorials/pointcloud2simplicial/witness_lifting.ipynb b/tutorials/pointcloud2simplicial/witness_lifting.ipynb new file mode 100644 index 00000000..7aa7c259 --- /dev/null +++ b/tutorials/pointcloud2simplicial/witness_lifting.ipynb @@ -0,0 +1,335 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Point Cloud-to-Simplicial Complex Lifting Tutorial\n", + "\n", + "***\n", + "This notebook shows how to import a dataset, with the desired lifting, and how to run a neural network using the loaded data.\n", + "\n", + "The notebook is divided into sections:\n", + "\n", + "- [Loading the dataset](#loading-the-dataset) loads the config files for the data and the desired tranformation, createsa a dataset object and visualizes it.\n", + "- [Loading and applying the lifting](#loading-and-applying-the-lifting) defines a simple neural network to test that the lifting creates the expected incidence matrices.\n", + "- [Create and run a simplicial nn model](#create-and-run-a-simplicial-nn-model) simply runs a forward pass of the model to check that everything is working as expected.\n", + "\n", + "***\n", + "***\n", + "\n", + "Note that for simplicity the notebook is setup to use a simple graph. However, there is a set of available datasets that you can play with.\n", + "\n", + "To switch to one of the available datasets, simply change the *dataset_name* variable in [Dataset config](#dataset-config) to one of the following names:\n", + "\n", + "* cocitation_cora\n", + "* cocitation_citeseer\n", + "* cocitation_pubmed\n", + "* MUTAG\n", + "* NCI1\n", + "* NCI109\n", + "* PROTEINS_TU\n", + "* AQSOL\n", + "* ZINC\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports and utilities" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "from modules.data.load.loaders import PointCloudLoader\n", + "from modules.data.preprocess.preprocessor import PreProcessor\n", + "from modules.utils.utils import (\n", + " describe_data,\n", + " load_dataset_config,\n", + " load_model_config,\n", + " load_transform_config,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading the Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we just need to spicify the name of the available dataset that we want to load. First, the dataset config is read from the corresponding yaml file (located at `/configs/datasets/` directory), and then the data is loaded via the implemented `Loaders`.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then access to the data through the `load()` method:" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Dataset configuration for toy_point_cloud:\n", + "\n", + "{'data_domain': 'point_cloud',\n", + " 'data_type': 'toy_dataset',\n", + " 'data_name': 'toy_point_cloud',\n", + " 'data_dir': 'datasets/point_cloud/toy_dataset',\n", + " 'num_points': 8,\n", + " 'num_classes': 2,\n", + " 'num_features': 1,\n", + " 'task': 'classification',\n", + " 'loss_type': 'cross_entropy'}\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Processing...\n", + "Done!\n" + ] + } + ], + "source": [ + "dataset_name = \"toy_point_cloud\"\n", + "dataset_config = load_dataset_config(dataset_name)\n", + "loader = PointCloudLoader(dataset_config)\n", + "\n", + "dataset = loader.load()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading and Applying the Lifting" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this section we will instantiate the whitness lifting. This lifting constructs a simplicial complex from a point cloud, called the **Witness complex** using the `GUDHI` library [[1]](https://gudhi.inria.fr/doc/latest/group__witness__complex.html). In the current implementation the Witness complex is defined over a point cloud $W$, called *witnesses*. From this set, a subset $L$ of *landmarks* is formed by randomly choosing the points. \n", + "\n", + "Current implementation can *only* lift to a *weak Euclidian Witness complex*:\n", + " - *weak* means that $\\sigma \\in L$ is witnessed by $w\\in W$ if $\\forall l\\in \\sigma, \\forall l' \\in L: d(w,l)\\leq d(w,l')$\n", + " - *euclidian* means that the set of witnesses and landmarks are in Euclidian space.\n", + "\n", + "\n", + "\n", + "***\n", + "[[1]](https://gudhi.inria.fr/doc/latest/group__witness__complex.html) Kachanovich S. (2020). Witness complex. GUDHI User and Reference Manual.\n", + "***\n", + "\n", + "For simplicial complexes creating a lifting involves creating a `SimplicialComplex` object from topomodelx and adding simplices to it using the method `add_simplices_from`. The `SimplicialComplex` class then takes care of creating all the needed matrices.\n", + "\n", + "Similarly to before, we can specify the transformation we want to apply through its type and id --the correxponding config files located at `/configs/transforms`. \n", + "\n", + "Note that the *tranform_config* dictionary generated below can contain a sequence of tranforms if it is needed.\n", + "\n", + "This can also be used to explore liftings from one topological domain to another, for example using two liftings it is possible to achieve a sequence such as: graph -> simplicial complex -> hypergraph. " + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Transform configuration for pointcloud2simplicial/witness_lifting:\n", + "\n", + "{'transform_type': 'lifting',\n", + " 'transform_name': 'WitnessLifting',\n", + " 'feature_lifting': 'ProjectionSum'}\n" + ] + } + ], + "source": [ + "transform_type = \"liftings\"\n", + "# If the transform is a topological lifting, it should include both the type of the lifting and the identifier\n", + "transform_id = \"pointcloud2simplicial/witness_lifting\"\n", + "\n", + "# Read yaml file\n", + "transform_config = {\n", + " \"lifting\": load_transform_config(transform_type, transform_id)\n", + " # other transforms (e.g. data manipulations, feature liftings) can be added here\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We than apply the transform via our `PreProcesor`:" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Processing...\n", + "Done!\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Dataset only contains 1 sample:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAgcAAAIbCAYAAAB/tT3bAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy80BEi2AAAACXBIWXMAAA9hAAAPYQGoP6dpAABqKUlEQVR4nO3dd5xddZ3/8dc5t0/PTNpMeqGlAQkBQgglhaBE2BUCuuK68ltBI+qiGNZd1GUBFUUs1ODqrhqlBFRgKGlgEmOAFGBSgFTS6/Ry+/n+/riZITeZPndyp7yfj0ce5N577jmfe2bIed/v93u+X8sYYxARERE5zk53ASIiItK1KByIiIhIEoUDERERSaJwICIiIkkUDkRERCSJwoGIiIgkUTgQERGRJAoHIiIikkThQDrFXXfdRZ8+fZrdZtKkSdx2221Jzy1btoxRo0ZhWRZ33XVXSmrZsGEDlmW1evvbbruNPn36YFkWo0aN4rbbbmPnzp0pqaU5s2bN4sc//nGn7b+x852OOpo6Zqp+3q3V1t+LznbyOUjHORGpp3AgafOd73yHuXPnNjyuqKhg7ty5LFq0CGMM3/nOd05rPTt37mTUqFHs3LmTRYsWUV5ezoIFCygrK+O55547rbV0hpPPt7Ss/neyT58+jBo1Shdr6TXc6S5Aeq8bbrgh6fGyZcvIz89n4sSJAOTl5bFs2TJuu+02duzY0en1zJ07l5EjR7J06dKG52bOnMnMmTM7/dinQ2Pn+3Sd2+7qS1/6ErNmzeJXv/oV69ata2hFWrRoUbpLE+lUCgciwHPPPceGDRt0oZQGO3fupKKigltvvRVIBMUFCxYwa9YsKioqyMvLS2+BIp1I3QqSNif2qd51113MnTuXnTt3YlkWt912G3PnzmXWrFkNz1mWRUVFRcP768cGjBo1iieffLLh+YqKCmbNmoVlWUyaNIlly5a1WMszzzzDxIkTGTlyZIvbzp07lyeffJInn3ySUaNGNez/ueeeY9KkSQ1jFU7uirjtttu46667Guru06fPKduUlpYmfa7map87d27SGIL6PvQTx0fUn1dIPt8tndu21HGiH//4xw1jRk4+93fddRejRo2iT58+rRr70Nz2Tf0MIHW/FyNHjmTBggVJz11wwQUArFu3rtn3NncemqqvJfXjeNryey3SbkakE8yfP9/k5eU1u83MmTPN/PnzGx4vWrTIjBw5Mmmbxp4zxpgbbrjBzJw505SXl5sdO3aYvLw8s379+ob9Tpw40ezYscOUl5ebG264wbT0qz5x4kRz6623tuqz1e9/5MiRZtGiRQ3PL1iwoKGGpUuXGqDhcf378vLyzNKlS015ebm59dZbDWB27NjR8DrQ8PoNN9zQ6Gc/8dyceI7nz59vRo4caR544IGG506ssTXnuz111Lv11lvNxIkTzfr16015eblZunRpw7Hrf14n/kxmzpyZdMwTa2vN9o39DFL9e3Gy+p9reXl5h85DU/WdeA5OfLx06VIzcuTIhuMuXbo06XdLJNUUDqRTdGY42LFjxyn/QC9YsMDMnz+/4bX6C64xxqxfv77Fi8DIkSOTammp7ry8vGYvEPX7PPFCffLnPfm49RevevUXoqaUl5cnfdaJEyeaBx54oOEiWv96fZ1tCQdtqaOxWk5Uf/5PPl/1Qenk2lq7/ck/g874vThZYz/DEzV3Hpqrr7F9n/j45CAo0tnUrSDdzoYNGwAYMWJEQ/P8XXfdxYYNG9iwYQN5eXmt6h440ciRIxv2W2/u3LkNTe6zZs1Kem3mzJmN9jk/+eSTzJ07l0mTJrXq9seZM2cmbVffbA2Qn5/f7Hvz8vKYOHFiQ/Pyzp07ufXWWxseP/vss4wcObJdfeNtqQMSgxubOu/r1q1rtI4LLrggafBnW7c/+WfQGb8XJ5o7dy4TJ07kgQceaHKb5s5Dc/W1ZObMmeTn5zf8LvaEu2eka1M4kG5p4sSJlJeXJ/1p7ELTWrNmzWLZsmVJ/e71t1TOnz//lO0b+8d/0qRJLFq0iNtuu43169c33HXRFm29kM+cOZOlS5eybNmyhotlfdBZunTpKXcodFYdzTnxnKZy+8Z+Bqn+vahXfyfLicGgfnxJ/Z/WzA3R3vry8vLYsWMHCxYsIC8vj7lz5572uSikd1E4kG5n4sSJbNiwodGLyMiRI6moqGjzpEXz589n5MiR7b6PfefOnQ0X5Lbc+rhs2TImT57crmMC3HTTTSxbtoylS5c2tG7ccMMNLFu2jGXLlp3S4tFZJk6c2OR5r28dOfnntW7dukY/e1u3P7GGVP9ewMeDN09uMVi/fj0m0TXbECKbOw/N1ddat956K4sWLWLBggU888wz7d6PSEsUDqRLGzlyZMOFYtmyZezcuZORI0dy6623NtzdAIk7BX784x8zceJEJk6cyNy5cxv+kf7Sl77UqmMtWrSIZ599lrlz5zb8I75hw4ZWXVDqm97rR5/X3xp5sieffLJh3/X1198q1x71F6PnnnuuIZTMmjWLZ555hoqKimaDSmPntr1O/pnU13TXXXcxceJEZs6cyYwZMxpeq/8m3ljLRlu3b6oG6Pjvxdy5c5k8eTI33ngjFRUVDX/acx6aq68l9dvVH3/p0qUd6iIRaYnCgXSaioqKpGbXxm6Za0n9P+ojRoxI+ua2YMECJk6cyKRJk+jTpw8LFixouBAuX76c/Pz8hlvgbrvttlb9Qzpx4kR27dpFfn4+X/rSl+jTp0/DbYAttSjk5eUxf/78htvU6lsQTm6enzlzJj/84Q8ZMWIEO3fuZP369R1uwq+/YNZ/xpkzZ7Jhw4YWWzCaOrftVf8zmDVrVsPP5KabbgJoOB+TJk1ixIgR5Ofns379+ib31dbtT6whVb8XO3fubLiw148RqP/T3G2EzZ2H5uprTv3kXPXjFSoqKvjVr37V4vtE2ssyxph0FyHSG8yaNavFAW0iIl2BWg5EREQkicKBiIiIJFE4EBERkSQacyAiIiJJ1HIgIiIiSRQOREREJInCgYiIiCRROBAREZEkCgciIiKSROFAREREkigciIiISBKFAxEREUmicCAiIiJJFA5EREQkicKBiIiIJFE4EBERkSQKByIiIpJE4UBERESSKByIiIhIEoUDERERSaJwICIiIkkUDkRERCSJwoGIiIgkUTgQERGRJAoHIiIikkThQERERJIoHIiIiEgShQMRERFJonAgIiIiSRQOREREJInCgYiIiCRROBAREZEkCgciIiKSROFAREREkrjTXUBHxY2hNBTnSDDG0WCc2phD3BhclkWm26ZfwEX/gJsCvwuXZaW7XBERkS6v24aDqkiczWVhSspC1EYNjjHYloVjTMM29Y9tyyLTYzEh38/YfB85XlcaKxcREenaLGNOuJp2A+G4w+qDdZSUhYkbAwbctoUNWI20DBhjcICYY8ACl2UxId/H1MIMfC71qoiIiJysW4WDPdVRluyroTISx8bCbTUeCJpijCFmwMGQ53Uxa3AWQ7M9nVixiIhI99NtwkFJaYjl+2txjMFjWdgdGD/gGEP0eHfDjEGZTCjwp7BSERGR7q1btKuXlIZYvq8WxzF4OxgMIDEWwWtZOI5h+b5aSkpDKapURESk++vy4WBPdbShxcBrW23qRmiOZVl47cSAxeX7a9lTHU3JfkVERLq7Lh0OwnGHJftqUh4M6p0YEJbuqyEcd1K6fxERke6oS4eD1QfrqIzE8VipDwb1LMvCY1lUROKsPljXKccQERHpTrpsOKiKxCkpC2PT8TEGLbEtCxuLkrIwVZF4px5LRESkq+uy4WDz8XkM3KdpUkO3lZhtcXNZ+PQcUEREpIvqkuEgbgwlZSEwbZvHoCMsywIDJWWhxORKIiIivVSXDAeloTi1UYPbPr1rIbhti9poYq0GERGR3qpLhoMjwVhiTYRWbLt9/Ro2rVjM2uJnG5576p47kh63lk1iFsWjwVib3ysiItJTdMlwcDQYx27FHQplB/aSkZ1L0RljWPn0rxueH3TWOEoP7Gnzca3jxzwSVMuBiIj0Xl0yHNTGnKTVFZtSdnAfRWeOYdPKJYyaOKXh+THTZlJQNKRdx3aMoS6m+Q5ERKT36pJLNrd2QODoSYlAsPGvrzL7S99qeL6mpoaCUWOIxqJ8uPp1gtWV7N+6hXGXz254T3NiGpAoIiK9WJdsOXC14Q6FYE0VB7ZtSbroV+zbRU7/Qt5ft4a6YB2TrrmB2bd+k6fu+bdW7dN9mu6QEBER6Yq6ZDjIdNutnvio/MA+8guTuxDcbg8FBQW4nDjb1/2dY8eOYVxuMnLyOLB1S7P7sy2LDHeXPC0iIiKnRZfsVugXcOEYg2nFPAf+rOykx5tWLGbc5bMBGHvJlZwz5XJqqqupqqqiuryUvsNHNbkvYwzGGPoHXB3/ECIiIt1UlwwH/QNubMvCAVq6TOcXDWHsZbNYW/wsgeN3LpzItmxycnJZ9uSDzP7Kv1NWXo7f7yc7OxuXnbx3h0QY6RfokqdFRETktLCM6Xqj7+LG8D/vl1MTcfC5Ot7Ev2nFYgDGXX4VwVCImpoaHMchMzOTzMxMLBKtE+G4Q5bX5l/P6dOmcQ8iIiI9SZfsXHdZFhPy/WAlmvo7Yvv6Nfizchh3+WwObH2fYNkxCgoKyMjIoLa2lmPHjhEKhzDGAQsm5PsVDEREpFfrsu3nY/N9vHUkSMyAp53X6rIDe5PuUAjVVnP/8sSAxOysbAKBANXV1VRUVODPzCLg9zM235eC6kVERLqvLtmtUO/1fTW8UxrCY3Xuss2hSJhQNMbGl5/lDFPOt7/9bXJzczvteCIiIl1Zl+xWqDe1MIM8r4vo8bsIOoMxBsvtYWBOJpcNyuaZZ57hkksuYeHChcTjmkZZRER6ny4dDnwum1mDs7Ati4iT+oBgjCHiGGzL4qoh2Xz1y7fyt7/9jVmzZjF//nw+8YlPsHbt2pQeU0REpKvr0uEAYGi2hxmDMlMeEE4MBjMGZTI02wPAgAED+PnPf85LL72Ey+Xiuuuu4/bbb+fQoUMpOa6IiEhX16XHHJyopDTE8v21OMZ0eAyCYwxR83EwmFDgb3w7x+HZZ5/l/vvvJxgM8o1vfIPbbrsNr9fb7mOLiIh0dd0mHADsqY6ydF8NFZE4NhZuq+UZFE9kjCFmwMGQ53Uxa3BWQ4tBc6qqqnjooYf4zW9+w+DBg7nnnnuYOXNmm44tIiLSXXSrcACJiYpWH6yjpCycWL3RgNu2sGk8KBhjcICYY8Cqn0PBx9TCjDZPsLRt2za++93vsnLlSq688kr++7//m1Gjmp6OWUREpDvqduGgXlUkzuayMCVlIWqjibEIlmXhnPBxbMtqeD7Tk5hYaWy+jxxv+9dOMMawZMkSvv/973PgwAG+9KUv8W//9m9kZ2e3/GYREZFuoNuGg3pxYygNxTkajHEkGKcu5hAzBvfx1RX7B1z0C7gp8LtSOvNhOBzmiSee4Je//CVZWVn853/+JzfccAO23eXHeIqIiDSr24eDdDtw4AD33nsvL7zwAhMnTuS+++7jvPPOS3dZIiIi7aavuR1UVFTE448/zp/+9CdCoRCf/OQnueOOOzh69Gi6SxMREWkXtRykUDwe5w9/+AM/+tGPiMfjfPOb3+SWW27B42n5jggREZGuQuGgE1RUVPCTn/yE3/72t4wcOZJ7772Xyy+/PN1liYiItIq6FTpBXl4e999/P0uXLqVfv3589rOf5Ytf/CK7d+9Od2kiIiItUstBJzPG8NJLL/Hf//3fHDt2jK985St87WtfIyMjI92liYiINErh4DSpq6vj0Ucf5dFHHyU/P5/vfe97XHfddZplUUREuhyFg9Nsz5493HPPPbz66qtceOGF3H///YwdOzbdZYmIiDTQmIPTbOjQofz617/mmWeeoby8nNmzZ/Pv//7vlJWVpbs0ERERQC0HaRWNRvntb3/Lgw8+iGVZzJ8/n89//vO43e50lyYiIr2YwkEXcOzYMX70ox/x1FNPce211/L444+nuyQREenF1K3QBfTt25cHH3yQV155haFDh6a7HBER6eXUctDFOI6jxZtERCStFA5EREQkib6iioiISBINi+8uNm6EVatg6FCoqICbb053RSIi0kOp5aA7qKqCO++EefNg/PjE46oqmD8/8V8REZEUUjjoDlauhGHDEi0HkAgJu3fDe+/B3Lkwezbcd196axQRkR5D3QrdQW4unHceTJuWeLx7N+TkwOLFicfFxTBnTtrKExGRnkV3K3QX992XCAiQCAv1QWHhQrj22kRYEBERSQGFg+7u1lvhySfTXYWIiPQgGnPQnVVVQV5eky/fd9997Ny58/TVIyIiPYLCQXeWkwM//nGTL69bt44rr7yS++67j+rq6tNYmIiIdGfqVujBQqEQTzzxBL/85S/JycnhP//zP7n++us1PbOIiDRL4aAX2L9/P/feey8vvvgikyZN4t577+W8+sGNIiIiJ9FXyF5g0KBBPPHEEzz//PPU1dVxzTXX8K1vfYtjx46luzQREemC1HLQy8RiMRYuXMgDDzyA4zh861vf4otf/CIejyfdpYmISBehcNBLlZeX85Of/ITf/e53jBo1invvvZfLLrss3WWJiEgXoG6FXqpPnz784Ac/YMmSJRQUFPCZz3yGW265hd27d6e7NBERSTO1HAjGGF566SXuueceSktLmTdvHrfffjsZGRnpLk1ERNJA4UAa1NXV8cgjj/DYY49RUFDA9773Pa699losy0p3aSIichopHMgpdu/ezT333MNrr73GRRddxP3338+YMWPSXZaIiJwmGnMgpxg2bBi/+c1veOqppygtLeWqq67iO9/5DuXl5ekuTURETgO1HEizotEo//u//8tPf/pTbNvmrrvu4uabb8bt1mrfIiI9lcKBtMrRo0f50Y9+xNNPP83ZZ5/Nfffdx5QpU1K2/7gxlIbiHAnGOBqMUxtziBuDy7LIdNv0C7joH3BT4Hfh0hgIEZFOpXAgbfLuu+/y3e9+l/Xr13Pttdfy3e9+l0GDBrV7f1WROJvLwpSUhaiNGhxjsC0L54Rfy/rHtmWR6bGYkO9nbL6PHK8rFR9JREROonAgbeY4Ds8//zz3338/VVVVfO1rX+MrX/kKfr+/1fsIxx1WH6yjpCxM3Bgw4LYtbGj07ghjDA4QcwxY4LIs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3x//OL31SK+kcCAiIiJJNOZAREREkigciIh0E2roldNF4UBEpBv405/+xMSJE1m/fn26S5FeQGMORES6gWg0yvXXX8+BAwdYtmwZeXl56S5JejC1HIiIdAMej4cnnniCuro6vvGNb+A4TrpLkh5M4UBEpJsoKiril7/8JUuXLmXBggXpLkd6MIUDEZFuZObMmcybN48f/OAHrFu3Lt3lSA+lMQciIt1MNBrlhhtuYP/+/SxdupQ+ffqkuyTpYdRyICLSzXg8Hh5//HGCwaDGH0inUDgQEemGioqKePjhh1m2bBlPPPFEusuRHkbhQESkm5o+fTq33347P/zhD1m7dm26y5EeRGMORES6sVgsxvXXX8++fftYunQp+fn56S5JegC1HIiIdGNut5snnniCcDis8QeSMgoHIiLdXGFhIQ8//DDLly/n8ccfT3c50gMoHIiI9ABXXnklX/va1/jRj37E22+/ne5ypJvTmAMRkR4iFosxd+5cdu/ezbJlyzT+QNpNLQciIj2E2+3m8ccfJxKJ8LWvfU3jD6TdFA5ERHqQgQMH8sgjj/DGG2/w2GOPpbsc6aYUDkREepgrrriCr3/96zzwwAO89dZb6S5HuiGNORAR6YFisRg33ngju3btYtmyZRQUFKS7JOlG1HIgItID1Y8/iMViGn8gbaZwICLSQw0YMIBHHnmEFStW8Oijj6a7HOlGFA5ERHqwyy+/nG984xs88MADvPnmm+kuR7oJjTkQEenhYrEYN910Ezt37mTp0qX07ds33SVJF6eWAxGRHs7tdvPYY49p/IG0msKBiEgvMGDAAB599FFWrlzJww8/nO5ypItTOBAR6SUuu+wy/u3f/o2f/OQnrFmzJt3lSBemMQciIr1IPB7npptuYseOHRp/IE1Sy4GISC/icrl49NFHicfj3H777cTj8XSXJF2QwoGISC9TP/5g1apVGn8gjVI4EBHphaZNm8Ydd9zBgw8+yN///vd0lyNdjMYciIj0UvF4nM985jNs27aNpUuX0q9fv3SXJF2EWg5ERHqp+vEHxhiNP5AkCgciIr1Y//79efTRR/nb3/7GL3/5y3SXI12EwoGISC936aWX8s1vfpOf/vSnrF69Ot3lSBegMQciIkI8Huezn/0sH374IUuXLqV///6nbmMMpaE4R4Ixjgbj1MYc4sbgsiwy3Tb9Ai76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tDDGcNttt7Fy5Upef/11ioqK0l2SHKdwICIiaVNRUcH06dMZOXIkzzzzDC6XK90lCepWEBGRNMrLy+Phhx9mzZo1LFiwIN3lyHEKByIiklZTp07lK1/5Cg888AAlJSXpLkdQt4KIiHQB0WiUOXPmUFdXx+LFi8nIyEh3Sb2aWg5ERCTt6m9v3L9/P/fcc0+6y+n1FA5ERKRLGD16NPfccw+///3vWbx4cbrL6dXUrSAiIl2GMYYvfvGLrF27ltdff50BAwaku6ReSeFARES6lNLSUmbMmME555zDH/7wB2xbjdynm864iIh0KQUFBfz85z9nxYoV/PrXv053Ob2SwoGIiHQ5V1xxBV/60pe47777eP/999NdTq+jbgUREemSwuEwn/zkJ3Ech1dffRW/35/uknoNtRyIiEiX5PP5eOyxx/joo4+477770l1Or6KWAxER6dJ27NhBSUkJU6ZMYeDAgekup1dQOBARkS7NGIPjOADYto1lWWmuqOdTOBAREZEkGnMgIiIiSRQOREREJInCgYiIiCRROBAREZEk7nQXICIi0qKNG2HVKhg6FCoq4Oab011Rj6aWAxER6dqqquDOO2HePBg/PvEYoLgYZs9O3nbhwkSIWLjw9NfZgygciIhI17ZyJQwblrjoQyIkAMyZA3l5H29X//q0aYnni4tPZ5U9isKBiIh0bbm5cN55iYv+sGGwe3fj223cmHi9/j3vvnu6KuxxNOZARES6tmnTYMWKj1sCcnM/DgEnq6w8fXX1YAoHIiLS9d19d8vbjB//catCZWWitUHaRd0KIiLSPa1aBXv2fNyiMG1aYrBi/fNz5qS3vm5MayuIiIhIErUciIiISBKFAxEREUmicCAiIiJJFA5ERKTHefjhh/nc5z7HoUOH0l1Kt6QBiSIi0uNUV1dz1VVX0bdvX/785z/jduvO/bZQy4GIiPQ42dnZPPLII7z77rv84he/SHc53Y7CgYiI9EiTJk3ijjvu4Gc/+xnr1q1LdzndiroVRESkx4rFYnz605/myJEjLF26lOzs7HSX1C2o5UBERHost9vNI488QllZGXe3ZgpmARQORESkhxs6dCg//OEPWbRoES+88EK6y+kW1K0gIiI9njGGr371q7z++ussX76cQYMGpbukLk3hQEREeoWqqipmzJjBkCFDWLRoES6XK90ldVnqVhAR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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " - The complex has 8 0-cells.\n", + " - The 0-cells have features dimension 1\n", + " - The complex has 12 1-cells.\n", + " - The 1-cells have features dimension 1\n", + " - The complex has 7 2-cells.\n", + " - The 2-cells have features dimension 1\n", + "\n" + ] + } + ], + "source": [ + "lifted_dataset = PreProcessor(dataset, transform_config, loader.data_dir)\n", + "describe_data(lifted_dataset)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create and Run a Simplicial NN Model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this section a simple model is created to test that the used lifting works as intended. In this case the model uses the `up_laplacian_1` and the `down_laplacian_1` so the lifting should make sure to add them to the data." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Model configuration for simplicial SAN:\n", + "\n", + "{'in_channels': None,\n", + " 'hidden_channels': 32,\n", + " 'out_channels': None,\n", + " 'n_layers': 2,\n", + " 'n_filters': 2,\n", + " 'order_harmonic': 5,\n", + " 'epsilon_harmonic': 0.1}\n" + ] + } + ], + "source": [ + "from modules.models.simplicial.san import SANModel\n", + "\n", + "model_type = \"simplicial\"\n", + "model_id = \"san\"\n", + "model_config = load_model_config(model_type, model_id)\n", + "\n", + "model = SANModel(model_config, dataset_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [], + "source": [ + "y_hat = model(lifted_dataset.get(0))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If everything is correct the cell above should execute without errors. " + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "topox", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}