{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Wprowadzenie\n",
        "\n",
        "Ten notatnik prezentuje różne rodzaje podziałów grafu na zbiór treningowy i testowy - z wykorzystaniem paradygmatu indukcyjnego i transdukcyjnego.\n",
        "\n",
        "Będziemy pracować na zbiorze danych \"Karate Club\", wbudowanym w NetwrokX oraz PyG."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Informacja o dodatkowych zależnościach PyG\n",
        "\n",
        "Notatniki w niniejszym rozdziale wykorzystują wybrane funkcjonalności biblioteki **PyTorch Geometric (PyG)**, które w praktyce wymagają instalacji dodatkowych (opcjonalnych) rozszerzeń binarnych. W szczególności mogą być potrzebne następujące pakiety:\n",
        "\n",
        "- `pyg-lib`\n",
        "- `torch-scatter`\n",
        "- `torch-sparse`\n",
        "- `torch-cluster`\n",
        "- `torch-spline-conv`\n",
        "\n",
        "W projekcie zostały one zgrupowane jako extra `pyg` (zob. `pyproject.toml`). Jeżeli w trakcie uruchamiania notatników pojawi się błąd w rodzaju `ModuleNotFoundError: No module named 'torch_sparse'`, należy doinstalować rozszerzenia poleceniem:\n",
        "\n",
        "```bash\n",
        "uv sync --extra pyg\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Import bibliotek"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {},
      "outputs": [],
      "source": [
        "import networkx as nx\n",
        "import torch as th\n",
        "import torch.nn as nn\n",
        "import torch_geometric as pyg\n",
        "import matplotlib.pyplot as plt\n",
        "import torch_geometric.datasets as pyg_datasets\n",
        "import torch_geometric.data as pyg_data\n",
        "import torch_geometric.utils as pyg_utils\n",
        "import torch_geometric.nn as pyg_nn\n",
        "import torch_geometric.transforms as pyg_transforms\n",
        "import deepsnap.graph as deepsnap_graph\n",
        "import deepsnap.dataset as deepsnap_dataset\n",
        "\n",
        "from typing import Tuple, Iterable, Dict\n",
        "from tqdm.auto import tqdm\n",
        "from copy import deepcopy\n",
        "\n",
        "import sys\n",
        "sys.path.append(\"..\")\n",
        "import utils.vis_utils as vis_utils"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {},
      "outputs": [],
      "source": [
        "import visualizations as viz"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Przygotowanie danych"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Data(x=[34, 34], edge_index=[2, 156], y=[34])"
            ]
          },
          "execution_count": 3,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "graph = pyg_datasets.KarateClub()[0]\n",
        "graph.train_mask = None\n",
        "graph"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {},
      "outputs": [],
      "source": [
        "from importlib import reload\n",
        "viz = reload(viz)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 89892 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "f1 = viz.plot_network_with_colors_and_shapes(graph, title=\"\", seed=222)\n",
        "f1.savefig(\"images/rys_5.3.tiff\", format=\"tif\", pil_kwargs={\"compression\": \"tiff_lzw\"})"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Podziały danych"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Podział wierzchołków"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Transdukcyjny\n",
        "Wierzchołki treningowe, walidacyjne i testowe należą do tego samego grafu - sąsiadują ze sobą."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "Data(x=[34, 34], edge_index=[2, 156], y=[34], train_mask=[34], val_mask=[34], test_mask=[34])"
            ]
          },
          "execution_count": 6,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "pyg.seed_everything(123)\n",
        "random_splitter = pyg_transforms.RandomNodeSplit(\n",
        "    split=\"train_rest\", \n",
        "    num_splits=1,\n",
        "    num_train_per_class=3,\n",
        "    num_test=10,\n",
        "    num_val=10)\n",
        "random_split_graph = random_splitter(graph.clone())\n",
        "random_split_graph"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(tensor(14), tensor(10), tensor(10))"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "random_split_graph.train_mask.sum(), random_split_graph.val_mask.sum(), random_split_graph.test_mask.sum()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 82348 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "f2 = viz.plot_network_with_colors_and_shapes(random_split_graph, use_mask_shapes=True, title=\"\", legend_loc=\"lower left\")\n",
        "plt.tight_layout()\n",
        "# rys 5.5 i 5.17 to te same rysunki\n",
        "f2.savefig(\"images/rys_5.17.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Widzimy, że tym razem zachowana została pełna struktura grafu, a wierzchołki przyporządkowane do różnych podzbiorów sąsiadują ze sobą. Przykładowo: wierzchołki 19 i 30 są przyporządkowane do zbioru treningowego, ale już trójkąt 0-10-5 obejmuje wierzchołki należące do zbioru walidacyjnego, treningowego i testowego. \n",
        "\n",
        "Dowolnego typu grafowa warstwa splotu będzie miała dostęp do całej struktury w procesie przekazywania wiadomości. Jednak liczenie funkcji kosztu i optymalizacja wag będzie wykorzystywało tylko wyniki uzyskane dla wierzchołków treningowych. Przykładowy kod takiego działania zaprezentowano poniżej:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "application/vnd.jupyter.widget-view+json": {
              "model_id": "8b1dce08cac0491193e5db3eec31c897",
              "version_major": 2,
              "version_minor": 0
            },
            "text/plain": [
              "  0%|          | 0/10 [00:00<?, ?it/s]"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Epoka  1, koszt treningowy: 1.4724, koszt walidacyjny: 1.3587\n",
            "Epoka  2, koszt treningowy: 1.4255, koszt walidacyjny: 1.3279\n",
            "Epoka  3, koszt treningowy: 1.3819, koszt walidacyjny: 1.2958\n",
            "Epoka  4, koszt treningowy: 1.3395, koszt walidacyjny: 1.2607\n",
            "Epoka  5, koszt treningowy: 1.2967, koszt walidacyjny: 1.2230\n",
            "Epoka  6, koszt treningowy: 1.2531, koszt walidacyjny: 1.1832\n",
            "Epoka  7, koszt treningowy: 1.2081, koszt walidacyjny: 1.1426\n",
            "Epoka  8, koszt treningowy: 1.1629, koszt walidacyjny: 1.1017\n",
            "Epoka  9, koszt treningowy: 1.1177, koszt walidacyjny: 1.0615\n",
            "Epoka 10, koszt treningowy: 1.0718, koszt walidacyjny: 1.0227\n"
          ]
        }
      ],
      "source": [
        "pyg.seed_everything(123)\n",
        "nclass = random_split_graph.y.unique().shape[0]\n",
        "dim_in = random_split_graph.x.shape[1]\n",
        "sample_gnn = pyg_nn.GraphSAGE(\n",
        "    in_channels=dim_in,\n",
        "    hidden_channels=16,\n",
        "    out_channels=nclass,\n",
        "    num_layers=2)\n",
        "optimizer = th.optim.Adam(sample_gnn.parameters(), lr=0.01)\n",
        "epochs = 10\n",
        "for epoch in tqdm(range(epochs)):\n",
        "    sample_gnn.train()\n",
        "    optimizer.zero_grad()\n",
        "    yhat = sample_gnn(random_split_graph.x, random_split_graph.edge_index)\n",
        "    train_mask = random_split_graph.train_mask\n",
        "    yhat_train = yhat[train_mask]\n",
        "    y_train = random_split_graph.y[train_mask]\n",
        "    loss = nn.functional.cross_entropy(yhat_train, y_train)\n",
        "    loss.backward()\n",
        "    optimizer.step()\n",
        "    with th.no_grad():\n",
        "        sample_gnn.eval()\n",
        "        yhat = sample_gnn(random_split_graph.x, random_split_graph.edge_index)\n",
        "        val_mask = random_split_graph.val_mask\n",
        "        yhat_val = yhat[val_mask]\n",
        "        y_val = random_split_graph.y[val_mask]\n",
        "        val_loss = nn.functional.cross_entropy(yhat_val, y_val)\n",
        "            \n",
        "    print(f\"Epoka {epoch+1:2d}, koszt treningowy: {loss.item():.4f}, koszt walidacyjny: {val_loss.item():.4f}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Podział indukcyjny\n",
        "\n",
        "Całkowite rozdzielenie grafów na zbiory treningowe, walidacyjne i testowe."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Podgraf odpowiadający zbiorowi treningowemu, walidacyjnemu i testowemu można uzyskać na jeden z dwóch sposobów:\n",
        "1. Za pomocą biblioteki PyG\n",
        "2. Za pomocą biblioteki NetworkX"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Budowa podgrafu biblioteką PyG:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "tensor([[ 2,  2,  2,  2,  3,  3,  3,  7,  7,  9,  9, 12, 19, 22, 28, 28, 28, 30,\n",
              "         31, 31, 33, 33, 33, 33, 33, 33],\n",
              "        [ 3,  7,  9, 28,  2,  7, 12,  2,  3,  2, 33,  3, 33, 33,  2, 31, 33, 33,\n",
              "         28, 33,  9, 19, 22, 28, 30, 31]])"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "train_subgraph_pyg_indices, _ = pyg_utils.subgraph(\n",
        "    random_split_graph.train_mask.nonzero(), \n",
        "    random_split_graph.edge_index)\n",
        "\n",
        "val_subgraph_pyg_indices, _ = pyg_utils.subgraph(\n",
        "    random_split_graph.val_mask.nonzero(), \n",
        "    random_split_graph.edge_index)\n",
        "\n",
        "test_subgraph_pyg_indices, _ = pyg_utils.subgraph(\n",
        "    random_split_graph.test_mask.nonzero(), \n",
        "    random_split_graph.edge_index)\n",
        "\n",
        "train_subgraph_pyg_indices"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Za pomocą biblioteki NetworkX:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 19912 characters]",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "networkx_graph = pyg_utils.to_networkx(random_split_graph)\n",
        "train_subgraph_networkx = networkx_graph.subgraph(random_split_graph.train_mask.nonzero().squeeze().tolist())\n",
        "nx.draw(train_subgraph_networkx)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Funkcja poniżej służy do wizualizacji wszystkich uzyskanych podgrafów jednocześnie."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 64716 characters]",
            "text/plain": [
              "<Figure size 472.441x984.252 with 3 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig2 = viz.plot_multiple_subgraphs({\n",
        "    \"Trening\": (random_split_graph.train_mask.nonzero().view(-1).tolist(), 'blue'),\n",
        "    \"Walidacja\": (random_split_graph.val_mask.nonzero().view(-1).tolist(), \"green\"),\n",
        "    \"Test\": (random_split_graph.test_mask.nonzero().view(-1).tolist(), \"red\")\n",
        "}, pyg_utils.to_networkx(random_split_graph, node_attrs=['y']), title=\"\", figsize=(12, 25));\n",
        "fig2.savefig(\"images/rys_5.4.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Podział krawędzi"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Trywialny przykład dla małego grafu"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 22660 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "trivial_g = nx.Graph()\n",
        "trivial_g.add_edges_from([\n",
        "    (0, 1),\n",
        "    (1, 2),\n",
        "    (1, 3),\n",
        "    (2, 3),\n",
        "    (2, 4),\n",
        "    (3, 4)\n",
        "])\n",
        "\n",
        "fig, ax = plt.subplots(figsize=vis_utils.image_size_in_cm(12, 12))\n",
        "pos = nx.spring_layout(trivial_g, seed=123)\n",
        "nx.draw(trivial_g, pos, with_labels=True, ax=ax, font_size=12, node_color=\"lightgrey\")\n",
        "plt.title(\"\")\n",
        "plt.tight_layout()\n",
        "fig.savefig(\"images/rys_5.5.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {},
      "outputs": [],
      "source": [
        "trivial_g_pyg = pyg_utils.from_networkx(trivial_g)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Podział indukcyjny"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "/home/filip/projects/grafowe_sieci_neuronowe/.venv/lib/python3.12/site-packages/deepsnap/graph.py:522: UserWarning: Node related key is required.\n",
            "  warnings.warn(\"Node related key is required.\")\n"
          ]
        },
        {
          "data": {
            "image/png": "[encoded data, 46944 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 53440 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 50672 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "pyg.seed_everything(123)\n",
        "deepnap_g = deepsnap_graph.Graph(trivial_g)\n",
        "snap_ds = deepsnap_dataset.GraphDataset(\n",
        "    [deepcopy(deepnap_g)] * 3, \n",
        "    task='link_pred', \n",
        "    edge_message_ratio=0.5,\n",
        "    edge_train_mode='disjoint',\n",
        "    edge_negative_sampling_ratio=1.)\n",
        "\n",
        "snap_train, snap_val, snap_test = snap_ds.split(\n",
        "    transductive=False, \n",
        "    split_ratio=[0.5, 0.25, 0.25])\n",
        "\n",
        "viz = reload(viz)\n",
        "\n",
        "fig_num_in_book = 7\n",
        "\n",
        "for (snapg, name, color) in [(snap_train, \"Trening\", \"blue\"), (snap_val, \"Walidacja\", \"green\"), (snap_test, \"Test\", \"red\")]:\n",
        "    f = viz.plot_snap_graph_split_edges(\n",
        "        snapg,\n",
        "        trivial_g_pyg,\n",
        "        split_name=name,\n",
        "        split_color=color,\n",
        "        figsize=(12, 12),\n",
        "        seed=123,\n",
        "        plot_messsage_passing_links=True,\n",
        "        use_curved_edges=True if name == \"Walidacja\" else False\n",
        "    )\n",
        "    plt.tight_layout()\n",
        "    f.savefig(f\"./images/rys_5.{fig_num_in_book}.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})\n",
        "    plt.show()\n",
        "    fig_num_in_book += 1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "#### Podział transdukcyjny"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 28056 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 35452 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 44764 characters]",
            "text/plain": [
              "<Figure size 472.441x472.441 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "viz = reload(viz)\n",
        "pyg.seed_everything(123)\n",
        "trivial_splitter = pyg_transforms.RandomLinkSplit(\n",
        "    num_val=2,\n",
        "    num_test=2,\n",
        "    is_undirected=True,\n",
        "    disjoint_train_ratio=0.5,\n",
        "    add_negative_train_samples=True,\n",
        "    neg_sampling_ratio=1.0,\n",
        ")\n",
        "\n",
        "trivial_train, trivial_val, trivial_test = trivial_splitter(trivial_g_pyg)\n",
        "\n",
        "fig_index_in_book = 10\n",
        "\n",
        "for (pyg_split, name, color) in [(trivial_train, \"Trening\", \"blue\"), (trivial_val, \"Walidacja\", \"green\"), (trivial_test, \"Test\", \"red\")]:\n",
        "    fig = viz.plot_multiple_networks_with_edge_labels(\n",
        "        trivial_g_pyg,\n",
        "        [(pyg_split, color, name)],\n",
        "        plot_messsage_passing_links=True,\n",
        "        seed=123,\n",
        "        use_curved_edges=False\n",
        "\n",
        "    )\n",
        "    plt.tight_layout()\n",
        "    fig.savefig(f\"images/rys_5.{fig_index_in_book}.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})\n",
        "    plt.show()\n",
        "    fig_index_in_book += 1\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "### Graf Karate Club - transdukcyjny podział krawędzi"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 148572 characters]",
            "text/plain": [
              "<Figure size 787.402x787.402 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 188912 characters]",
            "text/plain": [
              "<Figure size 787.402x787.402 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        },
        {
          "data": {
            "image/png": "[encoded data, 186124 characters]",
            "text/plain": [
              "<Figure size 787.402x787.402 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "fig_index_in_book = 13\n",
        "#410\n",
        "pyg.seed_everything(410)\n",
        "random_link_splitter = pyg_transforms.RandomLinkSplit(\n",
        "    num_val=10, \n",
        "    num_test=5, \n",
        "    is_undirected=True, \n",
        "    add_negative_train_samples=False, \n",
        "    disjoint_train_ratio=0.5)\n",
        "\n",
        "train_g1, val_g1, test_g1 = random_link_splitter(graph.clone())\n",
        "\n",
        "for (pyg_split, name, color) in [(train_g1, \"Trening\", \"blue\"), (val_g1, \"Walidacja\", \"green\"), (test_g1, \"Test\", \"red\")]:\n",
        "    fig = viz.plot_multiple_networks_with_edge_labels(\n",
        "        graph,\n",
        "        [(pyg_split, color, name)],\n",
        "        plot_messsage_passing_links=True,\n",
        "        seed=123,\n",
        "        figsize=(20, 20),\n",
        "        font_size=16,\n",
        "        node_size=450\n",
        "    )\n",
        "    plt.tight_layout()\n",
        "    fig.savefig(f\"images/rys_5.{fig_index_in_book}.tiff\", format=\"tiff\", pil_kwargs={\"compression\": \"tiff_lzw\"})\n",
        "    plt.show()\n",
        "    fig_index_in_book += 1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "# Podziały grafów"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "W poniższym przykładzie zostanie wykorzystany zbiór **\"MUTAG\"**, wbudowany w bibliotekę PyG. W tym zbiorze występuje 188 grafów obrazujących różne cząsteczki. \n",
        "Zadaniem sieci jest klasyfikacja cząsteczek, jako powstrzymujących replikację wirusa HIV lub nie."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Rozpoczynamy od pobrania danych."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {},
      "outputs": [
        {
          "name": "stderr",
          "output_type": "stream",
          "text": [
            "Downloading https://www.chrsmrrs.com/graphkerneldatasets/MUTAG.zip\n",
            "Processing...\n",
            "Done!\n"
          ]
        }
      ],
      "source": [
        "mutag_data = pyg_datasets.TUDataset(root='data/TUDataset', name='MUTAG')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Ilość  grafów (cząsteczel): 188\n",
            "Ilość klas: 2\n",
            "Ilość cech w każdym grafie: 7\n"
          ]
        }
      ],
      "source": [
        "print(\"Ilość  grafów (cząsteczel):\", len(mutag_data))\n",
        "print(\"Ilość klas:\", mutag_data.num_classes)\n",
        "print(\"Ilość cech w każdym grafie:\", mutag_data.num_features)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Każdy obiekt w zbiorze danych jest grafem. Przykład analizy pierwszego z nich:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Data(edge_index=[2, 38], x=[17, 7], edge_attr=[38, 4], y=[1])\n"
          ]
        }
      ],
      "source": [
        "graph_0 = mutag_data[0]\n",
        "print(graph_0)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {},
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Ilość wierzchołków w grafie: 17\n",
            "Ilość krawędzi w grafie: 38\n"
          ]
        }
      ],
      "source": [
        "print(\"Ilość wierzchołków w grafie:\", graph_0.num_nodes)\n",
        "print(\"Ilość krawędzi w grafie:\", graph_0.num_edges)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Grafy są listą (kolekcją), którą można losowo pomieszać, wybierająć elementy do zbiorów treningowego, walidacyjnego i testowego."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {},
      "outputs": [],
      "source": [
        "pyg.seed_everything(123)\n",
        "mutag_data.shuffle()\n",
        "\n",
        "train_perc = 0.7\n",
        "val_perc = test_perc = 0.15\n",
        "\n",
        "train_size = int(train_perc * len(mutag_data))\n",
        "val_size = test_size = int(val_perc * len(mutag_data))\n",
        "\n",
        "train_graphs = mutag_data[:train_size]\n",
        "val_graphs = mutag_data[train_size:train_size+val_size]\n",
        "test_graphs = mutag_data[train_size+val_size:]"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "(131, 28, 29)"
            ]
          },
          "execution_count": 23,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "len(train_graphs), len(val_graphs), len(test_graphs)"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": ".venv",
      "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.12.3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 2
}
