{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Continuous-integration-and-deployment)=\n", "# Continuous integration and deployment\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you've gotten this far, you now have a working knowledge of how to create a fully-featured Python package! We went through quite a lot to get here: we learned about package structure, developed source code, created tests, wrote documentation, and learned how to release new versions of a package.\n", "\n", "As you continue to develop your package into the future it would be helpful to automate many of these workflows so you and your collaborators can focus more on writing code and less on the nuances of packaging and testing. This is where continuous integration\\index{continuous integration} (CI) and continuous deployment\\index{continuous deployment} (CD) come in! CI/CD\\index{CI/CD} generally refers to the automated testing, building, and deployment of software. In this chapter, we'll first introduce CI/CD and walk through how to set it up with the GitHub Actions service. After that, we'll show how to set up CI/CD for a Python package, demonstrating concepts using the `pycounts` package we've been developing throughout this book.\n", "\n", "```{note}\n", "This chapter requires basic familiarity with Git and GitHub or similar version control\\index{version control} tools. To learn more about Git and GitHub, we recommend the following resources: [*Happy Git and GitHub for the useR*](https://happygitwithr.com){cite:p}`bryan2021` and [*Research Software Engineering with Python*](https://merely-useful.tech/py-rse/git-cmdline.html){cite:p}`rsep2021`.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## An introduction to CI/CD" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Continuous integration\\index{continuous integration} (CI) refers to the process of automatically evaluating your code as it is updated by yourself and contributors, to try and catch any potential issues your updates have caused. A CI workflow typically includes automatic execution of many of the steps we've seen throughout this book, such as running tests, calculating code coverage, and building documentation, among others.\n", "\n", "Continuous deployment\\index{continuous deployment} (CD) is the process of automating the deployment of new versions of your software to e.g., PyPI, from changes that have made it through CI.\n", "\n", "CI/CD\\index{CI/CD} can automate the packaging workflows that we've done manually throughout this book and can ultimately save you time and help you release new versions of your package quickly. CI/CD also helps others contribute to your package, because the process of updating your package is automated and doesn't depend on one person's expert knowledge (i.e., yours) of how to make releases manually. Even if your package won't be updated very often, setting up CI/CD is still beneficial because it means you don't have to remember all the manual steps required to make a release of your package (which can be daunting and deter you from wanting to update and maintain your package)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## CI/CD tools" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You could manually write and execute a CI/CD workflow by, for example, writing scripts that execute all of the steps we've walked through in previous chapters (i.e., running tests, building documented, build and release distributions, etc.). However, this process is not efficient or scalable, and it does not work well if more than one person (i.e., you) is contributing to your code.\n", "\n", "It is therefore more common to use a CI/CD service to implement CI/CD. These services essentially do what we described above but in an automated manner; we define a workflow, which these services will automatically run at certain \"trigger events\", which we can also define (for example, merging new code into the \"main\" branch of a GitHub repository might trigger the automatic deployment of a new version of the software).\n", "\n", "There are many CI/CD services out there — such as [GitHub Actions](https://docs.github.com/en/actions)\\index{GitHub Actions}, [Travis CI](https://www.travis-ci.com), and [CircleCi](https://circleci.com). We'll be using GitHub Actions in this chapter, which is a service for executing CI/CD workflows for software stored in a GitHub repository. We'll introduce how to use GitHub Actions in the next section.\n", "\n", "\\newpage\n", "\n", "```{note}\n", "GitHub Actions is free for public repositories and includes a generous amount of free minutes for private repositories. Read more in the GitHub Actions [documentation](https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions).\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Introduction-to-GitHub-Actions)=\n", "## Introduction to GitHub Actions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Key-concepts)=\n", "### Key concepts" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "GitHub Actions\\index{GitHub Actions} is a service for executing CI/CD workflows. The general idea is to create a set of commands that GitHub Actions will run on our behalf. We call this set of commands a \"workflow\". A GitHub Actions workflow is defined in a *.yml* file and contains the set of \"actions\" we want GitHub Actions to run for us (such as running our tests with `pytest` or building our documentation with `sphinx`). Actions are organized as \"steps\" in a workflow (e.g., step 1: run tests, step 2: build documentation), which in turn are organized into \"jobs\" (e.g., job 1: continuous integration). A workflow is executed on a machine provided by GitHub Actions called a \"runner\", when triggered by a particular \"event\" (like merging code into the `main` branch of a repository).\n", "\n", "That's a lot to take in, but don't worry! All this terminology is summarized in {numref}`08-actions-table`, and we'll walk through an example of using GitHub Actions, which refers to this terminology in the next section.\n", "\n", "```{table} Terminology used in GitHub Actions.\n", ":name: 08-actions-table\n", "|Keyword|Description|\n", "|:--- | :--- |\n", "|Actions|Individual tasks you want to perform.|\n", "|Workflow|A collection of actions (specified together in one file).|\n", "|Event|Something that triggers the running of a workflow.|\n", "|Runner|A machine that can run the Github Action(s).|\n", "|Job|A set of steps executed on the same runner.|\n", "|Step|A set of commands or actions which a job executes.|\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:A-toy-example)=\n", "### A toy example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this section, we'll walk through a simple example of running a workflow with GitHub Actions. The workflow will contains actions that simply print some things to the GitHub Actions runner's terminal using the `echo` command, when a change is made to a repository's content.\n", "\n", "- **Step 1**\n", "\n", " Create a new repository on GitHub named anything you like (we called our repository \"actions-example\"). Click on the \"Actions\" tab, and then click the \"Set up this workflow\" button as shown in {numref}`08-example-1-fig`.\n", "\n", "```{figure} images/08-example-1.png\n", "---\n", "width: 100%\n", "name: 08-example-1-fig\n", "alt: Setting up our first GitHub Actions workflow.\n", "---\n", "Setting up our first GitHub Actions workflow.\n", "```\n", "\n", "- **Step 2**\n", "\n", " Commit the *.yml* file that has been created for you to your repository by clicking \"Start commit\" and then \"Commit new file\". Then, open the file on GitHub. You should see the contents below. All the terminology we defined in **{numref}`08:Key-concepts`** exists in this workflow file, and each line is commented to describe exactly what it does. For example, we can see that:\n", " - This workflow is triggered `on` any push or pull request made to the main branch.\n", " - It consists of one job called `build`.\n", " - The job will run on `ubuntu-latest` (the latest version of the Ubuntu runner GitHub Actions provides).\n", " - It contains three `steps`. The first step \"checks out\" the repository — this is necessary for the runner to access your repository. The second step will print \"Hello, world!\" to the runner's terminal. The final step will print two lines. We'll discuss steps and how to write them in **{numref}`08:Actions-and-commands`**.\n", "\n", " ```yaml\n", " # This is a basic workflow to help you get started with Actions\n", " \n", " # Name of the workflow\n", " name: CI\n", "\n", " # Controls when the workflow will run\n", " on:\n", " # Triggers the workflow on push or pull request events but only for \n", " # the main branch\n", " push:\n", " branches: [ main ]\n", " pull_request:\n", " branches: [ main ]\n", "\n", " # Allows you to run this workflow manually from the Actions tab\n", " workflow_dispatch:\n", "\n", " # A workflow run is made up of one or more jobs that can run \n", " # sequentially or in parallel\n", " jobs:\n", " # This workflow contains a single job called \"build\"\n", " build:\n", " # The type of runner that the job will run on\n", " runs-on: ubuntu-latest\n", "\n", " # Steps represent a sequence of tasks that will be executed as \n", " # part of the job\n", " steps:\n", " # Checks-out your repository so your job can access it\n", " - name: Check-out repository\n", " uses: actions/checkout@v2\n", "\n", " # Runs a single command using the runners shell\n", " - name: Run a one-line script\n", " run: echo Hello, world!\n", "\n", " # Runs a set of commands using the runners shell\n", " - name: Run a multi-line script\n", " run: |\n", " echo Add other actions to build,\n", " echo test, and deploy your project.\n", " ```\n", "\n", "- **Step 3**\n", "\n", " Now, go to the \"Actions\" tab of your repository. You should see one workflow run, as in {numref}`08-example-2-fig`. This workflow ran because in **Step 2** we committed our workflow *.yml* file to the `main` branch of our repository, and the workflow is triggered to execute on any push or pull request with the `main` branch.\n", "\n", "```{figure} images/08-example-2.png\n", "---\n", "width: 100%\n", "name: 08-example-2-fig\n", "alt: Our first GitHub Actions workflow.\n", "---\n", "Our first GitHub Actions workflow.\n", "```\n", "\n", "- **Step 4**\n", "\n", " Look at the logs of the executed workflow by clicking on the \"Create blank.yml\" workflow, then clicking the \"build\" job in the left-hand panel. Click on arrows inside the build logs to examine their output. You should be able to see output printed to the screen for the \"Run a one-line script\" and \"Run a multi-line script\" steps in our workflow, as shown in {numref}`08-example-3-fig`.\n", "\n", "```{figure} images/08-example-3.png\n", "---\n", "width: 100%\n", "name: 08-example-3-fig\n", "alt: The logs of our first GitHub Actions workflow.\n", "---\n", "The logs of our first GitHub Actions workflow.\n", "```\n", "\n", "We'll practice writing workflows for implementing CI and CD for a Python package in the following sections of this chapter, but at this point, the high-level concepts to be aware of are:\n", "\n", "- A workflow is a set of commands that are triggered to execute by certain events (like a push to the `main` branch of a repository).\n", "- A workflow is run on a machine called a runner, which uses a particular operating system and is hosted by the CI/CD service.\n", "- A workflow contains one or more jobs.\n", "- Each job contains one or more steps to execute. In GitHub Actions, steps comprise either \"actions\" or \"commands\" as we'll discuss in the next section.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Actions-and-commands)=\n", "### Actions and commands" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we saw in the workflow file in **{numref}`08:A-toy-example`**, a step in a GitHub Actions can be an \"action\" (specified with the keyword `uses`) or a \"command\" (specified with keyword `run`). We'll briefly explain the difference between these two concepts here.\n", "\n", "Steps that use command line commands consist of a `name` and a `run` key, as shown in the example below: \n", "\n", "```yaml\n", "# Runs a single command using the runners shell\n", "- name: Run a one-line script\n", " run: echo Hello, world!\n", "```\n", "\n", "Anything after the `run` key will be executed at the runner's command line. You can run multiple commands in a single step using the `|` character:\n", "\n", "```yaml\n", "# Runs a set of commands using the runners shell\n", "- name: Run a multi-line script\n", " run: |\n", " echo Add other actions to build,\n", " echo test, and deploy your project.\n", "```\n", "\n", "In contrast to commands, actions are reusable units of code that perform a particular task without having to write out any commands. You'll typically use actions that have been created by others and shared on the [GitHub Marketplace](https://github.com/marketplace?type=). Actions are specified with the `uses` keyword, followed by the name of the action you want to use. The `@` symbol is used to specify which version of the action you want to use, like in the example below:\n", "\n", "```yaml\n", "# Checks-out your repository so your job can access it\n", "- name: Check-out repository\n", " uses: actions/checkout@v2\n", "```\n", "\n", "Some actions can also be configured with inputs using the `with` key, as in the example below:\n", "\n", "```yaml\n", "# Set up a Python environment for use in actions\n", "- name: Set up Python\n", " uses: actions/setup-python@v2\n", " with:\n", " python-version: 3.9\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Setting-up-continuous-integration)=\n", "## Setting up continuous integration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have a basic familiarity with GitHub Actions, in this section we'll build up a continuous integration\\index{continuous integration} workflow for a Python package. We'll create this workflow for the `pycounts` package we've been developing throughout this book. However, it will be applicable to any Python package, and it should be straightforward to see how you can modify it to your needs.\n", "\n", "Our goal here is to create a CI workflow that will install our package with `poetry`, run our package's tests with `pytest`, and build its documentation with `sphinx`, every time someone makes a push or pull request of changes to the `main` branch of the `pycounts` GitHub repository. These are steps we, or a collaborator, would usually perform locally every time our package is changed, so it makes sense to automate them." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To set up a workflow with GitHub Actions, we need to create a workflow file. Workflow files are *.yml* files located in a *`.github/workflows/`* directory in the root package directory. We'll call our file *`ci-cd.yml`*. You can create that file in an editor of your choice, or by running the following commands at the command line, from your root package directory:\n", "\n", "```{prompt} bash \\$ auto\n", "$ mkdir -p .github/workflows\n", "$ touch .github/workflows/ci-cd.yml\n", "```\n", "\n", "Your package directory structure should now look something like the following:\n", "\n", "\\newpage\n", "\n", "```{code-block} md\n", "---\n", "emphasize-lines: 2-4\n", "---\n", "pycounts\n", "├── .github <--------\n", "│ └── workflows <--------\n", "│ └── ci-cd.yml <--------\n", "├── .readthedocs.yml\n", "├── CHANGELOG.md\n", "├── CONDUCT.md\n", "├── CONTRIBUTING.md\n", "├── docs\n", "│ └── ...\n", "├── LICENSE\n", "├── README.md\n", "├── poetry.lock\n", "├── pyproject.toml\n", "├── src\n", "│ └── ...\n", "└── tests\n", " └── ...\n", "```\n", "\n", "Open this new *`ci-cd.yml`* file in an editor. We are going to set up a CI workflow that triggers when someone pushes new content or makes a pull-request to any branch of our repository (note that this differs slightly to **{numref}`08:A-toy-example`**, where our workflow ran on push or pull request to the \"main\" branch only). To set this up, copy and paste the following text into *`ci-cd.yml`*:\n", "\n", "```{tip}\n", "When building our CI workflow, we'll be using the same syntax and terminology we described previously in **{numref}`08:Introduction-to-GitHub-Actions`**. Don't be afraid to revise that section as needed.\n", "```\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "```\n", "\n", "\\newpage\n", "\n", "Now we need to set up the steps that will be executed if one of the above trigger events occurs. GitHub Actions essentially provides you with a blank operating system of your choice (a \"runner\"), which we need to set up based on what steps we are going to want it to execute. In our case, we need to install Python and install `poetry` on the runner, so that we can then install our packages and run its tests and build its documentation. Thus, our setup will involve the steps below, for which we've indicated whether step will use an action or command(s) (**{numref}`08:Actions-and-commands`**):\n", "\n", "1. Specify an operating system. We'll be using Ubuntu with the syntax `runs-on: ubuntu-latest`. MacOS and Windows are also available if you wish to test your package on those systems, but Ubuntu is a good default to use — see the GitHub Actions [documentation](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)).\n", "2. Install Python (action: [actions/setup-python@v2](https://github.com/actions/setup-python)).\n", "3. Checkout our repository so we can access its contents (action: [actions/checkout@v2](https://github.com/actions/checkout)).\n", "4. Install `poetry` (action: [snok/install-poetry@v1](https://github.com/snok/install-poetry)).\n", "5. Use `poetry` to install `pycounts` (command: `poetry install`).\n", "\n", "We'll add all these steps to our workflow in a job called \"ci\":\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " ci:\n", " # Set up operating system\n", " runs-on: ubuntu-latest\n", "\n", " # Define job steps\n", " steps:\n", " - name: Set up Python 3.9\n", " uses: actions/setup-python@v2\n", " with:\n", " python-version: 3.9\n", "\n", " - name: Check-out repository\n", " uses: actions/checkout@v2\n", "\n", " - name: Install poetry\n", " uses: snok/install-poetry@v1\n", "\n", " - name: Install package\n", " run: poetry install\n", "```\n", "\n", "The above steps will set up our system in preparation for:\n", "\n", "1. Running `pycounts`'s unit tests with `pytest`.\n", "2. Checking the code coverage of our tests.\n", "3. Checking that `pycounts`'s documentation builds correctly.\n", "\n", "We'll create each of these steps in the following sections." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running tests" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Remember all the hard work we put into writing tests for our package back in the **Chapter 5: {ref}`05:Testing`**? Well, we likely want to make sure that these tests (and any others that we add) continue to pass for any new changes proposed to our package. \n", "\n", "Recall that we used `pytest` as the testing framework for our `pycounts` package. This is listed as a development dependency of our package, so it will already be installed on our runner from \"Step 5\" when we execute `poetry install`. Therefore, we just need to add a new step to our workflow with a command to run `pytest`. Because our runner is not using a `conda` virtual environment, `poetry` sets one up automatically when `poetry install` is executed. We need to explicitly tell `poetry` to use this virtual environment it set up for us by prefixing commands with `poetry run`, as we do below:\n", "\n", "```yaml\n", " - name: Test with pytest\n", " run: poetry run pytest tests/ --cov=pycounts --cov-report=xml\n", "```\n", "\n", "\\newpage\n", "\n", "```{note}\n", "We could install `conda` on our runner and set up a virtual environment if we wanted to. But this is a lot of overhead for a workflow that is just going to run tests and build documentation, so we've decided not to do that here.\n", "```\n", "\n", "Note that in the command above we are also obtaining our test coverage through the `--cov` argument and outputting a report to *.xml* format with the `--cov-report` argument (these require the `pytest-cov` package which we used and added as a dependency of our package in **{numref}`05:Calculating-coverage`**). In the next section, we will integrate another service called [Codecov](https://codecov.io/) into our workflow that will automatically record test coverage for us using the *.xml* report." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recording code coverage" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the previous step, we ran the tests for our `pycounts` package. However, if someone adds new code to your package but forgets to write tests for that new code, your existing tests will still pass, but the coverage will be reduced. So, we probably want to track code coverage in our CI workflow.\n", "\n", "To do this, we could print the coverage to our runner's build log, but having the coverage buried in those logs is not overly helpful. Instead, it's common to use a service like [Codecov](https://codecov.io/) to track our code coverage for us. To set up Codecov, first create a Codecov account by linking it with your GitHub account, as described in the Codecov [documentation](https://docs.codecov.com/docs) (Codecov also supports GitLab and Bitbucket). Once you've done this, Codecov automatically syncs with all the repositories that you have access to. Now, to use Codecov to automatically track code coverage as part of our CI workflow we can use the action they've created called [codecov/codecov-action@v2](https://github.com/marketplace/actions/codecov), as below:\n", "\n", "```{attention}\n", "If your GitHub repository is private, you'll need to provide an \"upload token\" to allow Codecov to access it as described in the Codecov [documentation](https://github.com/marketplace/actions/codecov). \n", "```\n", "\n", "```yaml\n", " - name: Use Codecov to track coverage\n", " uses: codecov/codecov-action@v2\n", " with:\n", " files: ./coverage.xml # coverage report\n", "```\n", "\n", "With this step in our workflow, coverage will automatically be recorded for each new proposed change to our code. Codecov can show you whether coverage has increased or decreased, by how much, and will link to relevant areas of your package's source code. This information will automatically appear on any pull request someone makes to the `main` branch on GitHub, or it can be viewed anytime on the Codecov website; for example, {numref}`08-cov-fig` shows the coverage dashboard for a package called `pypkgs`, where coverage decreased significantly after the most recent commit.\n", "\n", "```{figure} images/08-cov.png\n", "---\n", "width: 100%\n", "name: 08-cov-fig\n", "alt: Example of the Codecov dashboard linked to a repository called pypkgs. Coverage decreased significantly after the most recent commit.\n", "---\n", "Example of the Codecov dashboard linked to a repository called pypkgs. Coverage decreased significantly after the most recent commit.\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Build documentation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The final step we'll add to our CI workflow will be to check that our documentation builds without issue. We'll use the same `make html` command we've used throughout this book to build documentation with `sphinx` to do this:\n", "\n", "```yaml\n", " - name: Build documentation\n", " run: poetry run make html --directory docs/\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing continuous integration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've now set up our CI pipeline! Our final *`.github/workflows/ci-cd.yml`* file looks like this:\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " ci:\n", " # Set up operating system\n", " runs-on: ubuntu-latest\n", "\n", " # Define job steps\n", " steps:\n", " - name: Set up Python 3.9\n", " uses: actions/setup-python@v2\n", " with:\n", " python-version: 3.9\n", "\n", " - name: Check-out repository\n", " uses: actions/checkout@v2\n", "\n", " - name: Install poetry\n", " uses: snok/install-poetry@v1\n", "\n", " - name: Install package\n", " run: poetry install\n", "\n", " - name: Test with pytest\n", " run: poetry run pytest tests/ --cov=pycounts --cov-report=xml\n", "\n", " - name: Use Codecov to track coverage\n", " uses: codecov/codecov-action@v2\n", " with:\n", " files: ./coverage.xml # coverage report\n", "\n", " - name: Build documentation\n", " run: poetry run make html --directory docs/\n", "```\n", "\n", "We're now ready to test out our workflow! Let's go ahead and commit our workflow file to version control and push it to GitHub. This will trigger our workflow because we configured it to run when someone pushes new work to any branch of our repository.\n", "\n", "```{prompt} bash \\$ auto\n", "$ git add .github/workflows/ci-cd.yml\n", "$ git commit -m \"build: add CI workflow\"\n", "$ git push\n", "```\n", "\n", "Now if we go to our `pycounts` GitHub repository and click on the \"Actions\" tab, we should see our workflow, as shown in {numref}`08-ci-1-fig`:\n", "\n", "```{figure} images/08-ci-1.png\n", "---\n", "width: 100%\n", "name: 08-ci-1-fig\n", "alt: Successfully run continuous integration workflow on GitHub.\n", "---\n", "Successfully run continuous integration workflow on GitHub.\n", "```\n", "\n", "We can investigate the build logs by clicking the \"ci\" job as in {numref}`08-ci-2-fig`:\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-ci-2.png\n", "---\n", "width: 100%\n", "name: 08-ci-2-fig\n", "alt: Continuous integration workflow logs.\n", "---\n", "Continuous integration workflow logs.\n", "```\n", "\n", "This workflow will trigger anytime someone makes a push or pull request with the `main` branch. Now you, or your collaborators, don't have to worry about remembering all these steps or running them manually! In the next section, we'll take this automation to the next level, and set up a workflow to automatically deploy a new version of our package if proposed changes pass the CI workflow." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setting up continuous deployment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the previous step, we set up CI for our package to check that tests run, code coverage is stable, and documentation still builds, whenever we make a push or pull request with new changes to the `main` branch of our repository.\n", "\n", "In this section, we'll set up continuous deployment\\index{continuous deployment} (CD). If the changes we push to our repository pass our CI, then we want a CD workflow that will automatically:\n", "\n", "1. Create a new version of our `pycounts` package.\n", "2. Build new distributions (i.e., sdist and wheel).\n", "3. Upload the distributions to TestPyPI and test that the package can be installed successfully.\n", "4. Upload the distributions to PyPI.\n", "5. Create a GitHub release.\n", "\n", "We'll build up that CD workflow in this section." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Setup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To set up CD, we'll add to the *`.github/workflows/ci-cd.yml`* workflow file we created in **{numref}`08:Setting-up-continuous-integration`**. Our aim here is to add a new job called \"cd\" to this workflow that will trigger a deployment of our package each time updated code is pushed to the `main` branch of our repository. We only want this job to execute if:\n", "\n", "1. The \"ci\" job passes — we don't want to deploy a new version of our package if it isn't passing CI. We can specify this constraint using the `needs` keyword in GitHub Actions ([documentation](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idneeds)).\n", "2. Code is pushed to the `main` branch — we don't want to deploy a new version of our package when a pull request is opened, we only want to deploy a new version when a pull request is merged into the `main` branch or changes are pushed to the `main` branch directly. We can specify this constraint using the `if` syntax in GitHub Actions ([documentation](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif)).\n", "\n", "We can set up a new job and add the two constraints above with the following syntax:\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " ci:\n", " # ...\n", " # CI steps hidden\n", " # ...\n", "\n", " cd:\n", " # Only run this job if the \"ci\" job passes\n", " needs: ci\n", "\n", " # Only run this job if new work is pushed to the \"main\" branch\n", " if: github.event_name == 'push' && github.ref == 'refs/heads/main'\n", "```\n", "\n", "Now we can set up our CD workflow. In GitHub Actions, each job runs on a fresh runner, which we need to set up from scratch. Once we've done that, our CD workflow will effectively comprise all the steps we walked through manually in **Chapter 7: {ref}`07:Releasing-and-versioning`**. Below we list all the steps required to set up the CD workflow:\n", "\n", "1. Specify an operating system. We'll be using Ubuntu again with the syntax `runs-on: ubuntu-latest`.\n", "2. Install Python (action: [actions/setup-python@v4](https://github.com/actions/setup-python)).\n", "3. Check out our repository so we can access its contents (action: [actions/checkout@v3](https://github.com/actions/checkout)).\n", "4. Make a new release of `pycounts` (action: [python-semantic-release/python-semantic-release@v8.3.0](https://python-semantic-release.readthedocs.io/en/latest/automatic-releases/github-actions.html), this uses the [Python Semantic Release (PSR)](https://python-semantic-release.readthedocs.io/en/latest/) tool which we described in **{numref}`07:Automatic-version-bumping`** and will describe again below).\n", "5. Upload new release to TestPyPI (action: [pypa/gh-action-pypi-publish@release/v1](https://github.com/pypa/gh-action-pypi-publish)).\n", "6. Test that the new package version installs successfully from TestPyPI (command: `pip install`).\n", "7. Upload new release to PyPI (action: [pypa/gh-action-pypi-publish@release/v1](https://github.com/pypa/gh-action-pypi-publish)).\n", "8. Create a new release on GitHub (action: [python-semantic-release/upload-to-gh-release@main](https://python-semantic-release.readthedocs.io/en/latest/github-action.html#))\n", "\n", "Steps 1 to 3 are the same as we set up previously for our CI workflow, so we can just copy and paste them into our \"cd\" job as below. The only new code here is that we specify the input parameter `fetch-depth: 0` for the [actions/checkout@v3](https://github.com/actions/checkout) action. This input parameter will allow the PSR tool to access the full history of commits in our repository, so that it can determine how to bump the package's version. Without this parameter, PSR can only access the single most recent commit message.\n", "\n", "One more thing we need to add is to give GitHub actions permissions to allow token authentication and creation of GitHub releases. We do this at the very beginning of the workflow under the name `cd`.\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " ci:\n", " # ...\n", " # CI steps hidden\n", " # ...\n", "\n", " cd:\n", " permissions:\n", " id-token: write\n", " contents: write\n", " \n", " # Only run this job if the \"ci\" job passes\n", " needs: ci\n", "\n", " # Only run this job if new work is pushed to \"main\"\n", " if: github.event_name == 'push' && github.ref == 'refs/heads/main'\n", "\n", " # Set up operating system\n", " runs-on: ubuntu-latest\n", "\n", " # Define job steps\n", " steps:\n", " - name: Set up Python 3.9\n", " uses: actions/setup-python@v2\n", " with:\n", " python-version: 3.9\n", "\n", " - name: Check-out repository\n", " uses: actions/checkout@v2\n", " with:\n", " fetch-depth: 0\n", "```\n", "\n", "We'll discuss steps 4 to 8 in the sections below." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(08:Automatically-creating-a-new-package-version)=\n", "### Automatically creating a new package version" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we saw in **Chapter 7: {ref}`07:Releasing-and-versioning`**, there are a few key steps to go through when creating a new version of your package:\n", "\n", "1. Document what's changed in the *`CHANGELOG.md`*\\index{documentation!changelog}.\n", "2. Bump the package version\\index{versioning} number.\n", "3. Build a new sdist and wheel\\index{distribution}.\n", "4. Publish the new package version to TestPyPI and PyPI\n", "5. Tag\\index{version control!tag} a new release\\index{version control!release} on GitHub.\n", "\n", "In **{numref}`07:Automatic-version-bumping`**, we introduced the [Python Semantic Release (PSR)](https://python-semantic-release.readthedocs.io/en/latest/) tool, which can automatically bump your package's version number based on keywords it finds in commit messages.\n", "\n", "However, PSR\\index{versioning!Python Semantic Release}\\index{Python Semantic Release} can do more than this! In fact, it can do almost all the steps we list above (the one step it will not do is publish the new package version to TestPyPI and PyPI). In **{numref}`07:Automatic-version-bumping`**, we saw how PSR is configured using a table called `[tool.semantic_release]` in the *`pyproject.toml`*\\index{pyproject.toml}. To configure it to perform all the steps above, we need to add a few keys to that table as follows:\n", "\n", "```toml\n", "[tool.semantic_release]\n", "version_toml = [\n", " \"pyproject.toml:tool.poetry.version\",\n", "] # version location\n", "branch = \"main\" # branch to make releases of\n", "changelog_file = \"CHANGELOG.md\" # changelog file\n", "build_command = \"pip install poetry && poetry build\" # build dists \n", "```\n", "\n", "We've added comments above to clarify what each key is doing in the table, and we describe them in {numref}`08-psr-table`. You can also read more about these configuration options in the PSR [documentation](https://python-semantic-release.readthedocs.io/en/latest/configuration.html).\n", "\n", "\\newpage\n", "\n", "```{table} Description of Python Semantic Release configuration options.\n", ":name: 08-psr-table\n", "| Key | Description |\n", "| :--- | ---: |\n", "|`version_toml`|Location of version number for PSR to bump.|\n", "|`branch`|Branch where releases should be made from.|\n", "|`changelog_file`|Location of changelog file for PSR to update using commit messages.|\n", "|`build_command`|How to build new distributions for the release.|\n", "```\n", "\n", "With PSR configured in our *`pyproject.toml`*, we can now add it as a step to our CD workflow.\n", "\n", "In **{numref}`07:Automatic-version-bumping`**, we used the `semantic-release version` to get PSR to automatically update our version based on keywords it finds in all the commit messages that have been made since the last tag of your package. In our CD workflow, we'll instead be using GitHub Actions to run two tasks using PSR, the first action, [python-semantic-release/python-semantic-release@v8.3.0](https://python-semantic-release.readthedocs.io/en/latest/github-action.html), will bump our version, update our changelog, tag a new release on GitHub, and build a new sdist and wheel. We will discuss this now. Later we will discuss a second PSR GitHub action, [python-semantic-release/upload-to-gh-release@main](https://github.com/python-semantic-release/upload-to-gh-release), to also create a release on GitHub.\n", "\n", "PSR's [python-semantic-release/python-semantic-release@v8.3.0](https://python-semantic-release.readthedocs.io/en/latest/github-action.html) action will need to interact with our GitHub repository to modify our *`CHANGELOG.md`* file and bump the package version in `pyproject.toml`. To give PSR permission to do this there's a few things we need to do:\n", "\n", "1. Give content write permissions to GitHub actions, we did that above by adding `permissions:` and then `content: write` below it in the `cd` job in `ci-cd.yml`.\n", "2. Provide the first PSR GitHub action with a GitHub [access token](https://python-semantic-release.readthedocs.io/en/latest/envvars.html#env-gh-token) to allow it to read/write files in our repository. At the start of each workflow run, [GitHub automatically creates](https://docs.github.com/en/actions/security-guides/automatic-token-authentication) such a token for us, called `GITHUB_TOKEN`. We can pass this token to PSR using the `with` key and syntax `github_token: ${{ secrets.GITHUB_TOKEN }}`. We've done it for you in the code shown below.\n", "\n", "```yaml\n", " - name: Use Python Semantic Release to prepare release\n", " id: release\n", " uses: python-semantic-release/python-semantic-release@v8.3.0\n", " with:\n", " github_token: ${{ secrets.GITHUB_TOKEN }}\n", "```\n", "\n", "We'll see PSR in action shortly (and add the second action for it to create the release on GitHub), but let's first configure the rest of our CD workflow file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Uploading to TestPyPI and PyPI, and creating a release on GitHub" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The PSR step will create an sdist and wheel for our new package version. What we need to do next in our CD workflow is try uploading those distributions to TestPyPI\\index{TestPyPI}. This step is not strictly necessary, but it's a good idea because it can help catch any unexpected errors before we upload our package to PyPI\\index{PyPI}.\n", "\n", "Rather than write the code needed to do all this from scratch, we'll use the [pypa/gh-action-pypi-publish@release/v1](https://github.com/pypa/gh-action-pypi-publish) action. This action relies on token authentication with TestPyPI (rather than the classic username and password authentication). To use the action, you'll need to log-in to [TestPyPI](https://test.pypi.org), [create an API token](https://pypi.org/help/#apitoken), and [add the token as a secret](https://docs.github.com/en/actions/reference/encrypted-secrets) called `TEST_PYPI_API_TOKEN` to your GitHub repository, as shown in {numref}`08-token-1-fig`.\n", "\n", "```{figure} images/08-token-1.png\n", "---\n", "width: 100%\n", "name: 08-token-1-fig\n", "alt: Adding the TestPyPI API token to our GitHub repository.\n", "---\n", "Adding the TestPyPI API token to our GitHub repository.\n", "```\n", "\n", "To use the [pypa/gh-action-pypi-publish@release/v1](https://github.com/pypa/gh-action-pypi-publish) action, we can add the following step to our CD workflow. Note how we configure the action to use the token, specifying `password` as the `TEST_PYPI_API_TOKEN` we just added to our repository, and we point the action to the TestPyPI repository (`repository_url: https://test.pypi.org/legacy/`). We also use a conditional (see the keyword `if`) to only run this step if PSR successfully created a release in the step above.\n", "\n", "```yaml\n", " - name: Publish to TestPyPI\n", " uses: pypa/gh-action-pypi-publish@release/v1\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " repository-url: https://test.pypi.org/legacy/\n", " password: ${{ secrets.TEST_PYPI_API_TOKEN }}\n", "```\n", "\n", "The above action will publish the new version of your package to TestPyPI. We now want to test that we can install the package correctly from TestPyPI using the following command:\n", "\n", "```yaml\n", " - name: Test install from TestPyPI\n", " run: |\n", " pip install \\\n", " --index-url https://test.pypi.org/simple/ \\\n", " --extra-index-url https://pypi.org/simple \\\n", " pycounts\n", "```\n", "\n", "The next step in our CD workflow will be publishing our package to PyPI. This uses the same [pypa/gh-action-pypi-publish@release/v1](https://github.com/pypa/gh-action-pypi-publish) action as earlier and will require you to obtain a token from [PyPI](https://pypi.org) and add the token as `PYPI_API_TOKEN` to your GitHub repository, as shown in {numref}`08-token-2-fig`. Again, we also use a conditional (see the keyword `if`) to only run this step if PSR successfully created a release earlier in the workflow.\n", "\n", "```yaml\n", " - name: Publish to PyPI\n", " uses: pypa/gh-action-pypi-publish@release/v1\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " password: ${{ secrets.PYPI_API_TOKEN }}\n", "```\n", "\n", "\\newpage\n", "\n", "\n", "```{figure} images/08-token-2.png\n", "---\n", "width: 100%\n", "name: 08-token-2-fig\n", "alt: Adding the PyPI API token to our GitHub repository.\n", "---\n", "Adding the PyPI API token to our GitHub repository.\n", "```\n", "\n", "Finally, the last step in our CD workflow will be to create a GitHub release that corresponds to the new version published on TestPyPI and PyPI. \n", "To do this we will use a second GitHub action from PSR, [python-semantic-release/upload-to-gh-release@main](https://github.com/python-semantic-release/upload-to-gh-release). \n", "This action also requires the GitHub access token for permission to create the GitHub release.\n", "Again, we also use a conditional (see the keyword `if`) to only run this step if PSR successfully created a release earlier in the workflow.\n", "\n", "```yaml\n", " - name: Publish package distributions to GitHub Releases\n", " uses: python-semantic-release/upload-to-gh-release@main\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " github_token: ${{ secrets.GITHUB_TOKEN }}\n", "```\n", "\n", "That's it! We're done setting up!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Testing continuous deployment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We've now set up our CD workflow! Our final *`.github/workflows/ci-cd.yml`* file looks like this:\n", "\n", "```yaml\n", "name: ci-cd\n", "\n", "on: [push, pull_request]\n", "\n", "jobs:\n", " ci:\n", " # ...\n", " # CI steps same as before\n", " # ...\n", "\n", " cd:\n", " permissions:\n", " id-token: write\n", " contents: write\n", " # Only run this job if the \"ci\" job passes\n", " needs: ci\n", "\n", " # Only run this job if new work is pushed to \"main\"\n", " if: github.event_name == 'push' && github.ref == 'refs/heads/main'\n", "\n", " # Set up operating system\n", " runs-on: ubuntu-latest\n", "\n", " # Define job steps\n", " steps:\n", " - name: Set up Python\n", " uses: actions/setup-python@v4\n", " with:\n", " python-version: \"3.9\"\n", "\n", " - name: Check-out repository\n", " uses: actions/checkout@v3\n", " with:\n", " fetch-depth: 0\n", "\n", " - name: Use Python Semantic Release to prepare release\n", " id: release\n", " uses: python-semantic-release/python-semantic-release@v8.3.0\n", " with:\n", " github_token: ${{ secrets.GITHUB_TOKEN }}\n", "\n", " - name: Publish to TestPyPI\n", " uses: pypa/gh-action-pypi-publish@release/v1\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " repository-url: https://test.pypi.org/legacy/\n", " password: ${{ secrets.TEST_PYPI_API_TOKEN }}\n", "\n", " - name: Test install from TestPyPI\n", " run: |\n", " pip install \\\n", " --index-url https://test.pypi.org/simple/ \\\n", " --extra-index-url https://pypi.org/simple \\\n", " pycounts\n", "\n", " - name: Publish to PyPI\n", " uses: pypa/gh-action-pypi-publish@release/v1\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " password: ${{ secrets.PYPI_API_TOKEN }}\n", "\n", " - name: Publish package distributions to GitHub Releases\n", " uses: python-semantic-release/upload-to-gh-release@main\n", " if: steps.release.outputs.released == 'true'\n", " with:\n", " github_token: ${{ secrets.GITHUB_TOKEN }}\n", "```\n", "\n", "We're now ready to test out our full CI/CD workflow! Let's go ahead and commit our new workflow file and our *`pyproject.toml`* file (which we changed when we added the configuration options for PSR) to version control and push it to GitHub.\n", "When we commit our changes, we will be sure to include the \"feat\" keyword in our commit message to trigger PSR to make a minor release of our package from these changes.\n", "This will trigger our CI/CD workflow because we configured it to run when someone pushes to the \"main\" branch of our repository \n", "and indicates in a commit message that they have made a patch, minor or major change.\n", "\n", "```{note}\n", "We described what keywords trigger particular version bumps in **{numref}`07:Automatic-version-bumping`**.\n", "```\n", "\n", "```{prompt} bash \\$ auto\n", "$ git add .github/workflows/ci-cd.yml pyproject.toml\n", "$ git commit -m \"feat: add CI/CD workflow\"\n", "$ git push\n", "```\n", "\n", "Now if we go to our `pycounts` GitHub repository and click on the \"Actions\" tab, we should see a new run of our workflow as shown in {numref}`08-cd-1-fig`:\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-cd-1.png\n", "---\n", "width: 100%\n", "name: 08-cd-1-fig\n", "alt: Continuous deployment workflow on GitHub.\n", "---\n", "Continuous deployment workflow on GitHub.\n", "```\n", "\n", "If we click on the workflow we will see it was composed of two jobs, \"ci\" and \"cd\", each of which ran successfully, as shown in {numref}`08-cd-2-fig`:\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-cd-2.png\n", "---\n", "width: 100%\n", "name: 08-cd-2-fig\n", "alt: Successfully run continuous deployment workflow on GitHub.\n", "---\n", "Successfully run continuous deployment workflow on GitHub.\n", "```\n", "\n", "If we click on the \"cd\" job to view the build log, we can see that PSR parsed our commit message — \"feat: add CI/CD workflow\" — to determine that our package should be bumped with a minor release from 0.2.0 to 0.3.0 as shown in {numref}`08-cd-3-fig`.\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-cd-3.png\n", "---\n", "width: 100%\n", "name: 08-cd-3-fig\n", "alt: The Python semantic release tool automatically bumped the package version from 0.2.0 to 0.3.0.\n", "---\n", "The Python semantic release tool automatically bumped the package version from 0.2.0 to 0.3.0.\n", "```\n", "\n", "PSR also automatically updated our changelog and tagged a new release of our package, as shown in {numref}`08-cd-4-fig` and {numref}`08-cd-5-fig`, respectively.\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-cd-4.png\n", "---\n", "width: 100%\n", "name: 08-cd-4-fig\n", "alt: The Python semantic release tool automatically updated the changelog and added an entry for v0.3.0 based on commit messages.\n", "---\n", "The Python semantic release tool automatically updated the changelog and added an entry for v0.3.0 based on commit messages.\n", "```\n", "\n", "```{figure} images/08-cd-5.png\n", "---\n", "width: 100%\n", "name: 08-cd-5-fig\n", "alt: The Python semantic release tool automatically created tagged release v0.3.0.\n", "---\n", "The Python semantic release tool automatically created tagged release v0.3.0.\n", "```\n", "\n", "\\newpage\n", "\n", "Finally, we can see from the build logs that the new version of our package was released to TestPyPI and PyPI, as shown in {numref}`08-cd-6-fig`:\n", "\n", "\\newpage\n", "\n", "```{figure} images/08-cd-6.png\n", "---\n", "width: 100%\n", "name: 08-cd-6-fig\n", "alt: Deployment of new package version 0.3.0 to PyPI.\n", "---\n", "Deployment of new package version 0.3.0 to PyPI.\n", "```\n", "\n", "It didn't take too long for us to implement a CD workflow that completely automates all the steps we would usually have to perform manually when publishing a new release of our package. You may choose, or need, to use different tools and commands to what we've used here to implement CD for your packages in the future. But hopefully you can see the kinds of things that are possible with CD and how it can be useful to quickly deploy new releases of your package, how it saves you from having to remember all the commands you'd need to run to do this yourself, and how it lowers the barrier for potential collaborators to contribute to your package." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this chapter, we created CI/CD workflows for our `pycounts` package. What we've shown here is just one example and one set of tools for implementing CI/CD — but after reading this chapter, the hope is that you can appreciate the utility of CI/CD and the kinds of workflows that you can set up for your packages in the future.\n", "\n", "It's important to note that when using version control in this book, we've been directly modifying the `main` branch of our repository. However, changes to your package are more typically made on branches. Branches isolate your changes so you can develop your package without affecting the existing, stable version. Only when you're happy with your changes and they've passed CI, do you merge them into the existing source and trigger a CD workflow (if it exists). In collaborative environments, this is typically done via a \"pull request\", which you can read more about in the GitHub [documentation](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). Open-source projects are built off pull requests; go visit your favorite Python package repository on GitHub and click the \"Pull requests\" tab to see what and how collaborators are merging changes into the package, and the kind of CI/CD workflows that are set up to handle them.\n", "\n", "Finally, while we developed our workflow file from scratch here, the `py-pkgs-cookiecutter` [template](https://github.com/py-pkgs/py-pkgs-cookiecutter) we used to set up our package in **{numref}`03:Creating-a-package-structure`** can make the workflow *.yml* file for you. Recall from **{numref}`03:Creating-a-package-structure`** that one of the `py-pkgs-cookiecutter` prompts was as follows:\n", "\n", "```md\n", "Select include_github_actions:\n", "1 - no\n", "2 - ci\n", "3 - ci+cd\n", "Choose from 1, 2, 3 [1]:\n", "```\n", "\n", "In the future, you can include a workflow file for CI or CI+CD by selecting an appropriate response.\n", "\n", "Congratulations on making it to the end of the book and happy packaging!" ] } ], "metadata": { "celltoolbar": "Tags", "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.11.6" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": true, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "toc-autonumbering": false, "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { "06e8b80b569b4ac2bb5a989af9695ced": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "07d43b717dd94d33a8eeae066f36d839": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "09294cf0fd6549c78d0f15acf20400b2": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "096724e20b094296bb243e553b2820e6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_d2ef8aa5fad04047a98f733f89624812", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_d3343e0c83844ad987beb592199dd6f6", "value": -0.1 } }, "0b412fd29e8a44f5bfaaa9a7122a33c3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94", "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3" ], "layout": "IPY_MODEL_9a4a74191d4b483f8e6ca535c155a33f" } }, "107d8e3c4e4249429f4ea5f86f97caa7": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "1455d02e130148c4a24a0943765db04a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1574978c1a56499dbe842cb3bf5b9e8f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "16770b98026442368338a8d27ffceab4": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1989b160147948d4a6414de3b6c60694": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1a1f847a77d44da7977d6ed1ae70ac12": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_884172dbe7194ea7aba24ace80d35049", "IPY_MODEL_384a6bfb62454f0bb09a41d14a5eb9c9", "IPY_MODEL_8cc71f1ee02d4a10acbda0a6f9cbc4fb" ], "layout": "IPY_MODEL_da554c291c9f4442991fcf6d620e017e" } }, "1cb1287d7a5940829cb770a5553a95b3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "1da474a760df45c3a290bbf3ba8a7f94": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_323357ab97fc4ed0af4ab5743aa038cc", "style": "IPY_MODEL_64e2b864e41244388bcb173e865939f8" } }, "1ecb0c5110084d99bd02c6845b36b5f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "1ff35f5f7c9543158cd8fcb2fe45e951": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "200d9095bbf14c67af4083eebe042331": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a", "IPY_MODEL_544ffe02934f47abbe60aa68b1552268" ], "layout": "IPY_MODEL_2949d658a5e24ae8af8b48f1c68ca710" } }, "204835ba5cf741febeb96117f71f03db": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_e788ecb6fbad4de6aac05557dcf109f5", "style": "IPY_MODEL_871a273a718c4b7ab105f4bf6f9500e1" } }, "20f7461031e44d41bd5b14f4cdd8542a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "24102f82e9cf49aca85e512f59a1db81": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "262d0a636bf64c67a8b3b7fdb6852569": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "2844ba0a05594aeeb871eaf4fae2c4b9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "28a33b60ad9947cebcab993a3ecd7b01": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1989b160147948d4a6414de3b6c60694", "style": "IPY_MODEL_f24ec537d93846e48f35cf5bca83e11d", "value": 40 } }, "2949d658a5e24ae8af8b48f1c68ca710": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "2ad8e9ec7f1d4f5f89fadb89c51892cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_3679e8d246954e7295c526cec653232b", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_455179b0a10a4d3793163b6270e8c1d4" } }, "2be4850df84041778088cca110e64d11": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_3b84503a8368455bac727f95db5d01d0", "IPY_MODEL_5ed1935a5446410aa7103f97d2500695" ], "layout": "IPY_MODEL_5bc7f1fcf97f41fea3a8623cf73e21a3" } }, "2ec147d796d24e06a192384ff4f271d8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1ecb0c5110084d99bd02c6845b36b5f5", "style": "IPY_MODEL_750974cf708b48bcac5c4b3e178f9e34" } }, "323357ab97fc4ed0af4ab5743aa038cc": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "3679e8d246954e7295c526cec653232b": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "373d595c629d4c96ab6c69cabadfbae3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "384a6bfb62454f0bb09a41d14a5eb9c9": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_79023cd4f95e412d86f33dacb04f9cce", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_da243475d71a41f48bf4855c01052769" } }, "3966b0590b414fe1873b43e1835dbc0b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_096724e20b094296bb243e553b2820e6", "IPY_MODEL_890cbfc7c0f343d2b7991c18b88b8177", "IPY_MODEL_fcc376497f9d4b8aa11a102ebc19b6ab" ], "layout": "IPY_MODEL_ae6272a2743a4752b8798fed0b5dc332" } }, "39af2f7e78ff42679117a13c8e3e0c02": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_43d437e0efff40b0b7c59c66b9b815f5", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "3ac4ddb748c447f9903d248db1422cf1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_b8aa32ebeb924971ade818f6af335eec", "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2" ], "layout": "IPY_MODEL_e57657510026435496e5903d5c875cbe" } }, "3b84503a8368455bac727f95db5d01d0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a", "IPY_MODEL_544ffe02934f47abbe60aa68b1552268" ], "layout": "IPY_MODEL_20f7461031e44d41bd5b14f4cdd8542a" } }, "3ccc38f44df249cd833536b7a8a34327": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4007d719e1aa45349acb4e9f80f0e4ba": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_2ec147d796d24e06a192384ff4f271d8", "IPY_MODEL_f285911bbe39497a88f0994509363fbb" ], "layout": "IPY_MODEL_9ecdf012164d45aaa03c28b34bebf7fa" } }, "42727846507043a098dc2e932813ee65": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_16770b98026442368338a8d27ffceab4", "style": "IPY_MODEL_b1e4ec6cae8d454999896d5ec41be03c" } }, "43d437e0efff40b0b7c59c66b9b815f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "454c456031894c1498a69aaa4dcb0e97": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "455179b0a10a4d3793163b6270e8c1d4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "479edca2ec334d01b8af52d21f4c1565": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "494640e15fe7442ebfa3b6786ef64d82": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "4af9e16a11ed4597bbd81a734add4f5c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_535d254dd54d41ada990b62be72d5a14", "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2" ], "layout": "IPY_MODEL_7415264924174df19d8822f16c379f55" } }, "4c8b0dc16ddc40af8b131803f5280311": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_edcfad1763d44bbab957f8d4f981f4e4", "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8" ], "layout": "IPY_MODEL_ef77db303b044fe783f80b51f045e866" } }, "4d8487b20ef9419aab03dcc0c813fc77": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_cf422506005a43978b50beb43766a819", "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d" ], "layout": "IPY_MODEL_ce3b1869747b4502b90f4448204f5d94" } }, "4f613b87c7184b80bbc2806c184eb4cb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_9053f57dacfc4255ad1dc0b717cbd4be", "IPY_MODEL_42727846507043a098dc2e932813ee65" ], "layout": "IPY_MODEL_8c5220ab1fe748d0abf46067668fd9d1" } }, "50f2a1ea21a24ed9a0db566a4678e24e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_933e89a135cd42e196abb362e0302570", "style": "IPY_MODEL_b4ed787903d4445cb7fd4c6827acf9cd" } }, "527235c0249e4be68af5cc1b5f6ecaa8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "535d254dd54d41ada990b62be72d5a14": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e", "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e" ], "layout": "IPY_MODEL_d9616e978f2840609e88757e2e7e47f3" } }, "544ffe02934f47abbe60aa68b1552268": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_7acc7084e5c34c5a9b18b84c2a78660b", "style": "IPY_MODEL_d10b5afbdea647baa88b2156fda8c6c0" } }, "54bac03c012745098c9455c5a2325e0a": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_1cb1287d7a5940829cb770a5553a95b3", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "57c1af3db3d44cc7bfcfc97614bea646": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_9053341eacf94d139041e6aa7cc93233", "style": "IPY_MODEL_527235c0249e4be68af5cc1b5f6ecaa8", "value": 40 } }, "587b16eff8dc4011bcc72b4f4070b14d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "58a162ea4105481e9d1345fdc79c9f1d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "5ab9a789ce9a4e97845523051ef0762c": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_965ef0e308be48eaa8b8ef19c63bed40", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "5bc7f1fcf97f41fea3a8623cf73e21a3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "5d02d239659c496cbb1972806465cbba": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "5d68805b72bd49498579e5c204071464": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94", "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3" ], "layout": "IPY_MODEL_72b02467571a402da6d4414c5e4489fa" } }, "5e5877766afd49fcbb63cdbbff1f5684": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_1ff35f5f7c9543158cd8fcb2fe45e951", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_7bc761c122284f8b9822ff186e0f78bd", "value": -1 } }, "5ed1935a5446410aa7103f97d2500695": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_262d0a636bf64c67a8b3b7fdb6852569", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "645724dd1b174799b0ff40a9e6d6ab62": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_98b7be5be6ad4194bafdff2ffd9a89f0", "IPY_MODEL_e5aa35e4f4aa46cf8044c66339de4b96" ], "layout": "IPY_MODEL_454c456031894c1498a69aaa4dcb0e97" } }, "64c7a0f8b8904b1ca743bc4911a341b2": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "64e2b864e41244388bcb173e865939f8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "6991e87f25be44a78f759d7fd412588b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_2ad8e9ec7f1d4f5f89fadb89c51892cd", "IPY_MODEL_5e5877766afd49fcbb63cdbbff1f5684", "IPY_MODEL_81a51a474122457fa2f9da64776cf5d1" ], "layout": "IPY_MODEL_8a2b88d881b247d08b03cb1b7183128d" } }, "6a03b9b6618c43bb80bcccfc6f7c19f9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "6b15b70b85584ce18f7b6efe71563f96": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "6c890acf62af426cab22ed65ff429088": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "6ffdc8fcab164aedbf54cf3a45384d64": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "72b02467571a402da6d4414c5e4489fa": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7415264924174df19d8822f16c379f55": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "750974cf708b48bcac5c4b3e178f9e34": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "78efe79186ed46c78e9d91400f519f1d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_fdfc64c80c3c407bacf0a395dede228c", "style": "IPY_MODEL_8b9cd3fe33484cb794a9c2a4d60caa9a" } }, "79023cd4f95e412d86f33dacb04f9cce": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7a81ba3691dc47449c052dcd9070b618": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_ac55030546d54b0ca4d7e4fbf355a3c8", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_6b15b70b85584ce18f7b6efe71563f96" } }, "7acc7084e5c34c5a9b18b84c2a78660b": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7b066d9aaaa34fcda5803a72047566f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_d6b85a8f0ec64ee491848be1b8bf8188", "style": "IPY_MODEL_3ccc38f44df249cd833536b7a8a34327", "value": 40 } }, "7bc761c122284f8b9822ff186e0f78bd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "7c23e7df356f475e994f2f3e70730747": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "7d29dde0dd4046fb9717131bf06b637f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_b674d805670c4a969daa587ae7d22805", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_494640e15fe7442ebfa3b6786ef64d82", "value": -1 } }, "81a51a474122457fa2f9da64776cf5d1": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_94487d6608754721aa3f4401e77dc292", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "856e6ab0a1b14ac490277652b9beabf6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_0b412fd29e8a44f5bfaaa9a7122a33c3", "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c" ], "layout": "IPY_MODEL_f55ac2b80afd433a919cc2d7ab87b098" } }, "871a273a718c4b7ab105f4bf6f9500e1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "884172dbe7194ea7aba24ace80d35049": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "m", "layout": "IPY_MODEL_d2e7ead2f8d74c5180cbabbb30b674a8", "max": 2, "min": -2, "step": 0.1, "style": "IPY_MODEL_373d595c629d4c96ab6c69cabadfbae3" } }, "890cbfc7c0f343d2b7991c18b88b8177": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "FloatSliderModel", "state": { "description": "b", "layout": "IPY_MODEL_24102f82e9cf49aca85e512f59a1db81", "max": 3, "min": -3, "step": 0.5, "style": "IPY_MODEL_64c7a0f8b8904b1ca743bc4911a341b2", "value": -0.5 } }, "8a2b88d881b247d08b03cb1b7183128d": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8b9cd3fe33484cb794a9c2a4d60caa9a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "8c5220ab1fe748d0abf46067668fd9d1": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "8cc71f1ee02d4a10acbda0a6f9cbc4fb": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_6a03b9b6618c43bb80bcccfc6f7c19f9", "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAD4CAYAAADxeG0DAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAANZklEQVR4nO3df6xkZ13H8ffHLsUEKrTuQku3uG0kxKIm4E2DgkpoU9paWzFqSqIWa7JBQwIJpmltgih/IRGNEW1WaPxBY6tCpZIS2Eob4x9Ubmt//6ALFula2osoxZCgDV//mLNmejt379ydM/fuV9+v5OaeOeeZ53znOed+5swzM7upKiRJfX3HThcgSVqMQS5JzRnkktScQS5JzRnkktTcrp3Y6e7du2vfvn07sWtJauvOO+/8alXtWb9+R4J83759rK6u7sSuJamtJF+atd6pFUlqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqbrQgT3JCkn9K8omx+pQkbW7MK/J3AA+N2J8kaQ6jBHmSvcBPAB8aoz9J0vzGuiL/PeBK4NsbNUiyP8lqktW1tbWRditJWjjIk1wMPFVVdx6tXVUdqKqVqlrZs2fPoruVJA3GuCJ/HXBJkseAG4A3JvnICP1KkuawcJBX1dVVtbeq9gGXAZ+pqp9fuDJJ0lz8HLkkNbdrzM6q6nbg9jH7lCQdnVfkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzS0c5EnOSHJbkgeTPJDkHWMUJkmaz64R+ngGeFdV3ZXkJODOJAer6sER+pYkbWLhK/KqeqKq7hqWvwE8BJy+aL+SpPmMOkeeZB/wauCOMfuVJG1stCBP8kLgo8A7q+rpGdv3J1lNsrq2tjbWbiXp/71RgjzJ85iE+PVV9bFZbarqQFWtVNXKnj17xtitJIlxPrUS4MPAQ1X1gcVLkiRtxRhX5K8DfgF4Y5K7h5+LRuhXkjSHhT9+WFX/AGSEWiRJx8BvdkpScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtSc6MEeZILkjyS5FCSq8boU5I0n4WDPMkJwAeBC4GzgbckOXvRfiVJ89k1Qh/nAIeq6osASW4ALgUeHKHvZ/nNv32AB//16bG7laRtc/bLvovf+MlXjdrnGFMrpwNfnrr9+LDuWZLsT7KaZHVtbW2E3UqSYJwr8rlU1QHgAMDKykodSx9jP4tJ0v8FY1yRHwbOmLq9d1gnSdoGYwT554BXJDkzyYnAZcDNI/QrSZrDwlMrVfVMkrcDnwJOAK6rqgcWrkySNJdR5sir6hbgljH6kiRtjd/slKTmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJam6hIE/y/iQPJ7k3yU1JXjxWYZKk+Sx6RX4Q+P6q+kHg88DVi5ckSdqKhYK8qj5dVc8MNz8L7F28JEnSVow5R34F8MkR+5MkzWHXZg2S3AqcOmPTNVX18aHNNcAzwPVH6Wc/sB/g5S9/+TEVK0l6rk2DvKrOO9r2JG8FLgbOrao6Sj8HgAMAKysrG7aTJG3NpkF+NEkuAK4EfryqvjlOSZKkrVh0jvwPgJOAg0nuTnLtCDVJkrZgoSvyqvresQqRJB0bv9kpSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc2NEuRJ3pWkkuweoz9J0vwWDvIkZwDnA/+yeDmSpK0a44r8d4ErgRqhL0nSFi0U5EkuBQ5X1T1ztN2fZDXJ6tra2iK7lSRN2bVZgyS3AqfO2HQN8OtMplU2VVUHgAMAKysrXr1L0kg2DfKqOm/W+iQ/AJwJ3JMEYC9wV5Jzquoro1YpSdrQpkG+kaq6D3jJkdtJHgNWquqrI9QlSZqTnyOXpOaO+Yp8varaN1ZfkqT5eUUuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUXKq2//9BTrIGfOkY774bOB7/Oznr2hrr2hrr2prjtS5YrLbvqao961fuSJAvIslqVa3sdB3rWdfWWNfWWNfWHK91wXJqc2pFkpozyCWpuY5BfmCnC9iAdW2NdW2NdW3N8VoXLKG2dnPkkqRn63hFLkmaYpBLUnPHZZAn+dkkDyT5dpKVdduuTnIoySNJ3rTB/c9McsfQ7sYkJy6hxhuT3D38PJbk7g3aPZbkvqHd6th1zNjfe5Icnqrtog3aXTCM4aEkV21DXe9P8nCSe5PclOTFG7TblvHa7PEnef5wjA8N59K+ZdUytc8zktyW5MHh/H/HjDZvSPL1qeP77mXXNez3qMclE78/jNe9SV6zDTW9cmoc7k7ydJJ3rmuzbeOV5LokTyW5f2rdKUkOJnl0+H3yBve9fGjzaJLLt7zzqjrufoDvA14J3A6sTK0/G7gHeD5wJvAF4IQZ9/9L4LJh+VrgV5Zc7+8A795g22PA7m0cu/cAv7ZJmxOGsTsLOHEY07OXXNf5wK5h+X3A+3ZqvOZ5/MCvAtcOy5cBN27DsTsNeM2wfBLw+Rl1vQH4xHadT/MeF+Ai4JNAgNcCd2xzfScAX2HyhZkdGS/gx4DXAPdPrftt4Kph+apZ5z1wCvDF4ffJw/LJW9n3cXlFXlUPVdUjMzZdCtxQVd+qqn8GDgHnTDdIEuCNwF8Pq/4U+Kll1Trs7+eAv1jWPpbgHOBQVX2xqv4LuIHJ2C5NVX26qp4Zbn4W2LvM/W1insd/KZNzBybn0rnDsV6aqnqiqu4alr8BPAScvsx9juhS4M9q4rPAi5Octo37Pxf4QlUd6zfGF1ZVfw98bd3q6fNooyx6E3Cwqr5WVf8OHAQu2Mq+j8sgP4rTgS9P3X6c557o3w38x1RozGozph8FnqyqRzfYXsCnk9yZZP8S65j29uHl7XUbvJSbZxyX6QomV2+zbMd4zfP4/7fNcC59ncm5tS2GqZxXA3fM2PzDSe5J8skkr9qmkjY7Ljt9Tl3GxhdTOzFeR7y0qp4Ylr8CvHRGm4XHbtex1ba4JLcCp87YdE1VfXy765llzhrfwtGvxl9fVYeTvAQ4mOTh4Zl7KXUBfwS8l8kf3nuZTPtcscj+xqjryHgluQZ4Brh+g25GH69ukrwQ+Cjwzqp6et3mu5hMH/zn8P7H3wCv2IayjtvjMrwHdglw9YzNOzVez1FVlWQpn/fesSCvqvOO4W6HgTOmbu8d1k37NyYv63YNV1Kz2oxSY5JdwE8DP3SUPg4Pv59KchOTl/UL/QHMO3ZJ/hj4xIxN84zj6HUleStwMXBuDZODM/oYfbxmmOfxH2nz+HCcX8Tk3FqqJM9jEuLXV9XH1m+fDvaquiXJHybZXVVL/Qei5jguSzmn5nQhcFdVPbl+w06N15Qnk5xWVU8MU01PzWhzmMlc/hF7mbw/OLduUys3A5cNnyg4k8kz6z9ONxgC4jbgZ4ZVlwPLusI/D3i4qh6ftTHJC5KcdGSZyRt+989qO5Z185Jv3mB/nwNekcmne05k8rL05iXXdQFwJXBJVX1zgzbbNV7zPP6bmZw7MDmXPrPRk89Yhjn4DwMPVdUHNmhz6pG5+iTnMPkbXuoTzJzH5WbgF4dPr7wW+PrUlMKybfiqeCfGa53p82ijLPoUcH6Sk4ep0POHdfPbjndzj+Hd3zczmSf6FvAk8Kmpbdcw+cTBI8CFU+tvAV42LJ/FJOAPAX8FPH9Jdf4J8LZ1614G3DJVxz3DzwNMphiWPXZ/DtwH3DucRKetr2u4fRGTT0V8YZvqOsRkHvDu4efa9XVt53jNevzAbzF5ogH4zuHcOTScS2dtwxi9nsmU2L1T43QR8LYj5xnw9mFs7mHypvGPbENdM4/LuroCfHAYz/uY+rTZkmt7AZNgftHUuh0ZLyZPJk8A/z3k1y8zeV/l74BHgVuBU4a2K8CHpu57xXCuHQJ+aav79iv6ktRct6kVSdI6BrkkNWeQS1JzBrkkNWeQS1JzBrkkNWeQS1Jz/wOt0eLL/K7pHwAAAABJRU5ErkJggg==\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "8dda8bf6c916451e91b96e56fc4d2a76": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "9053341eacf94d139041e6aa7cc93233": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9053f57dacfc4255ad1dc0b717cbd4be": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_ef44415ff8dd456681965184a2a299e8", "style": "IPY_MODEL_cc29a198d1d44797a909ea9a68a5d484" } }, "907bd0f286754766afa0d6ba1612d301": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4d8487b20ef9419aab03dcc0c813fc77", "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8" ], "layout": "IPY_MODEL_1574978c1a56499dbe842cb3bf5b9e8f" } }, "90a70b5f560f437f9bb9422a4a6f6592": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "933e89a135cd42e196abb362e0302570": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "935fac04b32e4e0b9e1f9ea118a04ee1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_b9e169b8efac4eeb990ae37db1e8de12", "max": 200, "style": "IPY_MODEL_9927296f85f248858bdc0a0abcc67e94", "value": 100 } }, "94487d6608754721aa3f4401e77dc292": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": { "height": "350px" } }, "965ef0e308be48eaa8b8ef19c63bed40": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "97c973da7b714095959a2a66d7d28c65": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "98b7be5be6ad4194bafdff2ffd9a89f0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_b12e150681c94126b68be84b0d7cf158", "IPY_MODEL_204835ba5cf741febeb96117f71f03db" ], "layout": "IPY_MODEL_7c23e7df356f475e994f2f3e70730747" } }, "9927296f85f248858bdc0a0abcc67e94": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "9a4a74191d4b483f8e6ca535c155a33f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9c4a507b139f440fabaa95140d4b2193": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "9ecdf012164d45aaa03c28b34bebf7fa": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "a5e552b00bc54d1991d64d1e61258563": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "_dom_classes": [ "widget-interact" ], "children": [ "IPY_MODEL_7a81ba3691dc47449c052dcd9070b618", "IPY_MODEL_7d29dde0dd4046fb9717131bf06b637f", "IPY_MODEL_54bac03c012745098c9455c5a2325e0a" ], "layout": "IPY_MODEL_2844ba0a05594aeeb871eaf4fae2c4b9" } }, "ac55030546d54b0ca4d7e4fbf355a3c8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "acc0b478d5b748c29c4683cbcfc8e0f3": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_c69f7b00b5a54f60908cb3d92021daf6", "style": "IPY_MODEL_479edca2ec334d01b8af52d21f4c1565" } }, "ae6272a2743a4752b8798fed0b5dc332": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b070d7bd6a794d2ba0242e4220af368f": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b12e150681c94126b68be84b0d7cf158": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_1455d02e130148c4a24a0943765db04a", "style": "IPY_MODEL_c2bcf8f8ff7b4623a8311427461ec26b" } }, "b1e4ec6cae8d454999896d5ec41be03c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "b4ed787903d4445cb7fd4c6827acf9cd": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "b65b837d3b614513bef7014f6e97b8be": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_d9226ab50b4a4df9bdd6574f239a76a5", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "b674d805670c4a969daa587ae7d22805": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "b8aa32ebeb924971ade818f6af335eec": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e", "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e" ], "layout": "IPY_MODEL_587b16eff8dc4011bcc72b4f4070b14d" } }, "b9e169b8efac4eeb990ae37db1e8de12": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "bba860475dc34aa295f004356f4c7e70": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4f613b87c7184b80bbc2806c184eb4cb", "IPY_MODEL_b65b837d3b614513bef7014f6e97b8be" ], "layout": "IPY_MODEL_f128f679eeb840f88c63d5fb65e08abe" } }, "be52a81fc3e44efc8f0d70d81da55763": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_4007d719e1aa45349acb4e9f80f0e4ba", "IPY_MODEL_39af2f7e78ff42679117a13c8e3e0c02" ], "layout": "IPY_MODEL_90a70b5f560f437f9bb9422a4a6f6592" } }, "bf592509273c491c9496cc5bce017755": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_200d9095bbf14c67af4083eebe042331", "IPY_MODEL_5ed1935a5446410aa7103f97d2500695" ], "layout": "IPY_MODEL_b070d7bd6a794d2ba0242e4220af368f" } }, "bfe8c68e54b24762adb625e2d926bc2e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_09294cf0fd6549c78d0f15acf20400b2", "style": "IPY_MODEL_d5700c1140ff4a598008ed28c45f7d25" } }, "c2bcf8f8ff7b4623a8311427461ec26b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "c2cc4c190c324cc0a0ce9bf781a499eb": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "c69f7b00b5a54f60908cb3d92021daf6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cc29a198d1d44797a909ea9a68a5d484": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "cd9593419907498e8a80fa9fb7850749": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "ce130e9bb1874109b6e9641a6f6b872a": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_c2cc4c190c324cc0a0ce9bf781a499eb", "style": "IPY_MODEL_cd9593419907498e8a80fa9fb7850749" } }, "ce3b1869747b4502b90f4448204f5d94": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "cf422506005a43978b50beb43766a819": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_9c4a507b139f440fabaa95140d4b2193", "style": "IPY_MODEL_107d8e3c4e4249429f4ea5f86f97caa7" } }, "d10b5afbdea647baa88b2156fda8c6c0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d11a21b0488f4970babc1c7ad3f86aa8": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_97c973da7b714095959a2a66d7d28c65", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "d2a772f4a9db458db7a47d77c13dd3d5": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_6ffdc8fcab164aedbf54cf3a45384d64", "style": "IPY_MODEL_e0dea1c96cc34a53938b3f0c52e09fe1", "value": 40 } }, "d2e7ead2f8d74c5180cbabbb30b674a8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d2ef8aa5fad04047a98f733f89624812": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d3343e0c83844ad987beb592199dd6f6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d5700c1140ff4a598008ed28c45f7d25": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "d67190fd827a4b1a8ca8079df64d0a0a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d6b85a8f0ec64ee491848be1b8bf8188": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d9226ab50b4a4df9bdd6574f239a76a5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "d9616e978f2840609e88757e2e7e47f3": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "da243475d71a41f48bf4855c01052769": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "da554c291c9f4442991fcf6d620e017e": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e0dea1c96cc34a53938b3f0c52e09fe1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "e57657510026435496e5903d5c875cbe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "e5aa35e4f4aa46cf8044c66339de4b96": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_fc608307f2064139ade48955ecbaf7e9", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "e6a05e8e4f6a4cefb61f5f99b1eb13c2": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_d67190fd827a4b1a8ca8079df64d0a0a", "outputs": [ { "name": "stdout", "output_type": "stream", "text": "0 * 0 = 0\n" } ] } }, "e788ecb6fbad4de6aac05557dcf109f5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ea2be2adfc72403eb1236220a37dfd36": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "HBoxModel", "state": { "children": [ "IPY_MODEL_5d68805b72bd49498579e5c204071464", "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c" ], "layout": "IPY_MODEL_6c890acf62af426cab22ed65ff429088" } }, "ec1db3073c594dac8b081d2ad5e072e1": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_5d02d239659c496cbb1972806465cbba", "style": "IPY_MODEL_58a162ea4105481e9d1345fdc79c9f1d", "value": 40 } }, "edcfad1763d44bbab957f8d4f981f4e4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "VBoxModel", "state": { "children": [ "IPY_MODEL_cf422506005a43978b50beb43766a819", "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d" ], "layout": "IPY_MODEL_fd89962cc1214dd7afd0ee8f6c352b76" } }, "ef44415ff8dd456681965184a2a299e8": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "ef77db303b044fe783f80b51f045e866": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f128f679eeb840f88c63d5fb65e08abe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "f24ec537d93846e48f35cf5bca83e11d": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "SliderStyleModel", "state": { "description_width": "" } }, "f285911bbe39497a88f0994509363fbb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", "model_name": "IntSliderModel", "state": { "layout": "IPY_MODEL_07d43b717dd94d33a8eeae066f36d839", "style": "IPY_MODEL_8dda8bf6c916451e91b96e56fc4d2a76" } }, "f55ac2b80afd433a919cc2d7ab87b098": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fc608307f2064139ade48955ecbaf7e9": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fcc376497f9d4b8aa11a102ebc19b6ab": { "model_module": "@jupyter-widgets/output", "model_module_version": "1.0.0", "model_name": "OutputModel", "state": { "layout": "IPY_MODEL_06e8b80b569b4ac2bb5a989af9695ced", "outputs": [ { "data": { "image/png": "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\n", "text/plain": "
" }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ] } }, "fd89962cc1214dd7afd0ee8f6c352b76": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} }, "fdfc64c80c3c407bacf0a395dede228c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", "model_name": "LayoutModel", "state": {} } }, "version_major": 2, "version_minor": 0 } } }, "nbformat": 4, "nbformat_minor": 4 }