{
 "cells": [
  {
   "cell_type": "raw",
   "id": "9e45e81c-e16e-4c6c-b6a3-2362e5193827",
   "metadata": {},
   "source": [
    "---\n",
    "sidebar_position: 3\n",
    "keywords: [RunnableBranch, LCEL]\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b47436a",
   "metadata": {},
   "source": [
    "# How to route between sub-chains\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following concepts:\n",
    "- [LangChain Expression Language (LCEL)](/docs/concepts/#langchain-expression-language)\n",
    "- [Chaining runnables](/docs/how_to/sequence/)\n",
    "- [Configuring chain parameters at runtime](/docs/how_to/configure)\n",
    "- [Prompt templates](/docs/concepts/#prompt-templates)\n",
    "- [Chat Messages](/docs/concepts/#message-types)\n",
    "\n",
    ":::\n",
    "\n",
    "Routing allows you to create non-deterministic chains where the output of a previous step defines the next step. Routing can help provide structure and consistency around interactions with models by allowing you to define states and use information related to those states as context to model calls.\n",
    "\n",
    "There are two ways to perform routing:\n",
    "\n",
    "1. Conditionally return runnables from a [`RunnableLambda`](/docs/how_to/functions) (recommended)\n",
    "2. Using a `RunnableBranch` (legacy)\n",
    "\n",
    "We'll illustrate both methods using a two step sequence where the first step classifies an input question as being about `LangChain`, `Anthropic`, or `Other`, then routes to a corresponding prompt chain."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c1c6edac",
   "metadata": {},
   "source": [
    "## Example Setup\n",
    "First, let's create a chain that will identify incoming questions as being about `LangChain`, `Anthropic`, or `Other`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8a8a1967",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Anthropic'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_anthropic import ChatAnthropic\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "\n",
    "chain = (\n",
    "    PromptTemplate.from_template(\n",
    "        \"\"\"Given the user question below, classify it as either being about `LangChain`, `Anthropic`, or `Other`.\n",
    "\n",
    "Do not respond with more than one word.\n",
    "\n",
    "<question>\n",
    "{question}\n",
    "</question>\n",
    "\n",
    "Classification:\"\"\"\n",
    "    )\n",
    "    | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "chain.invoke({\"question\": \"how do I call Anthropic?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7655555f",
   "metadata": {},
   "source": [
    "Now, let's create three sub chains:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "89d7722d",
   "metadata": {},
   "outputs": [],
   "source": [
    "langchain_chain = PromptTemplate.from_template(\n",
    "    \"\"\"You are an expert in langchain. \\\n",
    "Always answer questions starting with \"As Harrison Chase told me\". \\\n",
    "Respond to the following question:\n",
    "\n",
    "Question: {question}\n",
    "Answer:\"\"\"\n",
    ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "anthropic_chain = PromptTemplate.from_template(\n",
    "    \"\"\"You are an expert in anthropic. \\\n",
    "Always answer questions starting with \"As Dario Amodei told me\". \\\n",
    "Respond to the following question:\n",
    "\n",
    "Question: {question}\n",
    "Answer:\"\"\"\n",
    ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")\n",
    "general_chain = PromptTemplate.from_template(\n",
    "    \"\"\"Respond to the following question:\n",
    "\n",
    "Question: {question}\n",
    "Answer:\"\"\"\n",
    ") | ChatAnthropic(model_name=\"claude-3-haiku-20240307\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d8d042c",
   "metadata": {},
   "source": [
    "## Using a custom function (Recommended)\n",
    "\n",
    "You can also use a custom function to route between different outputs. Here's an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "687492da",
   "metadata": {},
   "outputs": [],
   "source": [
    "def route(info):\n",
    "    if \"anthropic\" in info[\"topic\"].lower():\n",
    "        return anthropic_chain\n",
    "    elif \"langchain\" in info[\"topic\"].lower():\n",
    "        return langchain_chain\n",
    "    else:\n",
    "        return general_chain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "02a33c86",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableLambda\n",
    "\n",
    "full_chain = {\"topic\": chain, \"question\": lambda x: x[\"question\"]} | RunnableLambda(\n",
    "    route\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "c2e977a4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"As Dario Amodei told me, to use Anthropic, you can start by exploring the company's website and learning about their mission, values, and the different services and products they offer. Anthropic is focused on developing safe and ethical AI systems, so they have a strong emphasis on transparency and responsible AI development. \\n\\nDepending on your specific needs, you can look into Anthropic's AI research and development services, which cover areas like natural language processing, computer vision, and reinforcement learning. They also offer consulting and advisory services to help organizations navigate the challenges and opportunities of AI integration.\\n\\nAdditionally, Anthropic has released some open-source AI models and tools that you can explore and experiment with. These can be a great way to get hands-on experience with Anthropic's approach to AI development.\\n\\nOverall, Anthropic aims to be a reliable and trustworthy partner in the AI space, so I'd encourage you to reach out to them directly to discuss how they can best support your specific requirements.\", response_metadata={'id': 'msg_01CtLFgFSwvTaJomrihE87Ra', 'content': [ContentBlock(text=\"As Dario Amodei told me, to use Anthropic, you can start by exploring the company's website and learning about their mission, values, and the different services and products they offer. Anthropic is focused on developing safe and ethical AI systems, so they have a strong emphasis on transparency and responsible AI development. \\n\\nDepending on your specific needs, you can look into Anthropic's AI research and development services, which cover areas like natural language processing, computer vision, and reinforcement learning. They also offer consulting and advisory services to help organizations navigate the challenges and opportunities of AI integration.\\n\\nAdditionally, Anthropic has released some open-source AI models and tools that you can explore and experiment with. These can be a great way to get hands-on experience with Anthropic's approach to AI development.\\n\\nOverall, Anthropic aims to be a reliable and trustworthy partner in the AI space, so I'd encourage you to reach out to them directly to discuss how they can best support your specific requirements.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=53, output_tokens=219)})"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"how do I use Anthropic?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "48913dc6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"As Harrison Chase told me, using LangChain involves a few key steps:\\n\\n1. **Set up your environment**: Install the necessary Python packages, including the LangChain library itself, as well as any other dependencies your application might require, such as language models or other integrations.\\n\\n2. **Understand the core concepts**: LangChain revolves around a few core concepts, like Agents, Chains, and Tools. Familiarize yourself with these concepts and how they work together to build powerful language-based applications.\\n\\n3. **Identify your use case**: Determine what kind of task or application you want to build using LangChain, such as a chatbot, a question-answering system, or a document summarization tool.\\n\\n4. **Choose the appropriate components**: Based on your use case, select the right LangChain components, such as agents, chains, and tools, to build your application.\\n\\n5. **Integrate with language models**: LangChain is designed to work seamlessly with various language models, such as OpenAI's GPT-3 or Anthropic's models. Connect your chosen language model to your LangChain application.\\n\\n6. **Implement your application logic**: Use LangChain's building blocks to implement the specific functionality of your application, such as prompting the language model, processing the response, and integrating with other services or data sources.\\n\\n7. **Test and iterate**: Thoroughly test your application, gather feedback, and iterate on your design and implementation to improve its performance and user experience.\\n\\nAs Harrison Chase emphasized, LangChain provides a flexible and powerful framework for building language-based applications, making it easier to leverage the capabilities of modern language models. By following these steps, you can get started with LangChain and create innovative solutions tailored to your specific needs.\", response_metadata={'id': 'msg_01H3UXAAHG4TwxJLpxwuuVU7', 'content': [ContentBlock(text=\"As Harrison Chase told me, using LangChain involves a few key steps:\\n\\n1. **Set up your environment**: Install the necessary Python packages, including the LangChain library itself, as well as any other dependencies your application might require, such as language models or other integrations.\\n\\n2. **Understand the core concepts**: LangChain revolves around a few core concepts, like Agents, Chains, and Tools. Familiarize yourself with these concepts and how they work together to build powerful language-based applications.\\n\\n3. **Identify your use case**: Determine what kind of task or application you want to build using LangChain, such as a chatbot, a question-answering system, or a document summarization tool.\\n\\n4. **Choose the appropriate components**: Based on your use case, select the right LangChain components, such as agents, chains, and tools, to build your application.\\n\\n5. **Integrate with language models**: LangChain is designed to work seamlessly with various language models, such as OpenAI's GPT-3 or Anthropic's models. Connect your chosen language model to your LangChain application.\\n\\n6. **Implement your application logic**: Use LangChain's building blocks to implement the specific functionality of your application, such as prompting the language model, processing the response, and integrating with other services or data sources.\\n\\n7. **Test and iterate**: Thoroughly test your application, gather feedback, and iterate on your design and implementation to improve its performance and user experience.\\n\\nAs Harrison Chase emphasized, LangChain provides a flexible and powerful framework for building language-based applications, making it easier to leverage the capabilities of modern language models. By following these steps, you can get started with LangChain and create innovative solutions tailored to your specific needs.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=50, output_tokens=400)})"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"how do I use LangChain?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a14d0dca",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='4', response_metadata={'id': 'msg_01UAKP81jTZu9fyiyFYhsbHc', 'content': [ContentBlock(text='4', type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=28, output_tokens=5)})"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"whats 2 + 2\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5147b827",
   "metadata": {},
   "source": [
    "## Using a RunnableBranch\n",
    "\n",
    "A `RunnableBranch` is a special type of runnable that allows you to define a set of conditions and runnables to execute based on the input. It does **not** offer anything that you can't achieve in a custom function as described above, so we recommend using a custom function instead.\n",
    "\n",
    "A `RunnableBranch` is initialized with a list of (condition, runnable) pairs and a default runnable. It selects which branch by passing each condition the input it's invoked with. It selects the first condition to evaluate to True, and runs the corresponding runnable to that condition with the input. \n",
    "\n",
    "If no provided conditions match, it runs the default runnable.\n",
    "\n",
    "Here's an example of what it looks like in action:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "2a101418",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"As Dario Amodei told me, to use Anthropic, you should first familiarize yourself with our mission and principles. Anthropic is committed to developing safe and beneficial artificial intelligence that can help solve important problems facing humanity. \\n\\nTo get started, I recommend exploring the resources on our website, which cover our research, products, and approach to AI development. You can also reach out to our team to learn more about how Anthropic's technology and services can support your specific needs.\\n\\nThe key is to engage with us in a way that aligns with our values of transparency, ethical AI, and a commitment to the wellbeing of humanity. We're here to collaborate and help you harness the power of advanced AI responsibly.\", response_metadata={'id': 'msg_0187BVnpniPDJnVvwf3M1LdY', 'content': [ContentBlock(text=\"As Dario Amodei told me, to use Anthropic, you should first familiarize yourself with our mission and principles. Anthropic is committed to developing safe and beneficial artificial intelligence that can help solve important problems facing humanity. \\n\\nTo get started, I recommend exploring the resources on our website, which cover our research, products, and approach to AI development. You can also reach out to our team to learn more about how Anthropic's technology and services can support your specific needs.\\n\\nThe key is to engage with us in a way that aligns with our values of transparency, ethical AI, and a commitment to the wellbeing of humanity. We're here to collaborate and help you harness the power of advanced AI responsibly.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=53, output_tokens=160)})"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.runnables import RunnableBranch\n",
    "\n",
    "branch = RunnableBranch(\n",
    "    (lambda x: \"anthropic\" in x[\"topic\"].lower(), anthropic_chain),\n",
    "    (lambda x: \"langchain\" in x[\"topic\"].lower(), langchain_chain),\n",
    "    general_chain,\n",
    ")\n",
    "full_chain = {\"topic\": chain, \"question\": lambda x: x[\"question\"]} | branch\n",
    "full_chain.invoke({\"question\": \"how do I use Anthropic?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "8d8caf9b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content=\"As Harrison Chase told me, using LangChain involves several key steps. First, you'll need to install the LangChain library and import the necessary modules. Then, you'll want to define your language model, any data sources you plan to use, and the specific tasks you want to accomplish, such as question answering, text generation, or agent-based reasoning. \\n\\nLangChain provides a flexible framework for building applications that leverage large language models. It includes abstractions for things like retrievers, prompts, and chains, which allow you to compose different components together to create powerful workflows. \\n\\nThe documentation on the LangChain website is excellent and covers many common use cases in detail. I'd recommend starting there to get a solid understanding of the core concepts and how to apply them to your specific needs. And of course, feel free to reach out if you have any other questions - I'm always happy to share more insights from my conversations with Harrison.\", response_metadata={'id': 'msg_01T1naS99wGPkEAP4LME8iAv', 'content': [ContentBlock(text=\"As Harrison Chase told me, using LangChain involves several key steps. First, you'll need to install the LangChain library and import the necessary modules. Then, you'll want to define your language model, any data sources you plan to use, and the specific tasks you want to accomplish, such as question answering, text generation, or agent-based reasoning. \\n\\nLangChain provides a flexible framework for building applications that leverage large language models. It includes abstractions for things like retrievers, prompts, and chains, which allow you to compose different components together to create powerful workflows. \\n\\nThe documentation on the LangChain website is excellent and covers many common use cases in detail. I'd recommend starting there to get a solid understanding of the core concepts and how to apply them to your specific needs. And of course, feel free to reach out if you have any other questions - I'm always happy to share more insights from my conversations with Harrison.\", type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=50, output_tokens=205)})"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"how do I use LangChain?\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "26159af7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='4', response_metadata={'id': 'msg_01T6T3TS6hRCtU8JayN93QEi', 'content': [ContentBlock(text='4', type='text')], 'model': 'claude-3-haiku-20240307', 'role': 'assistant', 'stop_reason': 'end_turn', 'stop_sequence': None, 'type': 'message', 'usage': Usage(input_tokens=28, output_tokens=5)})"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "full_chain.invoke({\"question\": \"whats 2 + 2\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa0f589d",
   "metadata": {},
   "source": [
    "## Routing by semantic similarity\n",
    "\n",
    "One especially useful technique is to use embeddings to route a query to the most relevant prompt. Here's an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "a23457d7",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.utils.math import cosine_similarity\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import PromptTemplate\n",
    "from langchain_core.runnables import RunnableLambda, RunnablePassthrough\n",
    "from langchain_openai import OpenAIEmbeddings\n",
    "\n",
    "physics_template = \"\"\"You are a very smart physics professor. \\\n",
    "You are great at answering questions about physics in a concise and easy to understand manner. \\\n",
    "When you don't know the answer to a question you admit that you don't know.\n",
    "\n",
    "Here is a question:\n",
    "{query}\"\"\"\n",
    "\n",
    "math_template = \"\"\"You are a very good mathematician. You are great at answering math questions. \\\n",
    "You are so good because you are able to break down hard problems into their component parts, \\\n",
    "answer the component parts, and then put them together to answer the broader question.\n",
    "\n",
    "Here is a question:\n",
    "{query}\"\"\"\n",
    "\n",
    "embeddings = OpenAIEmbeddings()\n",
    "prompt_templates = [physics_template, math_template]\n",
    "prompt_embeddings = embeddings.embed_documents(prompt_templates)\n",
    "\n",
    "\n",
    "def prompt_router(input):\n",
    "    query_embedding = embeddings.embed_query(input[\"query\"])\n",
    "    similarity = cosine_similarity([query_embedding], prompt_embeddings)[0]\n",
    "    most_similar = prompt_templates[similarity.argmax()]\n",
    "    print(\"Using MATH\" if most_similar == math_template else \"Using PHYSICS\")\n",
    "    return PromptTemplate.from_template(most_similar)\n",
    "\n",
    "\n",
    "chain = (\n",
    "    {\"query\": RunnablePassthrough()}\n",
    "    | RunnableLambda(prompt_router)\n",
    "    | ChatAnthropic(model=\"claude-3-haiku-20240307\")\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "664bb851",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using PHYSICS\n",
      "As a physics professor, I would be happy to provide a concise and easy-to-understand explanation of what a black hole is.\n",
      "\n",
      "A black hole is an incredibly dense region of space-time where the gravitational pull is so strong that nothing, not even light, can escape from it. This means that if you were to get too close to a black hole, you would be pulled in and crushed by the intense gravitational forces.\n",
      "\n",
      "The formation of a black hole occurs when a massive star, much larger than our Sun, reaches the end of its life and collapses in on itself. This collapse causes the matter to become extremely dense, and the gravitational force becomes so strong that it creates a point of no return, known as the event horizon.\n",
      "\n",
      "Beyond the event horizon, the laws of physics as we know them break down, and the intense gravitational forces create a singularity, which is a point of infinite density and curvature in space-time.\n",
      "\n",
      "Black holes are fascinating and mysterious objects, and there is still much to be learned about their properties and behavior. If I were unsure about any specific details or aspects of black holes, I would readily admit that I do not have a complete understanding and would encourage further research and investigation.\n"
     ]
    }
   ],
   "source": [
    "print(chain.invoke(\"What's a black hole\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "df34e469",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using MATH\n",
      "A path integral is a powerful mathematical concept in physics, particularly in the field of quantum mechanics. It was developed by the renowned physicist Richard Feynman as an alternative formulation of quantum mechanics.\n",
      "\n",
      "In a path integral, instead of considering a single, definite path that a particle might take from one point to another, as in classical mechanics, the particle is considered to take all possible paths simultaneously. Each path is assigned a complex-valued weight, and the total probability amplitude for the particle to go from one point to another is calculated by summing (integrating) over all possible paths.\n",
      "\n",
      "The key ideas behind the path integral formulation are:\n",
      "\n",
      "1. Superposition principle: In quantum mechanics, particles can exist in a superposition of multiple states or paths simultaneously.\n",
      "\n",
      "2. Probability amplitude: The probability amplitude for a particle to go from one point to another is calculated by summing the complex-valued weights of all possible paths.\n",
      "\n",
      "3. Weighting of paths: Each path is assigned a weight based on the action (the time integral of the Lagrangian) along that path. Paths with lower action have a greater weight.\n",
      "\n",
      "4. Feynman's approach: Feynman developed the path integral formulation as an alternative to the traditional wave function approach in quantum mechanics, providing a more intuitive and conceptual understanding of quantum phenomena.\n",
      "\n",
      "The path integral approach is particularly useful in quantum field theory, where it provides a powerful framework for calculating transition probabilities and understanding the behavior of quantum systems. It has also found applications in various areas of physics, such as condensed matter, statistical mechanics, and even in finance (the path integral approach to option pricing).\n",
      "\n",
      "The mathematical construction of the path integral involves the use of advanced concepts from functional analysis and measure theory, making it a powerful and sophisticated tool in the physicist's arsenal.\n"
     ]
    }
   ],
   "source": [
    "print(chain.invoke(\"What's a path integral\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff40bcb3",
   "metadata": {},
   "source": [
    "## Next steps\n",
    "\n",
    "You've now learned how to add routing to your composed LCEL chains.\n",
    "\n",
    "Next, check out the other how-to guides on runnables in this section."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "927b7498",
   "metadata": {},
   "source": []
  }
 ],
 "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}