mirror of
https://gitlab.rlp.net/mobitar/julia_course.git
synced 2024-09-14 12:47:20 +00:00
851 lines
27 KiB
Julia
851 lines
27 KiB
Julia
### A Pluto.jl notebook ###
|
|
# v0.19.42
|
|
|
|
using Markdown
|
|
using InteractiveUtils
|
|
|
|
# ╔═╡ 7a9ccfbc-bd2e-41d0-be5d-dea04b90d397
|
|
using BenchmarkTools
|
|
|
|
# ╔═╡ 8b1dfee2-bd8b-4d23-b9f8-9406002e0eaa
|
|
# Builtin package with useful functions for dealing with randomness
|
|
# Imported for the function `shuffle`
|
|
using Random
|
|
|
|
# ╔═╡ 1fb7d9af-333e-44f2-b693-09ff97937d4c
|
|
# Oh, no, you found my secret! 😱
|
|
# Don't change this hidden cell!
|
|
begin
|
|
using PlutoUI
|
|
|
|
TableOfContents()
|
|
end
|
|
|
|
# ╔═╡ 899a86c4-89e5-4779-8191-2b38ead6d567
|
|
md"""
|
|
# Workflow
|
|
"""
|
|
|
|
# ╔═╡ 81b6211e-b065-11ec-0a86-375721c85b07
|
|
md"""
|
|
## Jupyter notebooks
|
|
Although Pluto notebooks are very interactive and revolutionary, there are some cases where they provide you with too much interactivity. This is especially a problem when you are mutating a variable in a different cell. You can always use `begin` blocks, but sometimes, this is not practical.
|
|
|
|
Jupyter notebooks don't rerun every cell depending on a variable that was just updated. Cells are only run manually.
|
|
|
|
Jupyter supports Julia! Indeed, Jupyter is named after **Ju**lia + **Pyt**hon (+ **e**) + **R**.
|
|
|
|
To use Jupyter notebooks with Julia, you just have to install the `IJulia` package globally (not in an environment).
|
|
|
|
To install the package globally, open a terminal and launch Julia:
|
|
|
|
julia
|
|
|
|
In Julia, press `]` to switch to the package manager prompt (see `README.adoc`).
|
|
|
|
You should NOT activate an environment!
|
|
|
|
Now, run the following:
|
|
|
|
add IJulia
|
|
|
|
After the installation is done, you can verify that the package is installed by running the following (still in the package manager prompt):
|
|
|
|
status
|
|
|
|
Then you should see `IJulia` listed.
|
|
|
|
Now, you can exit Julia. You have now the Julia kernel for Jupyter.
|
|
|
|
If you don't already have JupyterLab (recommended) or Jupyter installed, install JupyterLab.
|
|
|
|
Launch JupyterLab or Jupyter. You should see the Julia kernel listed now! 🎉
|
|
"""
|
|
|
|
# ╔═╡ 7f45c502-0909-42df-b93d-384f743df6a9
|
|
md"""
|
|
## VS Code/Codium
|
|
If you are working on a big project, then splitting code up into different files does help maintaining it. Therefore, notebooks should not be used for big projects.
|
|
|
|
Because you should avoid using global variables in Julia and instead only call functions, you will not achieve the maximum performance using a notebook because of its workflow that does not support working only with functions. See the section about performance in this notebook.
|
|
|
|
Julia has a very good integration in VS Code. VS Codium is the free open source version of VS Code without proprietary code and telemetry. Both will do the job. Just start one of them after installation, go the extensions tab on the sidebar, type in Julia and install the Julia extension.
|
|
|
|
Now, you can edit Julia files with autocomplition, real-time feedback, formatting (you might need to set up formatting on save) and other features that you can find about in [the documentation of the extension](https://www.julia-vscode.org/docs/stable/).
|
|
|
|
Code written in normal Julia files can be run using one of these three methods:
|
|
|
|
1. You can let the VS Code/Codium extension run a line, a code section or an entire file.
|
|
|
|
2. You can run it using the following command in the terminal:
|
|
|
|
julia --project FILENAME.jl
|
|
|
|
The `--project` argument tells Julia to use the environment in the current directory. See section about environments in this notebook.
|
|
|
|
3. You can launch Julia, activate the project environment and run the functions defined in the Julia file after importing using the following in Julia:
|
|
|
|
include("FILENAME.jl")
|
|
|
|
I you are using the last method, consider using [Revise](https://timholy.github.io/Revise.jl/stable/)! It does reinclude functions of an included file if they where updated in this file. This way, you don't have to reinclude the file after every change or restart your Julia session (remember the startup time because of compilation on running code for the first time).
|
|
"""
|
|
|
|
# ╔═╡ f23ad33d-af1d-40c2-9efc-17ef8c4d1fb8
|
|
md"""
|
|
## Environments
|
|
If you are working on a project and not using Pluto notebooks, you should be using environments.
|
|
|
|
Environments separate project dependencies (packages). Therefore, you are less likely to have any conflicts between two packages. They also allow you to use a different version of a package for every project.
|
|
|
|
Each Pluto notebook has its own environment. After running
|
|
|
|
using PACKAGENAME
|
|
|
|
in a Pluto notebook, Pluto takes care of downloading and adding the package to the environment of the notebook. Therefore, if you are not working with Pluto notebooks, you should manually create and use a project environment and you have to add Packages manually to your environment.
|
|
|
|
To enter a project environment, you create a project directory and `cd` into it in the terminal. Then, you launch julia
|
|
|
|
julia --project
|
|
|
|
The argument `--project` activates a project environment in your current directory (project directory). To add packages to your project environment, press `]` to activate the package manager prompt and then run the following:
|
|
|
|
add PACKAGENAME
|
|
|
|
where you replace `PACKAGENAME` with the name of the package that you want to use.
|
|
|
|
In the package manager prompt, run the following to see a list of the packages added to your environment:
|
|
|
|
status
|
|
|
|
In the package manager prompt, run the following to update (or install if not already installed) all the packages in your environment:
|
|
|
|
update
|
|
|
|
In the package manager prompt, run the following to remove a package from your environment:
|
|
|
|
remove PACKAGENAME
|
|
|
|
|
|
"""
|
|
|
|
# ╔═╡ 6340aec8-6f77-4a30-8815-ce76ddecd6e8
|
|
md"""
|
|
## REPL
|
|
The Julia REPL is what you get when you launch Julia in the terminal.
|
|
|
|
The REPL is very useful if you want to quickly experiment with something or test if a function works how you do imagine.
|
|
|
|
It is also helpful if you want to look up documentation. To do so, press `?` in the normal prompt. This will cause the prompt to change from the normal prompt `julia>` to the help prompt `help?>`. Now you can enter what you are looking for and you will get its documentation.
|
|
|
|
The REPL does support tab autocompletion. Just press tab one time to autocomplete. Press tab two times to see what Julia would suggest to you. This also works in the package manager prompt.
|
|
|
|
It REPL also supports history. If you press arrow up or down, you can navigate the execution history. You can also press `CTRL+R` to see the a list of all last commands. You can search in this history and then press enter.
|
|
|
|
If you want syntax highlighting in your REPL, add the package `OhMyREPL` to your **global** environment and let this package be imported always on startup as described in [the documentation](https://kristofferc.github.io/OhMyREPL.jl/latest/installation/).
|
|
"""
|
|
|
|
# ╔═╡ 0e53e4ee-16a7-47ef-9992-77cbfd1ed258
|
|
md"""
|
|
# Benchmarking
|
|
Benchmarking is a tool to evaluate the performance of code.
|
|
|
|
Although you can just measure the time a function call takes, it is not a good idea to use only one measurement. Therefore, the package `BenchmarkTools.jl` runs a function multiple times and evaluates it statistically.
|
|
"""
|
|
|
|
# ╔═╡ d86e4a2f-e737-49a4-bc16-a149e81785bd
|
|
# Calculating factorials
|
|
function normal_for_loop(N)
|
|
factorials = zeros(BigInt, N)
|
|
|
|
for i in 1:N
|
|
factorials[i] = factorial(big(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
# ╔═╡ bc777c73-9573-41c3-8ab5-843335539f96
|
|
N = 5000
|
|
|
|
# ╔═╡ 55d1d616-b6ee-40dd-a0ed-6274a98b1e73
|
|
# This macro shows a lot of information
|
|
@benchmark normal_for_loop(N)
|
|
|
|
# ╔═╡ 00c27927-9c72-417a-862f-9b66318d9751
|
|
# This macro only shows the most important values
|
|
@btime normal_for_loop(N)
|
|
|
|
# ╔═╡ d21771de-272e-4d57-8c76-c75be709ad0a
|
|
md"""
|
|
# Multithreading
|
|
Julia is built as a modern language with parallel and asynchronous programming in mind.
|
|
|
|
You can use run Julia code on the GPU, on multiple CPU threads (multithreading) and with multiple processes (multiprocessing).
|
|
|
|
In this course, only multithreading will be presented.
|
|
|
|
The easiest way to use multithreading in Julia is by letting a `for` loop run on multiple threads. This can be achieved by only adding the macro `@threads` before the `for` loop.
|
|
|
|
Before trying to use multithreading, make sure that you launch Julia with multiple threads. To do so, run the following to launch Julia:
|
|
|
|
julia -t auto
|
|
|
|
You can append `--project` and other arguments to it.
|
|
|
|
`-t` is an alias to `--threads`. `auto` automatically determines how many threads your PC supports. You can also replace `auto` by the number of threads that you want to use. This number can not be bigger than the number of threads that your computer supports.
|
|
"""
|
|
|
|
# ╔═╡ 2cf5f891-8d8f-477d-8ef1-9b4b9480d477
|
|
# Show available number of threads
|
|
# If this is 1, then you can not use multithreading!
|
|
# If your PC has only one core and one thread, then you can do nothing about it.
|
|
# Otherwise, you have to tell Julia how many threads to use with the `-t` argument.
|
|
Threads.nthreads()
|
|
|
|
# ╔═╡ 76866972-0290-4e33-93de-b6128ba11994
|
|
md"""
|
|
## `@threads`
|
|
"""
|
|
|
|
# ╔═╡ cacc94b4-21e0-410e-acaa-80e37b447f94
|
|
# Using multiple threads for calculating factorials
|
|
function multithreaded_for_loop(N)
|
|
factorials = zeros(BigInt, N)
|
|
|
|
Threads.@threads for i in 1:N
|
|
factorials[i] = factorial(big(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
# ╔═╡ a3029480-bcdc-44f3-b504-8bd3bf3aa14d
|
|
# Magic 🪄
|
|
@btime multithreaded_for_loop(N)
|
|
|
|
# ╔═╡ 3fcdf8ff-224c-4616-acd6-d8062f3a7af0
|
|
# Demonstration of shuffling
|
|
shuffle(1:10)
|
|
|
|
# ╔═╡ f141dbb4-bdc5-4f16-8d97-fc0a3d5981f2
|
|
# Shuffle to change the order of calculating factorials
|
|
function shuffle_multithreaded_for_loop(N)
|
|
factorials = zeros(BigInt, N)
|
|
|
|
Threads.@threads for i in shuffle(1:N)
|
|
factorials[i] = factorial(big(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
# ╔═╡ e9dcda88-1eef-4c0a-99b2-12eaec56186b
|
|
# It did boost the performance even further 🤯
|
|
@btime shuffle_multithreaded_for_loop(N)
|
|
|
|
# ╔═╡ 768203a6-345d-4fa8-89a3-91e227579a38
|
|
md"""
|
|
The macro `@threads` splits the elements to iterate over evenly and gives every available thread a portion of these elements.
|
|
|
|
Example:
|
|
|
|
You have 2 threads and a loop over 1:10.
|
|
|
|
Then, one thread will get the values 1:5.
|
|
|
|
The other thread will get the values 6:10.
|
|
|
|
---
|
|
|
|
For our function above, calculating the factorial of the first elements does not take that much time as calculating the factorial of the last elements.
|
|
|
|
Therefore, the threads taking the first elements finish their job and wait for the thread that did get the portion with the biggest numbers.
|
|
|
|
With `shuffle`, we spread the hard job with big numbers over all threads. Therefore, the work is more evenly distributed and threads will finish (almost) together.
|
|
|
|
If you don't want the static work allocation of the macro `@threads` and do want to have more control about threads and what they do and when (dynamic thread allocation), take a look at the macro `Threads.@spawn`.
|
|
"""
|
|
|
|
# ╔═╡ 009bb2e8-f03e-40f7-a66b-166dc6a1962d
|
|
md"""
|
|
## Thread safety
|
|
Using threads is easy in Julia, but you have to pay attention when using threads!
|
|
|
|
If multiple threads try to modify a variable or an element in a container at the same time, weird dangerous things happen! 😵💫
|
|
"""
|
|
|
|
# ╔═╡ dd5e5073-be29-47e7-91c5-9e47c35f905c
|
|
function thread_unsafe()
|
|
N = 100000
|
|
var = 0
|
|
|
|
Threads.@threads for i in 1:N
|
|
var += 1
|
|
end
|
|
|
|
return var
|
|
end
|
|
|
|
# ╔═╡ 4554cbf0-36f5-45c6-a966-ad18b1592a60
|
|
# Run this cell multiple times.
|
|
# The output is random 🤯
|
|
thread_unsafe()
|
|
|
|
# ╔═╡ 7520326f-5576-42c7-aefd-29bc7d2c6b56
|
|
md"""
|
|
Lets see how to avoid such situations using another example.
|
|
"""
|
|
|
|
# ╔═╡ ec08a80c-8886-4312-9481-5c89951681e1
|
|
# Calculating the sum of sums
|
|
function thread_unsafe_sum(N)
|
|
sum_of_sums = 0
|
|
|
|
Threads.@threads for i in 1:N
|
|
sum_of_sums += sum(1:i)
|
|
end
|
|
|
|
return sum_of_sums
|
|
end
|
|
|
|
# ╔═╡ 3b5d9f7c-1fc9-4e85-8c07-8b5709895a10
|
|
N2 = 1000000
|
|
|
|
# ╔═╡ fbd2423b-aaea-47a7-a3cf-537860e11a93
|
|
# Also random!
|
|
thread_unsafe_sum(N2)
|
|
|
|
# ╔═╡ 73c86a45-d7f7-4d65-a588-1f5ff3adcf6f
|
|
md"""
|
|
The problem can be solved by using a vector with `N` elements. After calculating a sum, the thread that did calculate it places the result on a unique place in the vector that is reserved for this specific result.
|
|
|
|
At the end, the sum of sums is calculated by summing over the vector of sums.
|
|
"""
|
|
|
|
# ╔═╡ e65ad214-33ba-4d08-81f0-5f98022a9f78
|
|
function thread_safe_sum(N)
|
|
sums = zeros(Int64, N)
|
|
|
|
Threads.@threads for i in 1:N
|
|
sums[i] = sum(1:i)
|
|
end
|
|
|
|
return sum(sums)
|
|
end
|
|
|
|
# ╔═╡ 8ad3daa6-d221-4ff7-9bc2-8e8a66bdd8c7
|
|
# Stable!
|
|
sum_of_sums = @btime thread_safe_sum(N2)
|
|
|
|
# ╔═╡ 95dffc7f-3393-487e-8521-c96291cdc7bf
|
|
# Verify that we did not exceed the limit!
|
|
typemax(Int64) > sum_of_sums
|
|
|
|
# ╔═╡ ebd3a9d9-7a12-4001-9b53-913f664fb1c8
|
|
# Lets try shuffling again
|
|
function shuffle_safe_thread_sum(N)
|
|
sums = zeros(Int64, N)
|
|
|
|
Threads.@threads for i in shuffle(1:N)
|
|
sums[i] = sum(1:i)
|
|
end
|
|
|
|
return sum(sums)
|
|
end
|
|
|
|
# ╔═╡ ddd2409e-de34-4eb9-a0b7-e10cc6c0ce9f
|
|
# This is worse than the version without shuffling.
|
|
# In this case, shuffling is too expensive compared with its benefit
|
|
# Especially for multithreading, there is no silver bullet.
|
|
# Always benchmark! This is the only method to make sure that an "optimization" indeed an optimization is
|
|
@btime shuffle_safe_thread_sum(N2)
|
|
|
|
# ╔═╡ 24ad64f9-b0a4-48ac-b6dc-06a5d1c7b072
|
|
# Function to determine the shuffling cost
|
|
function shuffling_cost(N)
|
|
shuffle(1:N)
|
|
|
|
return
|
|
end
|
|
|
|
# ╔═╡ fe0b18c0-cbf0-421d-b6a0-987321a0b09d
|
|
# The shuffling itself is too expensive compared to the calculation in the loop
|
|
@btime shuffling_cost(N2)
|
|
|
|
# ╔═╡ 09f71a9e-6798-492f-98df-45087d0c4c8b
|
|
md"""
|
|
# Performance optimization
|
|
Julia has a focus on high performance. But if you don't pay attention, you might produce code that either can not be optimized by the compiler or results in a lot of allocations. In both cases, your code will be slow.
|
|
|
|
In this section, some tools and tips for performance optimization in Julia are presented.
|
|
"""
|
|
|
|
# ╔═╡ 491e077c-fbf0-4ae7-b54b-9f9c68f8f1b0
|
|
md"""
|
|
## Tips
|
|
Most important performance tips:
|
|
- Don't use global variables! 🌐
|
|
- Don't use containers with abstract type! 🆎
|
|
- Don't write long functions! 🦒
|
|
- Don't change the type of a variable! 🥸
|
|
- Preallocate when possible! 🕰️
|
|
- Reuse containers when possible! 🔄
|
|
- Use views instead of copies when possible! 🧐
|
|
- Benchmark, benchmark, benchmark! Also pay attention to allocations. ⌚️
|
|
|
|
These are some of the tips in the official documentation of Julia.
|
|
|
|
If you are writing performance critical code in Julia, make sure you have a **very good relationship** with [all performance tips in the documentation](https://docs.julialang.org/en/v1/manual/performance-tips/).
|
|
"""
|
|
|
|
# ╔═╡ 32981b03-edb9-417f-b5e0-c652e3ac715c
|
|
md"""
|
|
## Demo
|
|
"""
|
|
|
|
# ╔═╡ 6509dddd-ff17-49db-8e5e-fcea1ef0026c
|
|
N3 = 500000
|
|
|
|
# ╔═╡ 2a24aebc-0654-4d00-bdab-627a8e1a75f2
|
|
# Bad usage of a global array of type Any (container with abstract type)
|
|
begin
|
|
sin_vals = []
|
|
|
|
function global_allocating_access(N)
|
|
# First, empty the array because @btime runs the function multiple times
|
|
empty!(sin_vals)
|
|
|
|
for i in 1:N
|
|
push!(sin_vals, sin(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
@btime global_allocating_access(N3)
|
|
end
|
|
|
|
# ╔═╡ 56058ab1-4ea2-479d-88f9-5da6ac8c39c2
|
|
# Not using an array of type Any
|
|
begin
|
|
typed_sin_vals = Float64[]
|
|
|
|
function typed_global_allocating_access(N)
|
|
# First, empty the array because @btime runs the function multiple times
|
|
empty!(typed_sin_vals)
|
|
|
|
for i in 1:N
|
|
push!(typed_sin_vals, sin(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
@btime typed_global_allocating_access(N3)
|
|
end
|
|
|
|
# ╔═╡ ef164e7c-668a-4312-83f1-687ca7d4c8f9
|
|
# Preallocation
|
|
begin
|
|
preallocated_sin_vals = zeros(Float64, N3)
|
|
|
|
function global_preallocated_access(N)
|
|
for i in 1:N
|
|
preallocated_sin_vals[i] = sin(i)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
@btime global_preallocated_access(N3)
|
|
end
|
|
|
|
# ╔═╡ ebc621b5-3aa3-4cf7-bcdf-e4c5fbb79f50
|
|
# The difference of not using global variables
|
|
# Never use global variables!
|
|
begin
|
|
passed_preallocated_sin_vals = zeros(Float64, N3)
|
|
|
|
function local_preallocated_access(N, sin_vals)
|
|
for i in 1:N
|
|
sin_vals[i] = sin(i)
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
@btime local_preallocated_access(N3, passed_preallocated_sin_vals)
|
|
end
|
|
|
|
# ╔═╡ afcc15de-81e0-484f-80cf-3d805517c6e8
|
|
# Here, the difference of preallocation is clearer
|
|
begin
|
|
passed_sin_vals = Float64[]
|
|
|
|
function local_access(N, sin_vals)
|
|
# First, empty the array because @btime runs the function multiple times
|
|
empty!(passed_sin_vals)
|
|
|
|
for i in 1:N
|
|
push!(sin_vals, sin(i))
|
|
end
|
|
|
|
return
|
|
end
|
|
|
|
@btime local_access(N3, passed_sin_vals)
|
|
end
|
|
|
|
# ╔═╡ 43d2cbda-a21b-46ae-8433-7a9ef30c536b
|
|
md"""
|
|
## `StaticArrays`
|
|
If you are dealing with small arrays with less than 100 elements, then take a look at the package [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl). Especially if you are dealing with 2D or 3D coordinates, using `StaticArrays` will make a big performance difference.
|
|
"""
|
|
|
|
# ╔═╡ f0b634a5-19a9-4c61-932f-7ae357e13be2
|
|
md"""
|
|
## Profiling
|
|
Of course, you can profile your code in Julia. Check out the package [ProfileView](https://github.com/timholy/ProfileView.jl) for example.
|
|
"""
|
|
|
|
# ╔═╡ 00000000-0000-0000-0000-000000000001
|
|
PLUTO_PROJECT_TOML_CONTENTS = """
|
|
[deps]
|
|
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
|
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
|
|
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
|
|
|
[compat]
|
|
BenchmarkTools = "~1.3.2"
|
|
PlutoUI = "~0.7.59"
|
|
"""
|
|
|
|
# ╔═╡ 00000000-0000-0000-0000-000000000002
|
|
PLUTO_MANIFEST_TOML_CONTENTS = """
|
|
# This file is machine-generated - editing it directly is not advised
|
|
|
|
julia_version = "1.10.3"
|
|
manifest_format = "2.0"
|
|
project_hash = "b790568b1825bfdc3aa4caa97bcfe041482b08cd"
|
|
|
|
[[deps.AbstractPlutoDingetjes]]
|
|
deps = ["Pkg"]
|
|
git-tree-sha1 = "6e1d2a35f2f90a4bc7c2ed98079b2ba09c35b83a"
|
|
uuid = "6e696c72-6542-2067-7265-42206c756150"
|
|
version = "1.3.2"
|
|
|
|
[[deps.ArgTools]]
|
|
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"
|
|
version = "1.1.1"
|
|
|
|
[[deps.Artifacts]]
|
|
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"
|
|
|
|
[[deps.Base64]]
|
|
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
|
|
|
|
[[deps.BenchmarkTools]]
|
|
deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"]
|
|
git-tree-sha1 = "d9a9701b899b30332bbcb3e1679c41cce81fb0e8"
|
|
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
|
version = "1.3.2"
|
|
|
|
[[deps.ColorTypes]]
|
|
deps = ["FixedPointNumbers", "Random"]
|
|
git-tree-sha1 = "b10d0b65641d57b8b4d5e234446582de5047050d"
|
|
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
|
|
version = "0.11.5"
|
|
|
|
[[deps.CompilerSupportLibraries_jll]]
|
|
deps = ["Artifacts", "Libdl"]
|
|
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"
|
|
version = "1.1.1+0"
|
|
|
|
[[deps.Dates]]
|
|
deps = ["Printf"]
|
|
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"
|
|
|
|
[[deps.Downloads]]
|
|
deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"]
|
|
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"
|
|
version = "1.6.0"
|
|
|
|
[[deps.FileWatching]]
|
|
uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee"
|
|
|
|
[[deps.FixedPointNumbers]]
|
|
deps = ["Statistics"]
|
|
git-tree-sha1 = "05882d6995ae5c12bb5f36dd2ed3f61c98cbb172"
|
|
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
|
|
version = "0.8.5"
|
|
|
|
[[deps.Hyperscript]]
|
|
deps = ["Test"]
|
|
git-tree-sha1 = "179267cfa5e712760cd43dcae385d7ea90cc25a4"
|
|
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
|
|
version = "0.0.5"
|
|
|
|
[[deps.HypertextLiteral]]
|
|
deps = ["Tricks"]
|
|
git-tree-sha1 = "7134810b1afce04bbc1045ca1985fbe81ce17653"
|
|
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
|
|
version = "0.9.5"
|
|
|
|
[[deps.IOCapture]]
|
|
deps = ["Logging", "Random"]
|
|
git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c"
|
|
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
|
|
version = "0.2.4"
|
|
|
|
[[deps.InteractiveUtils]]
|
|
deps = ["Markdown"]
|
|
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
|
|
|
|
[[deps.JSON]]
|
|
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
|
|
git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a"
|
|
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
|
|
version = "0.21.4"
|
|
|
|
[[deps.LibCURL]]
|
|
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
|
|
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"
|
|
version = "0.6.4"
|
|
|
|
[[deps.LibCURL_jll]]
|
|
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
|
|
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"
|
|
version = "8.4.0+0"
|
|
|
|
[[deps.LibGit2]]
|
|
deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"]
|
|
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"
|
|
|
|
[[deps.LibGit2_jll]]
|
|
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"]
|
|
uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5"
|
|
version = "1.6.4+0"
|
|
|
|
[[deps.LibSSH2_jll]]
|
|
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
|
|
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"
|
|
version = "1.11.0+1"
|
|
|
|
[[deps.Libdl]]
|
|
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"
|
|
|
|
[[deps.LinearAlgebra]]
|
|
deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"]
|
|
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
|
|
|
[[deps.Logging]]
|
|
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
|
|
|
[[deps.MIMEs]]
|
|
git-tree-sha1 = "65f28ad4b594aebe22157d6fac869786a255b7eb"
|
|
uuid = "6c6e2e6c-3030-632d-7369-2d6c69616d65"
|
|
version = "0.1.4"
|
|
|
|
[[deps.Markdown]]
|
|
deps = ["Base64"]
|
|
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
|
|
|
|
[[deps.MbedTLS_jll]]
|
|
deps = ["Artifacts", "Libdl"]
|
|
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"
|
|
version = "2.28.2+1"
|
|
|
|
[[deps.Mmap]]
|
|
uuid = "a63ad114-7e13-5084-954f-fe012c677804"
|
|
|
|
[[deps.MozillaCACerts_jll]]
|
|
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"
|
|
version = "2023.1.10"
|
|
|
|
[[deps.NetworkOptions]]
|
|
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"
|
|
version = "1.2.0"
|
|
|
|
[[deps.OpenBLAS_jll]]
|
|
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"]
|
|
uuid = "4536629a-c528-5b80-bd46-f80d51c5b363"
|
|
version = "0.3.23+4"
|
|
|
|
[[deps.Parsers]]
|
|
deps = ["Dates", "PrecompileTools", "UUIDs"]
|
|
git-tree-sha1 = "8489905bcdbcfac64d1daa51ca07c0d8f0283821"
|
|
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
|
|
version = "2.8.1"
|
|
|
|
[[deps.Pkg]]
|
|
deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
|
|
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
|
|
version = "1.10.0"
|
|
|
|
[[deps.PlutoUI]]
|
|
deps = ["AbstractPlutoDingetjes", "Base64", "ColorTypes", "Dates", "FixedPointNumbers", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "MIMEs", "Markdown", "Random", "Reexport", "URIs", "UUIDs"]
|
|
git-tree-sha1 = "ab55ee1510ad2af0ff674dbcced5e94921f867a9"
|
|
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
|
|
version = "0.7.59"
|
|
|
|
[[deps.PrecompileTools]]
|
|
deps = ["Preferences"]
|
|
git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f"
|
|
uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
|
|
version = "1.2.1"
|
|
|
|
[[deps.Preferences]]
|
|
deps = ["TOML"]
|
|
git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6"
|
|
uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
|
version = "1.4.3"
|
|
|
|
[[deps.Printf]]
|
|
deps = ["Unicode"]
|
|
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"
|
|
|
|
[[deps.Profile]]
|
|
deps = ["Printf"]
|
|
uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79"
|
|
|
|
[[deps.REPL]]
|
|
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
|
|
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
|
|
|
|
[[deps.Random]]
|
|
deps = ["SHA"]
|
|
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
|
|
|
[[deps.Reexport]]
|
|
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
|
|
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
|
|
version = "1.2.2"
|
|
|
|
[[deps.SHA]]
|
|
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
|
|
version = "0.7.0"
|
|
|
|
[[deps.Serialization]]
|
|
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
|
|
|
|
[[deps.Sockets]]
|
|
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"
|
|
|
|
[[deps.SparseArrays]]
|
|
deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"]
|
|
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
|
|
version = "1.10.0"
|
|
|
|
[[deps.Statistics]]
|
|
deps = ["LinearAlgebra", "SparseArrays"]
|
|
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
|
|
version = "1.10.0"
|
|
|
|
[[deps.SuiteSparse_jll]]
|
|
deps = ["Artifacts", "Libdl", "libblastrampoline_jll"]
|
|
uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c"
|
|
version = "7.2.1+1"
|
|
|
|
[[deps.TOML]]
|
|
deps = ["Dates"]
|
|
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
|
|
version = "1.0.3"
|
|
|
|
[[deps.Tar]]
|
|
deps = ["ArgTools", "SHA"]
|
|
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"
|
|
version = "1.10.0"
|
|
|
|
[[deps.Test]]
|
|
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
|
|
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
|
|
|
[[deps.Tricks]]
|
|
git-tree-sha1 = "eae1bb484cd63b36999ee58be2de6c178105112f"
|
|
uuid = "410a4b4d-49e4-4fbc-ab6d-cb71b17b3775"
|
|
version = "0.1.8"
|
|
|
|
[[deps.URIs]]
|
|
git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b"
|
|
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
|
|
version = "1.5.1"
|
|
|
|
[[deps.UUIDs]]
|
|
deps = ["Random", "SHA"]
|
|
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
|
|
|
|
[[deps.Unicode]]
|
|
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
|
|
|
|
[[deps.Zlib_jll]]
|
|
deps = ["Libdl"]
|
|
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"
|
|
version = "1.2.13+1"
|
|
|
|
[[deps.libblastrampoline_jll]]
|
|
deps = ["Artifacts", "Libdl"]
|
|
uuid = "8e850b90-86db-534c-a0d3-1478176c7d93"
|
|
version = "5.8.0+1"
|
|
|
|
[[deps.nghttp2_jll]]
|
|
deps = ["Artifacts", "Libdl"]
|
|
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
|
|
version = "1.52.0+1"
|
|
|
|
[[deps.p7zip_jll]]
|
|
deps = ["Artifacts", "Libdl"]
|
|
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
|
|
version = "17.4.0+2"
|
|
"""
|
|
|
|
# ╔═╡ Cell order:
|
|
# ╟─899a86c4-89e5-4779-8191-2b38ead6d567
|
|
# ╟─81b6211e-b065-11ec-0a86-375721c85b07
|
|
# ╟─7f45c502-0909-42df-b93d-384f743df6a9
|
|
# ╟─f23ad33d-af1d-40c2-9efc-17ef8c4d1fb8
|
|
# ╟─6340aec8-6f77-4a30-8815-ce76ddecd6e8
|
|
# ╟─0e53e4ee-16a7-47ef-9992-77cbfd1ed258
|
|
# ╠═7a9ccfbc-bd2e-41d0-be5d-dea04b90d397
|
|
# ╠═d86e4a2f-e737-49a4-bc16-a149e81785bd
|
|
# ╠═bc777c73-9573-41c3-8ab5-843335539f96
|
|
# ╠═55d1d616-b6ee-40dd-a0ed-6274a98b1e73
|
|
# ╠═00c27927-9c72-417a-862f-9b66318d9751
|
|
# ╟─d21771de-272e-4d57-8c76-c75be709ad0a
|
|
# ╠═2cf5f891-8d8f-477d-8ef1-9b4b9480d477
|
|
# ╟─76866972-0290-4e33-93de-b6128ba11994
|
|
# ╠═cacc94b4-21e0-410e-acaa-80e37b447f94
|
|
# ╠═a3029480-bcdc-44f3-b504-8bd3bf3aa14d
|
|
# ╠═8b1dfee2-bd8b-4d23-b9f8-9406002e0eaa
|
|
# ╠═3fcdf8ff-224c-4616-acd6-d8062f3a7af0
|
|
# ╠═f141dbb4-bdc5-4f16-8d97-fc0a3d5981f2
|
|
# ╠═e9dcda88-1eef-4c0a-99b2-12eaec56186b
|
|
# ╟─768203a6-345d-4fa8-89a3-91e227579a38
|
|
# ╟─009bb2e8-f03e-40f7-a66b-166dc6a1962d
|
|
# ╠═dd5e5073-be29-47e7-91c5-9e47c35f905c
|
|
# ╠═4554cbf0-36f5-45c6-a966-ad18b1592a60
|
|
# ╟─7520326f-5576-42c7-aefd-29bc7d2c6b56
|
|
# ╠═ec08a80c-8886-4312-9481-5c89951681e1
|
|
# ╠═3b5d9f7c-1fc9-4e85-8c07-8b5709895a10
|
|
# ╠═fbd2423b-aaea-47a7-a3cf-537860e11a93
|
|
# ╟─73c86a45-d7f7-4d65-a588-1f5ff3adcf6f
|
|
# ╠═e65ad214-33ba-4d08-81f0-5f98022a9f78
|
|
# ╠═8ad3daa6-d221-4ff7-9bc2-8e8a66bdd8c7
|
|
# ╠═95dffc7f-3393-487e-8521-c96291cdc7bf
|
|
# ╠═ebd3a9d9-7a12-4001-9b53-913f664fb1c8
|
|
# ╠═ddd2409e-de34-4eb9-a0b7-e10cc6c0ce9f
|
|
# ╠═24ad64f9-b0a4-48ac-b6dc-06a5d1c7b072
|
|
# ╠═fe0b18c0-cbf0-421d-b6a0-987321a0b09d
|
|
# ╟─09f71a9e-6798-492f-98df-45087d0c4c8b
|
|
# ╟─491e077c-fbf0-4ae7-b54b-9f9c68f8f1b0
|
|
# ╟─32981b03-edb9-417f-b5e0-c652e3ac715c
|
|
# ╠═6509dddd-ff17-49db-8e5e-fcea1ef0026c
|
|
# ╠═2a24aebc-0654-4d00-bdab-627a8e1a75f2
|
|
# ╠═56058ab1-4ea2-479d-88f9-5da6ac8c39c2
|
|
# ╠═ef164e7c-668a-4312-83f1-687ca7d4c8f9
|
|
# ╠═ebc621b5-3aa3-4cf7-bcdf-e4c5fbb79f50
|
|
# ╠═afcc15de-81e0-484f-80cf-3d805517c6e8
|
|
# ╟─43d2cbda-a21b-46ae-8433-7a9ef30c536b
|
|
# ╟─f0b634a5-19a9-4c61-932f-7ae357e13be2
|
|
# ╟─1fb7d9af-333e-44f2-b693-09ff97937d4c
|
|
# ╟─00000000-0000-0000-0000-000000000001
|
|
# ╟─00000000-0000-0000-0000-000000000002
|