{ "cells": [ { "cell_type": "raw", "id": "cbd7323d-be69-4444-8780-e95429537143", "metadata": {}, "source": [ "\n", "" ] }, { "cell_type": "markdown", "id": "8b64defe-0863-4c33-b0e2-891ceb6c4f79", "metadata": {}, "source": [ "# Co-publication graph" ] }, { "cell_type": "markdown", "id": "92c839ae-37c8-4dc5-9f82-244946b9f1f7", "metadata": {}, "source": [ "## Lab authors" ] }, { "cell_type": "markdown", "id": "626c86e5-84a3-4ada-862c-499f328e9e28", "metadata": {}, "source": [ "Lab authors are the main ingredient to analyse a single lab (i.e. a group of researchers). You can create one just with a name and then automatically ask to retrieve the DB endpoints for this author." ] }, { "cell_type": "code", "execution_count": 1, "id": "6ee1e4e3-0578-40b3-b229-70151973688a", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:25.220030Z", "iopub.status.busy": "2025-07-23T16:12:25.220030Z", "iopub.status.idle": "2025-07-23T16:12:39.403418Z", "shell.execute_reply": "2025-07-23T16:12:39.402411Z", "shell.execute_reply.started": "2025-07-23T16:12:25.220030Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING:GisMap:Multiple entries for Maria Potop in hal\n" ] } ], "source": [ "from gismap.lab import LabAuthor\n", "\n", "maria = LabAuthor(\"Maria Potop\")\n", "maria.auto_sources()" ] }, { "cell_type": "markdown", "id": "0a4a3af2-a77c-4e96-af55-bab0219d39ea", "metadata": {}, "source": [ "We see a warning here. Let's look at the sources:" ] }, { "cell_type": "code", "execution_count": 2, "id": "2b4dcaa7-6630-4fbe-8f10-1dd26ea1adb4", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:39.404419Z", "iopub.status.busy": "2025-07-23T16:12:39.403418Z", "iopub.status.idle": "2025-07-23T16:12:39.413940Z", "shell.execute_reply": "2025-07-23T16:12:39.413433Z", "shell.execute_reply.started": "2025-07-23T16:12:39.404419Z" } }, "outputs": [ { "data": { "text/plain": [ "[HALAuthor(name='Maria Potop', key='858256', key_type='pid'),\n", " HALAuthor(name='Maria Potop', key='841868', key_type='pid'),\n", " DBLPAuthor(name='Maria Potop', key='p/MariaPotopButucaru')]" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maria.sources" ] }, { "cell_type": "markdown", "id": "04f3bab8-346c-4fbc-b06c-098a8544991f", "metadata": {}, "source": [ "This is actually normal: Maria has multiple identities in Hal. The warning is there to tell there is a possibility of homonyms but that is not the case here. Note that an author can have many names." ] }, { "cell_type": "code", "execution_count": 3, "id": "ce13ebf9-92fd-4755-bab4-451ca43b1fd2", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:39.414949Z", "iopub.status.busy": "2025-07-23T16:12:39.414949Z", "iopub.status.idle": "2025-07-23T16:12:39.421970Z", "shell.execute_reply": "2025-07-23T16:12:39.420962Z", "shell.execute_reply.started": "2025-07-23T16:12:39.414949Z" } }, "outputs": [ { "data": { "text/plain": [ "['Maria Gradinariu', 'Maria Gradinariu Potop-Butucaru', 'Maria Potop-Butucaru']" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "maria.aliases" ] }, { "cell_type": "markdown", "id": "7f9d66c5-ab98-4dec-8639-6ec1699169d3", "metadata": {}, "source": [ "When using `auto_source`, you can tell which DBs should be uses (only online DBLP and HAL are available right now)." ] }, { "cell_type": "code", "execution_count": 4, "id": "e93e21d3-07eb-4a38-af10-fd8058f45a97", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:39.422972Z", "iopub.status.busy": "2025-07-23T16:12:39.421970Z", "iopub.status.idle": "2025-07-23T16:12:39.575185Z", "shell.execute_reply": "2025-07-23T16:12:39.575185Z", "shell.execute_reply.started": "2025-07-23T16:12:39.422972Z" } }, "outputs": [ { "data": { "text/plain": [ "[HALAuthor(name='Céline Comte', key='celine-comte')]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from gismap.sources.hal import HAL\n", "\n", "celine = LabAuthor(\"Céline Comte\")\n", "celine.auto_sources(dbs=[HAL])\n", "celine.sources" ] }, { "cell_type": "markdown", "id": "0783fe44-33f3-4ff2-b193-553e7915595d", "metadata": {}, "source": [ "When the sources of an author are set one can retrieve her publications." ] }, { "cell_type": "code", "execution_count": 5, "id": "823583b9-a941-41a3-9f27-e14ae309b98b", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:39.576192Z", "iopub.status.busy": "2025-07-23T16:12:39.576192Z", "iopub.status.idle": "2025-07-23T16:12:39.792845Z", "shell.execute_reply": "2025-07-23T16:12:39.792845Z", "shell.execute_reply.started": "2025-07-23T16:12:39.576192Z" } }, "outputs": [ { "data": { "text/plain": [ "{'2118156': SourcedPublication(title=\"0 = 0, c'est le truc du noyau ! Application aux files d'attente\", authors=[HALAuthor(name='Anne Bouillard', key='anne-bouillard'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Élie de Panafieu', key='Élie de Panafieu', key_type='fullname'), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='ALGOTEL 2019 - 21èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2019, key='2118156', url='https://hal.science/hal-02118156v1'),\n", " '1889101': SourcedPublication(title='Of Kernels and Queues: when network calculus meets analytic combinatorics', authors=[HALAuthor(name='Anne Bouillard', key='anne-bouillard'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Élie de Panafieu', key='Élie de Panafieu', key_type='fullname'), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='NetCal 2018', type='conference', year=2018, key='1889101', url='https://hal.science/hal-01889101v1'),\n", " '2413496': SourcedPublication(title='Resource management in computer clusters : algorithm design and performance analysis', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='unpublished', type='thesis', year=2019, key='2413496', url='https://pastel.hal.science/tel-02413496v1'),\n", " '1306343': SourcedPublication(title='Performance of a Server Cluster with Parallel Processing and Randomized Load Balancing', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='unpublished', type='report', year=2016, key='1306343', url='https://hal.science/hal-01306343v1'),\n", " '5076337': SourcedPublication(title='Optimizing Asynchronous Federated Learning: A Delicate Trade-Off Between Model-Parameter Staleness and Update Frequency', authors=[HALAuthor(name='Abdelkrim Alahyane', key='1504678', key_type='pid'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Matthieu Jonckheere', key='matthieu-jonckheere'), HALAuthor(name='Éric Moulines', key='1350242', key_type='pid')], venue='unpublished', type='report', year=2025, key='5076337', url='https://hal.science/hal-04938472v2'),\n", " '2299321': SourcedPublication(title='Performance of Balanced Fairness in Resource Pools: A Recursive Approach', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='Proceedings of the ACM on Measurement and Analysis of Computing Systems', type='journal', year=2017, key='2299321', url='https://inria.hal.science/hal-01630420v3'),\n", " '2052607': SourcedPublication(title=\"Kleinberg's grid unchained\", authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='Theoretical Computer Science', type='journal', year=2020, key='2052607', url='https://inria.hal.science/hal-02052607v1'),\n", " '1581786': SourcedPublication(title='Balanced Fair Resource Sharing in Computer Clusters', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='Performance Evaluation', type='journal', year=2017, key='1581786', url='https://hal.science/hal-01581786v1'),\n", " '3219422': SourcedPublication(title='Modèle de couplage stochastique non-biparti', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='ALGOTEL 2021 - 23èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2021, key='3219422', url='https://hal.science/hal-03219422v1'),\n", " '2340255': SourcedPublication(title='Dynamic load balancing with tokens', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='Computer Communications', type='journal', year=2019, key='2340255', url='https://hal.science/hal-02340255v1'),\n", " '1517150': SourcedPublication(title='À la racine du parallélisme', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='ALGOTEL 2017 - 19èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2017, key='1517150', url='https://hal.science/hal-01517150v1'),\n", " '2328981': SourcedPublication(title='Poly-symmetry in processor-sharing systems', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Virag Shah', key='Virag Shah', key_type='fullname'), HALAuthor(name='Gustavo de Veciana', key='Gustavo de Veciana', key_type='fullname')], venue='Queueing Systems', type='journal', year=2017, key='2328981', url='https://hal.science/hal-01513544v2'),\n", " '4780574': SourcedPublication(title='Online Stochastic Matching: A Polytope Perspective', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu'), HALAuthor(name='Sushil Mahavir Varma', key='1440476', key_type='pid'), HALAuthor(name='Ana Bušić', key='anabusic')], venue='unpublished', type='report', year=2024, key='4780574', url='https://hal.science/hal-03502084v5'),\n", " '1773674': SourcedPublication(title='Un seul serveur vous manque, et tout est découplé !', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='ALGOTEL 2018 - 20èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2018, key='1773674', url='https://hal.science/hal-01773674v1'),\n", " '4612740': SourcedPublication(title='Score-Aware Policy-Gradient Methods and Performance Guarantees using Local Lyapunov Conditions', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Matthieu Jonckheere', key='matthieu-jonckheere'), HALAuthor(name='Jaron Sanders', key='Jaron Sanders', key_type='fullname'), HALAuthor(name='Albert Senen-Cerda', key='Albert Senen-Cerda', key_type='fullname')], venue='unpublished', type='report', year=2024, key='4612740', url='https://hal.science/hal-04329790v2'),\n", " '3224101': SourcedPublication(title='Pass-and-Swap Queues', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Jan-Pieter Dorsman', key='1098513', key_type='pid')], venue='Queueing Systems', type='journal', year=2021, key='3224101', url='https://hal.science/hal-03224101v1'),\n", " '3331759': SourcedPublication(title='Load Balancing in Heterogeneous Server Clusters: Insights From a Product-Form Queueing Model', authors=[HALAuthor(name='Mark van Der Boor', key='Mark van Der Boor', key_type='fullname'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='2021 IEEE/ACM 29th International Symposium on Quality of Service (IWQOS)', type='conference', year=2021, key='3331759', url='https://hal.science/hal-03331759v1'),\n", " '2118170': SourcedPublication(title='Rien ne sert de prédire ; il faut servir ancien.', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='ALGOTEL 2019 - 21èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2019, key='2118170', url='https://hal.science/hal-02118170v1'),\n", " '1517123': SourcedPublication(title='La Grille de Kleinberg, l’Univers et le Reste', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Fabien Mathieu', key='fabien-mathieu')], venue='ALGOTEL 2017 - 19èmes Rencontres Francophones sur les Aspects Algorithmiques des Télécommunications', type='conference', year=2017, key='1517123', url='https://hal.science/hal-01517123v1'),\n", " '3507517': SourcedPublication(title='Stochastic Non-Bipartite Matching Models and Order-Independent Loss Queues', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='Stochastic Models', type='journal', year=2022, key='3507517', url='https://hal.science/hal-03468064v2'),\n", " '4956887': SourcedPublication(title='Graph-Based Product Form', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Isaac Grosof', key='1510244', key_type='pid')], venue='unpublished', type='report', year=2025, key='4956887', url='https://hal.science/hal-04956887v1'),\n", " '3507566': SourcedPublication(title='Performance Evaluation of Stochastic Bipartite Matching Models', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Jan-Pieter Dorsman', key='1098513', key_type='pid')], venue='Performance Engineering and Stochastic Modeling', type='chapter', year=2021, key='3507566', url='https://hal.science/hal-03468055v2'),\n", " '1314992': SourcedPublication(title='The multi-source model for dimensioning data networks', authors=[HALAuthor(name='Thomas Bonald', key='tbonald'), LabAuthor(name='Céline Comte', metadata=AuthorMetadata())], venue='Computer Networks', type='journal', year=2016, key='1314992', url='https://hal.science/hal-01314992v1'),\n", " '5086684': SourcedPublication(title='Arrival Control in Quasi-Reversible Queueing Systems: Optimization and Reinforcement Learning', authors=[LabAuthor(name='Céline Comte', metadata=AuthorMetadata()), HALAuthor(name='Pascal Moyal', key='pascal-moyal')], venue='unpublished', type='report', year=2025, key='5086684', url='https://hal.science/hal-05074406v2')}" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "celine.get_publications()" ] }, { "cell_type": "markdown", "id": "ea883a15-d757-4177-9214-b72a692c3c96", "metadata": {}, "source": [ "Lab authors can have metadata that can be used for display and further analysis but we will not cover that in this tutorial." ] }, { "cell_type": "markdown", "id": "c3890090-8446-4681-8bc8-1c68557c8510", "metadata": {}, "source": [ "## Your first lab" ] }, { "cell_type": "markdown", "id": "13311631-7699-42f3-b61c-d038fbc30590", "metadata": {}, "source": [ "In GISMAP, a Lab is a class whose instances have two methods:\n", "\n", "- `update_authors` automatically refresh the members of the lab. It is useful at creation or when a lab evolves.\n", "- `update_publications` makes a full refresh of the publications of a lab. All publications from lab members are considered (temporal filtering may be enabled later).\n", "\n", "The simplest usable subclass of Lab is `ListLab`, which uses a list of names. For example, consider the executive committee of the LINCS lab plus Fabien Mathieu (GISMAP author)." ] }, { "cell_type": "code", "execution_count": 6, "id": "4430b91d-bb94-482b-a7f0-96786276c3b0", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:12:39.795355Z", "iopub.status.busy": "2025-07-23T16:12:39.794850Z", "iopub.status.idle": "2025-07-23T16:13:23.362218Z", "shell.execute_reply": "2025-07-23T16:13:23.362218Z", "shell.execute_reply.started": "2025-07-23T16:12:39.795355Z" } }, "outputs": [ { "data": { "text/plain": [ "{'tixeuil': LabAuthor(name='Tixeuil Sébastien', metadata=AuthorMetadata()),\n", " 'fabien-mathieu': LabAuthor(name='Mathieu Fabien', metadata=AuthorMetadata()),\n", " 'daniel-kofman': LabAuthor(name='Kofman Daniel', metadata=AuthorMetadata()),\n", " 'francois-baccelli': LabAuthor(name='Baccelli François', metadata=AuthorMetadata()),\n", " 'ludovic-noirie': LabAuthor(name='Noirie Ludovic', metadata=AuthorMetadata()),\n", " 'francesca-bassi': LabAuthor(name='Bassi Francesca', metadata=AuthorMetadata())}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from gismap.lab import ListLab\n", "\n", "lab = ListLab(\n", " author_list=[\n", " \"Tixeuil Sébastien\",\n", " \"Mathieu Fabien\",\n", " \"Kofman Daniel\",\n", " \"Baccelli François\",\n", " \"Noirie Ludovic\",\n", " \"Bassi Francesca\",\n", " ],\n", " name=\"toy_example\",\n", ")\n", "lab.update_authors()\n", "lab.authors" ] }, { "cell_type": "code", "execution_count": 7, "id": "c737805e-7504-4acb-9bbd-9dc8730ee574", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:13:23.363223Z", "iopub.status.busy": "2025-07-23T16:13:23.363223Z", "iopub.status.idle": "2025-07-23T16:13:26.875253Z", "shell.execute_reply": "2025-07-23T16:13:26.875253Z", "shell.execute_reply.started": "2025-07-23T16:13:23.363223Z" } }, "outputs": [ { "data": { "text/plain": [ "939" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "lab.update_publis()\n", "len(lab.publications)" ] }, { "cell_type": "markdown", "id": "50fe35df-46e7-4148-bf0e-7a68b0a3faec", "metadata": {}, "source": [ "Labs can be saved to you don't have to re-update them all the time." ] }, { "cell_type": "code", "execution_count": 8, "id": "7edf67f4-e151-45ac-bc36-9c014e37e3b8", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:13:26.876372Z", "iopub.status.busy": "2025-07-23T16:13:26.876372Z", "iopub.status.idle": "2025-07-23T16:13:26.882969Z", "shell.execute_reply": "2025-07-23T16:13:26.881963Z", "shell.execute_reply.started": "2025-07-23T16:13:26.876372Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "File toy_example.pkl.zst already exists! Use overwrite option to overwrite.\n" ] } ], "source": [ "lab.dump(lab.name)" ] }, { "cell_type": "markdown", "id": "db4c70a1-15d1-4ef0-af18-94e3e1ffbd47", "metadata": {}, "source": [ "When you have a populated lab, you can use `lab2graph` to create the collaboration graph. That graph is a standalone HTML that can be displayed in a notebook or saved for inclusion in a web page (`iframe` is recommended then)." ] }, { "cell_type": "code", "execution_count": 9, "id": "8f82a22a-a18a-45c4-bfbc-72ef0f4598c6", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:13:26.883969Z", "iopub.status.busy": "2025-07-23T16:13:26.883969Z", "iopub.status.idle": "2025-07-23T16:13:26.926075Z", "shell.execute_reply": "2025-07-23T16:13:26.925064Z", "shell.execute_reply.started": "2025-07-23T16:13:26.883969Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", "
\n", " ×\n", "
\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from gismap.lab import lab2graph\n", "from IPython.display import display, HTML\n", "\n", "display(HTML(lab2graph(lab)))" ] }, { "cell_type": "markdown", "id": "5e3af967-57e3-461f-adc4-039906eaebe9", "metadata": {}, "source": [ "Few things about the generated graph:\n", "\n", "- Singletons (authors with no co-publications) are discarded by default.\n", "- Authors are represented with their initials unless some picture url is provided.\n", "- You can hover an author to get her name. If you click, you have a modal with the list of publications.\n", "- The width and length of an edge depend on the number of co-publications. If you click you have a modal with the list of co-publications." ] }, { "cell_type": "markdown", "id": "6c17d826-a2bb-47cb-ab81-3393b7c17a51", "metadata": {}, "source": [ "## Make your own lab" ] }, { "cell_type": "markdown", "id": "ab853628-d001-47b5-9548-173e31cd03fc", "metadata": {}, "source": [ "The easiest way to manage a lab is to specify an internal method `_author_iterator` that returns Lab authors. \n", "\n", "To GISMAP a lab, you just need to specify that method. Most of the time, this is done by scrapping some Web page(s). See the references for examples.\n" ] }, { "cell_type": "markdown", "id": "311b1732-176e-4d3d-9478-ed6c8e2e5e38", "metadata": {}, "source": [ "## Example" ] }, { "cell_type": "markdown", "id": "0efbed5a-904a-4398-b0c0-a8e32ef074f6", "metadata": {}, "source": [ "The `LaasLab` class automatically builds a lab representation from `https://www.laas.fr/fr/equipes/*team_name*/`" ] }, { "cell_type": "code", "execution_count": 10, "id": "7db9c886-3671-4268-b925-80739700c77e", "metadata": { "execution": { "iopub.execute_input": "2025-07-23T16:13:26.927074Z", "iopub.status.busy": "2025-07-23T16:13:26.927074Z", "iopub.status.idle": "2025-07-23T16:13:27.011265Z", "shell.execute_reply": "2025-07-23T16:13:27.011265Z", "shell.execute_reply.started": "2025-07-23T16:13:26.927074Z" } }, "outputs": [ { "data": { "text/html": [ "\n", "
\n", "
\n", "
\n", " ×\n", "
\n", "
\n", "
\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from gismap.lab import LaasLab\n", "\n", "display(HTML(lab2graph(LaasLab.load(\"sara\"))))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.8" } }, "nbformat": 4, "nbformat_minor": 5 }