aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlek Westover2024-10-03 14:15:44 -0400
committerAlek Westover2024-10-03 14:15:44 -0400
commit0b633a516dc434d64176fb2a00981bbb8802153a (patch)
treee874cd540af4050acaf1970b7d06530831fbb47e
parentcaf0dfc41d7fed5aafc9b41b500575069d9c47e4 (diff)
upload
-rw-r--r--transformer-shortest-paths.ipynb0
-rw-r--r--transformer_shortest_paths.ipynb382
2 files changed, 382 insertions, 0 deletions
diff --git a/transformer-shortest-paths.ipynb b/transformer-shortest-paths.ipynb
deleted file mode 100644
index e69de29..0000000
--- a/transformer-shortest-paths.ipynb
+++ /dev/null
diff --git a/transformer_shortest_paths.ipynb b/transformer_shortest_paths.ipynb
new file mode 100644
index 0000000..fc0a1da
--- /dev/null
+++ b/transformer_shortest_paths.ipynb
@@ -0,0 +1,382 @@
+{
+ "nbformat": 4,
+ "nbformat_minor": 0,
+ "metadata": {
+ "colab": {
+ "provenance": [],
+ "gpuType": "T4"
+ },
+ "kernelspec": {
+ "name": "python3",
+ "display_name": "Python 3"
+ },
+ "language_info": {
+ "name": "python"
+ },
+ "accelerator": "GPU"
+ },
+ "cells": [
+ {
+ "cell_type": "code",
+ "source": [
+ "# imports\n",
+ "import numpy as np\n",
+ "from collections import deque\n",
+ "import pickle\n",
+ "from tqdm import tqdm\n",
+ "np.random.seed(42)\n",
+ "\n",
+ "import torch\n",
+ "import torch.nn as nn\n",
+ "import pickle\n",
+ "from math import sqrt\n",
+ "from torch.utils.data import DataLoader, TensorDataset\n",
+ "import matplotlib.pyplot as plt\n",
+ "torch.manual_seed(42)\n",
+ "\n",
+ "print(\"imports complete\")"
+ ],
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "ge5QvElvhCOw",
+ "outputId": "38b82493-509e-40d0-8b62-13484cec0cba"
+ },
+ "execution_count": null,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "imports complete\n"
+ ]
+ }
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Step 1: Generate synthetic data"
+ ],
+ "metadata": {
+ "id": "gKt-yIpDebF1"
+ }
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "collapsed": true,
+ "id": "1IbzGIWseK3E",
+ "outputId": "86cb72b8-8932-4cbe-ad3a-217206e3c66c"
+ },
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stderr",
+ "text": [
+ "100%|██████████| 54/54 [00:00<00:00, 102.56it/s]\n"
+ ]
+ }
+ ],
+ "source": [
+ "MAX_VTXS = 62\n",
+ "FAKE_VTX = 63 # padding token\n",
+ "INF = MAX_VTXS # represents unreachability\n",
+ "SEQ_LEN = 128\n",
+ "NTRAIN1 = 10000\n",
+ "NTRAIN2 = 2000\n",
+ "\n",
+ "# weirder way of representing a graph\n",
+ "# have it just be a list of vertex/parity pairs\n",
+ "\n",
+ "def random_graph(n):\n",
+ " assert n >= 8\n",
+ " edge_list = []\n",
+ " adjacencies = [set() for _ in range(n)]\n",
+ "\n",
+ " indices = np.random.randint(n, size=(2*n))\n",
+ " for i in range(0, len(indices), 2):\n",
+ " u = indices[i]\n",
+ " v = indices[i + 1]\n",
+ " if u != v:\n",
+ " edge_list.append(u)\n",
+ " edge_list.append(v)\n",
+ " adjacencies[u].add(v)\n",
+ " adjacencies[v].add(u)\n",
+ "\n",
+ " edge_list += [FAKE_VTX]*(SEQ_LEN-len(edge_list))\n",
+ " return edge_list, adjacencies\n",
+ "\n",
+ "\"\"\"\n",
+ "input: G, represented as an adjacency list\n",
+ "output: [d(0,i) for i in range(n)] if target=None\n",
+ "if target is set to some value, then we instead just output that specific distance\n",
+ "\"\"\"\n",
+ "def SSSP(G,target=None):\n",
+ " dist = [INF for _ in G]\n",
+ " dist[0] = 0\n",
+ " frontier = deque()\n",
+ " frontier.append(0)\n",
+ " while len(frontier) > 0:\n",
+ " vtx = frontier.popleft()\n",
+ " for x in G[vtx]:\n",
+ " if dist[x] == INF:\n",
+ " dist[x] = 1 + dist[vtx]\n",
+ " frontier.append(x)\n",
+ " if x == target:\n",
+ " return dist[target]\n",
+ " if target is not None:\n",
+ " return dist[target]\n",
+ " else:\n",
+ " return dist\n",
+ "\n",
+ "graphs1 = []\n",
+ "distance1 = []\n",
+ "\n",
+ "graphs2 = []\n",
+ "distances2 = []\n",
+ "\n",
+ "for n in tqdm(range(8, MAX_VTXS)):\n",
+ " for _ in range(NTRAIN1//MAX_VTXS):\n",
+ " edge_list, adj_list = random_graph(n)\n",
+ " dist = SSSP(adj_list, target=1)\n",
+ "\n",
+ " graphs1.append(edge_list)\n",
+ " distance1.append(dist)\n",
+ "\n",
+ "for n in range(8, MAX_VTXS//4):\n",
+ " for _ in range(NTRAIN2//MAX_VTXS):\n",
+ " edge_list, adj_list = random_graph(n)\n",
+ " distances = SSSP(adj_list)\n",
+ " graphs2.append(edge_list)\n",
+ " distances2.append(distances)\n",
+ "\n",
+ "split1 = int(len(graphs1)*3/4)\n",
+ "split2 = int(len(graphs2)*3/4)\n",
+ "\n",
+ "data = {\n",
+ " \"train1-data\": graphs1[:split1],\n",
+ " \"train1-labels\": distance1[:split1],\n",
+ " \"test1-data\": graphs1[split1:],\n",
+ " \"test1-labels\": distance1[split1:],\n",
+ " \"train2-data\": graphs2[:split2],\n",
+ " \"train2-labels\": distances2[:split2],\n",
+ " \"test2-data\": graphs2[split2:],\n",
+ " \"test2-labels\": distances2[split2:]\n",
+ "}\n",
+ "\n",
+ "with open('data.pkl', 'wb') as file:\n",
+ " pickle.dump(data, file)\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "source": [
+ "# Step 2: Define Transformer Model"
+ ],
+ "metadata": {
+ "id": "Q3Cg_8UQep8g"
+ }
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "class TransformerModel(nn.Module):\n",
+ " def __init__(self, input_dim, model_dim, output_dim, num_heads, num_layers, seq_len, device, dropout=0.1):\n",
+ " super().__init__()\n",
+ " self.embedding = nn.Embedding(input_dim, model_dim)\n",
+ " self.model_dim = model_dim\n",
+ " self.seq_len = seq_len\n",
+ " self.device = device\n",
+ "\n",
+ " encoder_layers = nn.TransformerEncoderLayer(d_model=model_dim, nhead=num_heads, dropout=dropout)\n",
+ " self.transformer_encoder = nn.TransformerEncoder(encoder_layers, num_layers)\n",
+ " self.fc_out = nn.Linear(model_dim*seq_len, output_dim)\n",
+ "\n",
+ " def positional_encoding(self, batch_size):\n",
+ " pos_encoding = torch.arange(self.seq_len, device=self.device).unsqueeze(1) % 2\n",
+ " pos_encoding = pos_encoding.float().unsqueeze(0).repeat(batch_size, 1, 1)\n",
+ " return pos_encoding\n",
+ "\n",
+ " def forward(self, src, src_mask=None):\n",
+ " batch_size, src_len = src.size(0), src.size(1)\n",
+ " src_pos = self.positional_encoding(batch_size)\n",
+ " embed = self.embedding(src)\n",
+ "\n",
+ " src = embed * sqrt(self.model_dim) + src_pos\n",
+ " output = self.transformer_encoder(src, src_mask)\n",
+ "\n",
+ " flat_output = torch.flatten(output, start_dim=1, end_dim=2)\n",
+ " output = self.fc_out(flat_output)\n",
+ "\n",
+ " return output\n"
+ ],
+ "metadata": {
+ "id": "tLOWhg_CeWzH"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
+ "assert device.type == 'cuda', \"CUDA is not available. Please check your GPU setup.\"\n",
+ "\n",
+ "# PARAMS\n",
+ "VOCAB_SIZE = 64 # one more than the max number of vertices\n",
+ "model_dim = 512 # Dimension of model (embedding and transformer)\n",
+ "num_epochs = 10\n",
+ "batch_size = 32\n",
+ "learning_rate = 0.001\n",
+ "max_seq_len = 128\n",
+ "model = TransformerModel(input_dim=VOCAB_SIZE, model_dim=model_dim, output_dim=VOCAB_SIZE, num_heads=8, num_layers=6, seq_len=max_seq_len, device=device).to(device)\n",
+ "\n",
+ "with open(\"data.pkl\", \"rb\") as f:\n",
+ " data = pickle.load(f)\n",
+ "\n",
+ "train_data1 = data[\"train1-data\"]\n",
+ "train_label1 = data[\"train1-labels\"]\n",
+ "\n",
+ "# Convert to tensors\n",
+ "train_data_tensor = torch.tensor(train_data1, dtype=torch.long, device=device)\n",
+ "train_label_tensor = torch.tensor(train_label1, dtype=torch.long, device=device)\n",
+ "\n",
+ "# Create DataLoader\n",
+ "train_dataset = TensorDataset(train_data_tensor, train_label_tensor)\n",
+ "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n",
+ "\n",
+ "# Loss and optimizer\n",
+ "criterion = nn.CrossEntropyLoss()\n",
+ "optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)\n",
+ "\n",
+ "losses = []"
+ ],
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "kWXvJRDYgFVP",
+ "outputId": "f9474f71-bbcf-4369-cf46-5eee985ebb1c"
+ },
+ "execution_count": null,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stderr",
+ "text": [
+ "/usr/local/lib/python3.10/dist-packages/torch/nn/modules/transformer.py:307: UserWarning: enable_nested_tensor is True, but self.use_nested_tensor is False because encoder_layer.self_attn.batch_first was not True(use batch_first for better inference performance)\n",
+ " warnings.warn(f\"enable_nested_tensor is True, but self.use_nested_tensor is False because {why_not_sparsity_fast_path}\")\n"
+ ]
+ }
+ ]
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "val_data1 = data[\"test1-data\"]\n",
+ "val_label1 = data[\"test1-labels\"]"
+ ],
+ "metadata": {
+ "id": "pOE1654fjR5p"
+ },
+ "execution_count": null,
+ "outputs": []
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "for epoch in range(num_epochs):\n",
+ " model.train() # set to training mode\n",
+ " epoch_loss = 0\n",
+ " for batch_src, batch_labels in train_loader:\n",
+ " optimizer.zero_grad()\n",
+ " output = model(batch_src)\n",
+ " loss = criterion(output, batch_labels)\n",
+ " epoch_loss += loss.item()\n",
+ " loss.backward()\n",
+ " optimizer.step()\n",
+ " losses.append(epoch_loss)\n",
+ " print(f\"Epoch {epoch}/{num_epochs} \\t Loss: {epoch_loss:.4f}\")\n",
+ "\n",
+ "plt.figure(figsize=(10, 5))\n",
+ "plt.plot(losses, label='Training Loss', color='blue')\n",
+ "plt.title('Training Loss Over Time')\n",
+ "plt.xlabel('Epochs'); plt.ylabel('Loss')\n",
+ "plt.legend(); plt.grid()\n",
+ "plt.show()\n"
+ ],
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/",
+ "height": 649
+ },
+ "id": "pvTfzGmCeXU4",
+ "outputId": "ef244f98-e209-4e8f-cea7-89f4feb6d805"
+ },
+ "execution_count": null,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Epoch 0/10 \t Loss: 3116.6495\n",
+ "Epoch 1/10 \t Loss: 686.7595\n",
+ "Epoch 2/10 \t Loss: 620.3947\n",
+ "Epoch 3/10 \t Loss: 536.0020\n",
+ "Epoch 4/10 \t Loss: 482.1293\n",
+ "Epoch 5/10 \t Loss: 452.6987\n",
+ "Epoch 6/10 \t Loss: 432.0869\n",
+ "Epoch 7/10 \t Loss: 414.4569\n",
+ "Epoch 8/10 \t Loss: 405.3528\n",
+ "Epoch 9/10 \t Loss: 400.7901\n"
+ ]
+ },
+ {
+ "output_type": "display_data",
+ "data": {
+ "text/plain": [
+ "<Figure size 1000x500 with 1 Axes>"
+ ],
+ "image/png": "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a68ZVZITf39/devWTcuWLdPhw4cd6/fu3Vtuz5tq166dIiMj9frrrztd7rdq1Srt2rVLvXv3lmR/3tT58+ed3tugQQOFhYU53nf8+PFCZ+Wuv/56SbrspYShoaEaPXq0du/erX/961+Ftn/22WeaP3++EhMT1bFjR6dtd999tw4fPqy33npL27dvd7qEUJL69+8vf39/TZo0qVBtNptNf/75Z7F1eSpfPCYAFQ9ntgDAx910002qWrWqBg8erL///e8ymUx67733POoyvokTJ2rNmjW6+eabNWzYMOXn5+vVV19VixYtlJaWVqJ9WCwWPfPMM4XWV6tWTY8++qimT5+uBx54QF26dNE999zjuPV7/fr1NXLkSEnSr7/+qq5du+quu+5Ss2bNFBAQoKVLlyozM1MDBgyQJL377rt67bXX1K9fPzVo0ECnTp3Sm2++qfDwcPXq1euyNY4dO1Y//PCDpk+frtTUVCUlJSkkJEQbN27UwoUL1bRpU7377ruF3terVy+FhYVp9OjR8vf3V1JSktP2Bg0a6JlnntG4ceO0f/9+9e3bV2FhYdq3b5+WLl2qoUOHavTo0SX6OXoKXzwmABUPYQsAfFz16tW1YsUKPf7443rqqadUtWpV3XffferatasSExONLk+S1LZtW61atUqjR4/W+PHjFRMTo8mTJ2vXrl0luluiZD9bN378+ELrGzRooEcffVRDhgxRaGionnvuOY0ZM0aVKlVSv379NH36dMcdBmNiYnTPPfdo3bp1eu+99xQQEKAmTZpoyZIljoDTpUsXbdmyRe+//74yMzMVERGhG2+8UYsWLVJsbOxla/T399eSJUu0YMECvfXWWxo/frxyc3PVoEEDPf3003r88cdVqVKlQu8LDg7WHXfcoUWLFqlbt26KjIwsNGbs2LG67rrrNGvWLE2aNMlxPAkJCbrjjjtK9DP0NL54TAAqFpPNk/7XJgAAF+nbt6927typPXv2GF0KAAClRs8WAMAjnDt3zun1nj17tHLlSsXHxxtTEAAAV4kzWwAAj1CrVi0NGTJE1157rQ4cOKC5c+cqJydHP/zwgxo1amR0eQAAlBo9WwAAj9CjRw/95z//UUZGhsxms+Li4vTss88StAAAXoszWwAAAADgAvRsAQAAAIALELYAAAAAwAXo2SoBq9Wqw4cPKywsTCaTyehyAAAAABjEZrPp1KlTql27tvz8Ln/uirBVAocPH1ZMTIzRZQAAAADwEAcPHlSdOnUuO4awVQJhYWGS7D/Q8PBwg6uRLBaL1qxZo4SEBAUGBhpdDnwc8w3uxpyDOzHf4G7MOe+XnZ2tmJgYR0a4HMJWCRRcOhgeHu4xYSs0NFTh4eH8IYXLMd/gbsw5uBPzDe7GnPMdJWkv4gYZAAAAAOAChC0AAAAAcAHCFgAAAAC4AD1bAAAAqFBsNpvy8vKUn5/v9s+2WCwKCAjQ+fPnDfl8lExgYKD8/f2vej+ELQAAAFQYubm5OnLkiM6ePWvI59tsNkVHR+vgwYM8v9WDmUwm1alTR5UrV76q/RC2AAAAUCFYrVbt27dP/v7+ql27toKCgtweeKxWq06fPq3KlStf8YG4MIbNZtPRo0d16NAhNWrU6KrOcBG2AAAAUCHk5ubKarUqJiZGoaGhhtRgtVqVm5ur4OBgwpYHq1mzpvbv3y+LxXJVYYvfMAAAACoUQg6upLzOeDLTAAAAAMAFCFsAAAAA4AKELQAAAKCCqV+/vl566aUSj1+/fr1MJpNOnDjhspp8EWELAAAA8FAmk+myy8SJE8u0361bt2ro0KElHn/TTTfpyJEjioiIKNPnlZSvhTruRuilbDajKwAAAICrHTlyxPH9Bx98oAkTJmj37t2OdRc/B8pmsyk/P18BAVf+J37NmjVLVUdQUJCio6NL9R5wZsvrLFok3XBDgBYvbmJ0KQAAAF7NZpPOnDFmKen/OI+OjnYsERERMplMjte//PKLwsLCtGrVKrVt21Zms1kbN27Ub7/9pjvvvFNRUVGqXLmy2rdvr7Vr1zrt99LLCE0mk9566y3169dPoaGhatSokZYvX+7YfukZp/nz56tKlSpavXq1mjZtqsqVK6tHjx5O4TAvL09///vfVaVKFVWvXl1jxozR4MGD1bdv37L+ynT8+HENGjRIVatWVWhoqHr27Kk9e/Y4th84cEB9+vRR1apVValSJTVv3lwrV650vHfgwIGqWbOmQkJC1KhRI82bN6/MtZQEYcvLWCzSTz+ZtGNHDaNLAQAA8Gpnz0qVK7t3CQ/3U506VXT2bPkdx9ixY/Xcc89p165datWqlU6fPq1evXpp3bp1+uGHH9SjRw/16dNH6enpl93PpEmTdNddd+nHH39Ur169NHDgQB07duwyP7+zeuGFF/Tee+9pw4YNSk9P1+jRox3bp0+frkWLFmnevHnatGmTsrOztWzZsqs61iFDhui7777T8uXLlZqaKpvNpl69eslisUiSkpOTlZOTow0bNmjHjh2aPn264+zf+PHj9fPPP2vVqlXatWuX5s6dqxo1XPtvai4j9DLx8fave/ZU1Zkz+apSxchqAAAAYLTJkyere/fujtfVqlVT69atHa+nTJmipUuXavny5Ro+fHix+xkyZIjuueceSdKzzz6rV155RVu2bFGPHj2KHG+xWPT666+rQYMGkqThw4dr8uTJju2zZ8/WuHHj1K9fP0nSq6++6jjLVBZ79uzR8uXLtWnTJt10002SpEWLFikmJkbLli3TX//6V6WnpyspKUktW7aUJF177bWO96enp6tNmzZq166dJPvZPVcjbHmZ+vWlevVsOnDAT6mpVvXsaXRFAAAA3ik0VDp92r2fabValZ2drdDQ8HLbZ0F4KHD69GlNnDhRn332mY4cOaK8vDydO3fuime2WrVq5fi+UqVKCg8PV1ZWVrHjQ0NDHUFLkmrVquUYf/LkSWVmZurGG290bPf391fbtm1ltVpLdXwFdu3apYCAAHXo0MGxrnr16mrcuLF27dolSfr73/+uYcOGac2aNerWrZuSkpIcxzVs2DAlJSXp+++/V0JCgvr27esIba7CZYReqHNn+0W+X31VPk+2BgAAqIhMJqlSJWMWUzn+M65SpUpOr0ePHq2lS5fq2Wef1ddff620tDS1bNlSubm5l91PYGDgJT8f02WDUVHjbQbfxe2hhx7S77//rvvvv187duxQu3btNHv2bElSz549deDAAY0cOVKHDx9W165dnS57dAXClhfq0sU+6TdsIGwBAADA2aZNmzRkyBD169dPLVu2VHR0tPbv3+/WGiIiIhQVFaWtW7c61uXn5+v7778v8z6bNm2qvLw8ffvtt451f/75p3bv3q1mzZo51sXExOiRRx7Rxx9/rMcff1xvvvmmY1vNmjU1ePBgLVy4UC+99JLeeOONMtdTElxG6IUKzmxt3WrSmTP2/zsCAAAASFKjRo308ccfq0+fPjKZTBo/fnyZL927Go899pimTZumhg0bqkmTJpo9e7aOHz8uUwlO6+3YsUNhYWGO1yaTSa1bt9add96phx9+WP/+978VFhamsWPH6pprrtGdd94pSRox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yyFslcCpU6ckSTExMQZXAgAAAMATnDp1ShEREZcdY7KVJJJVcFarVYcPH1ZYWJhMJpPR5Sg7O1sxMTE6ePCgwsPDjS4HPo75BndjzsGdmG9wN+ac97PZbDp16pRq164tP7/Ld2VxZqsE/Pz8VKdOHaPLKCQ8PJw/pHAb5hvcjTkHd2K+wd2Yc97tSme0CnCDDAAAAABwAcIWAAAAALgAYcsLmc1mPf300zKbzUaXggqA+QZ3Y87BnZhvcDfmXMXCDTIAAAAAwAU4swUAAAAALkDYAgAAAAAXIGwBAAAAgAsQtgAAAADABQhbXmbOnDmqX7++goOD1aFDB23ZssXokuCjpk2bpvbt2yssLEyRkZHq27evdu/ebXRZqCCee+45mUwmjRgxwuhS4MP++9//6r777lP16tUVEhKili1b6rvvvjO6LPig/Px8jR8/XrGxsQoJCVGDBg00ZcoUcZ8630fY8iIffPCBRo0apaefflrff/+9WrdurcTERGVlZRldGnzQV199peTkZG3evFkpKSmyWCxKSEjQmTNnjC4NPm7r1q3697//rVatWhldCnzY8ePHdfPNNyswMFCrVq3Szz//rJkzZ6pq1apGlwYfNH36dM2dO1evvvqqdu3apenTp2vGjBmaPXu20aXBxbj1uxfp0KGD2rdvr1dffVWSZLVaFRMTo8cee0xjx441uDr4uqNHjyoyMlJfffWVOnfubHQ58FGnT5/WDTfcoNdee03PPPOMrr/+er300ktGlwUfNHbsWG3atElff/210aWgArj99tsVFRWlt99+27EuKSlJISEhWrhwoYGVwdU4s+UlcnNztW3bNnXr1s2xzs/PT926dVNqaqqBlaGiOHnypCSpWrVqBlcCX5acnKzevXs7/V0HuMLy5cvVrl07/fWvf1VkZKTatGmjN9980+iy4KNuuukmrVu3Tr/++qskafv27dq4caN69uxpcGVwtQCjC0DJ/PHHH8rPz1dUVJTT+qioKP3yyy8GVYWKwmq1asSIEbr55pvVokULo8uBj3r//ff1/fffa+vWrUaXggrg999/19y5czVq1Cg9+eST2rp1q/7+978rKChIgwcPNro8+JixY8cqOztbTZo0kb+/v/Lz8zV16lQNHDjQ6NLgYoQtAFeUnJysn376SRs3bjS6FPiogwcP6h//+IdSUlIUHBxsdDmo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\n"
+ },
+ "metadata": {}
+ }
+ ]
+ },
+ {
+ "cell_type": "code",
+ "source": [
+ "!git clone git@github.com:awestover/transformer-shortest-paths.git"
+ ],
+ "metadata": {
+ "colab": {
+ "base_uri": "https://localhost:8080/"
+ },
+ "id": "yDJdR7ybk9oz",
+ "outputId": "a37dec29-24fe-4836-eb31-13081e6d8676"
+ },
+ "execution_count": null,
+ "outputs": [
+ {
+ "output_type": "stream",
+ "name": "stdout",
+ "text": [
+ "Cloning into 'transformer-shortest-paths'...\n",
+ "Host key verification failed.\n",
+ "fatal: Could not read from remote repository.\n",
+ "\n",
+ "Please make sure you have the correct access rights\n",
+ "and the repository exists.\n"
+ ]
+ }
+ ]
+ }
+ ]
+}