From 9244c25cacd5756b4d6a5b5ce8ec52dd1b8236dc Mon Sep 17 00:00:00 2001 From: ChrisRackauckas Date: Fri, 26 Jul 2024 14:52:20 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20=20@=208ed85?= =?UTF-8?q?8c4ce75371a3b8e22ccb0278e8c9ea45740=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lecture02/jl_r4SbWN/optimizing_16_1.png | Bin 0 -> 20670 bytes .../lecture02/jl_r4SbWN/optimizing_17_1.png | Bin 0 -> 21844 bytes .../lecture02/jl_u4mnMm/optimizing_16_1.png | Bin 21942 -> 0 bytes .../lecture02/jl_u4mnMm/optimizing_17_1.png | Bin 20791 -> 0 bytes _weave/lecture02/optimizing/index.html | 108 +++--- _weave/lecture03/jl_V4oHyq/sciml_35_1.png | Bin 0 -> 26059 bytes .../{jl_gD21qX => jl_V4oHyq}/sciml_36_1.png | Bin .../{jl_gD21qX => jl_V4oHyq}/sciml_37_1.png | Bin _weave/lecture03/jl_V4oHyq/sciml_41_1.png | Bin 0 -> 30601 bytes .../{jl_gD21qX => jl_V4oHyq}/sciml_42_1.png | Bin _weave/lecture03/jl_V4oHyq/sciml_45_1.png | Bin 0 -> 31279 bytes _weave/lecture03/jl_gD21qX/sciml_35_1.png | Bin 27445 -> 0 bytes _weave/lecture03/jl_gD21qX/sciml_41_1.png | Bin 29981 -> 0 bytes _weave/lecture03/jl_gD21qX/sciml_45_1.png | Bin 31347 -> 0 bytes _weave/lecture03/sciml/index.html | 214 ++++++------ _weave/lecture04/dynamical_systems/index.html | 28 +- .../dynamical_systems_11_1.png | Bin .../lecture05/parallelism_overview/index.html | 324 +++++++++--------- .../styles_of_parallelism/index.html | 104 +++--- .../discretizing_odes_12_1.png | Bin .../discretizing_odes_13_1.png | Bin .../discretizing_odes_14_1.png | Bin .../discretizing_odes_15_1.png | Bin .../discretizing_odes_16_1.png | Bin .../discretizing_odes_19_1.png | Bin .../discretizing_odes_20_1.png | Bin .../discretizing_odes_7_1.png | Bin .../discretizing_odes_8_1.png | Bin .../discretizing_odes_9_1.png | Bin .../lecture08/automatic_differentiation.jmd | 4 +- .../automatic_differentiation/index.html | 26 +- .../probabilistic_programming_3_1.png | Bin 22224 -> 0 bytes .../probabilistic_programming_4_1.png | Bin 18237 -> 0 bytes .../probabilistic_programming_5_1.png | Bin 13633 -> 0 bytes .../probabilistic_programming_6_1.png | Bin 6202 -> 0 bytes .../probabilistic_programming_7_1.png | Bin 19593 -> 0 bytes .../probabilistic_programming_8_1.png | Bin 21032 -> 0 bytes .../probabilistic_programming_10_1.png | Bin .../probabilistic_programming_1_1.png | Bin .../probabilistic_programming_3_1.png | Bin 0 -> 19952 bytes .../probabilistic_programming_4_1.png | Bin 0 -> 17706 bytes .../probabilistic_programming_5_1.png | Bin 0 -> 12894 bytes .../probabilistic_programming_6_1.png | Bin 0 -> 5873 bytes .../probabilistic_programming_7_1.png | Bin 0 -> 19335 bytes .../probabilistic_programming_8_1.png | Bin 0 -> 21363 bytes .../probabilistic_programming_9_1.png | Bin .../probabilistic_programming/index.html | 12 +- .../lecture17/global_sensitivity/index.html | 2 +- .../jl_TDBscy/global_sensitivity_2_1.png | Bin 33377 -> 0 bytes .../jl_l1lcWF/global_sensitivity_2_1.png | Bin 0 -> 33238 bytes .../code_profiling_5_1.png | Bin .../uncertainty_programming_1_1.png | Bin notes/02-Optimizing_Serial_Code/index.html | 108 +++--- .../index.html | 214 ++++++------ .../index.html | 28 +- .../index.html | 324 +++++++++--------- .../index.html | 104 +++--- .../index.html | 26 +- .../index.html | 12 +- .../17-Global_Sensitivity_Analysis/index.html | 2 +- 60 files changed, 820 insertions(+), 820 deletions(-) create mode 100644 _weave/lecture02/jl_r4SbWN/optimizing_16_1.png create mode 100644 _weave/lecture02/jl_r4SbWN/optimizing_17_1.png delete mode 100644 _weave/lecture02/jl_u4mnMm/optimizing_16_1.png delete mode 100644 _weave/lecture02/jl_u4mnMm/optimizing_17_1.png create mode 100644 _weave/lecture03/jl_V4oHyq/sciml_35_1.png rename _weave/lecture03/{jl_gD21qX => jl_V4oHyq}/sciml_36_1.png (100%) rename _weave/lecture03/{jl_gD21qX => jl_V4oHyq}/sciml_37_1.png (100%) create mode 100644 _weave/lecture03/jl_V4oHyq/sciml_41_1.png rename _weave/lecture03/{jl_gD21qX => jl_V4oHyq}/sciml_42_1.png (100%) create mode 100644 _weave/lecture03/jl_V4oHyq/sciml_45_1.png delete mode 100644 _weave/lecture03/jl_gD21qX/sciml_35_1.png delete mode 100644 _weave/lecture03/jl_gD21qX/sciml_41_1.png delete mode 100644 _weave/lecture03/jl_gD21qX/sciml_45_1.png rename _weave/lecture04/{jl_lshY2p => jl_fP27tE}/dynamical_systems_11_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_12_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_13_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_14_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_15_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_16_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_19_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_20_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_7_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_8_1.png (100%) rename _weave/lecture07/{jl_hCDNAD => jl_77KvXW}/discretizing_odes_9_1.png (100%) delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_3_1.png delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_4_1.png delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_5_1.png delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_6_1.png delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_7_1.png delete mode 100644 _weave/lecture16/jl_AHYinz/probabilistic_programming_8_1.png rename _weave/lecture16/{jl_AHYinz => jl_GcpKod}/probabilistic_programming_10_1.png (100%) rename _weave/lecture16/{jl_AHYinz => jl_GcpKod}/probabilistic_programming_1_1.png (100%) create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_3_1.png create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_4_1.png create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_5_1.png create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_6_1.png create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_7_1.png create mode 100644 _weave/lecture16/jl_GcpKod/probabilistic_programming_8_1.png rename _weave/lecture16/{jl_AHYinz => jl_GcpKod}/probabilistic_programming_9_1.png (100%) delete mode 100644 _weave/lecture17/jl_TDBscy/global_sensitivity_2_1.png create mode 100644 _weave/lecture17/jl_l1lcWF/global_sensitivity_2_1.png rename _weave/lecture18/{jl_OKpj4T => jl_bQVm2t}/code_profiling_5_1.png (100%) rename _weave/lecture19/{jl_gJdXoT => jl_1YHT0C}/uncertainty_programming_1_1.png (100%) diff --git a/_weave/lecture02/jl_r4SbWN/optimizing_16_1.png b/_weave/lecture02/jl_r4SbWN/optimizing_16_1.png new file mode 100644 index 0000000000000000000000000000000000000000..750cfd6936d42f730a434d53f5c18222acad932f GIT binary patch literal 20670 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture02/jl_r4SbWN/optimizing_17_1.png b/_weave/lecture02/jl_r4SbWN/optimizing_17_1.png new file mode 100644 index 0000000000000000000000000000000000000000..86d5284bf59e611a4cc7cda104f36c731c3f605f GIT binary patch literal 21844 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 QFu4%(;{?+Z6Vk1J0lB7SN&o-= literal 0 HcmV?d00001 diff --git a/_weave/lecture02/jl_u4mnMm/optimizing_16_1.png b/_weave/lecture02/jl_u4mnMm/optimizing_16_1.png deleted file mode 100644 index f8e3f5568b2fc4a61f2279571c78f934bfd8c614..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21942 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 diff --git a/_weave/lecture02/optimizing/index.html b/_weave/lecture02/optimizing/index.html index d01fea5c..c2ff69a8 100644 --- a/_weave/lecture02/optimizing/index.html +++ b/_weave/lecture02/optimizing/index.html @@ -10,7 +10,7 @@

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_rows!(C,A,B)
-7.654 μs (0 allocations: 0 bytes)
+7.587 μs (0 allocations: 0 bytes)
 
 function inner_cols!(C,A,B)
   for j in 1:100, i in 1:100
@@ -19,7 +19,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_cols!(C,A,B)
-2.414 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 

Lower Level View: The Stack and the Heap

Locally, the stack is composed of a stack and a heap. The stack requires a static allocation: it is ordered. Because it's ordered, it is very clear where things are in the stack, and therefore accesses are very quick (think instantaneous). However, because this is static, it requires that the size of the variables is known at compile time (to determine all of the variable locations). Since that is not possible with all variables, there exists the heap. The heap is essentially a stack of pointers to objects in memory. When heap variables are needed, their values are pulled up the cache chain and accessed.

Heap Allocations and Speed

Heap allocations are costly because they involve this pointer indirection, so stack allocation should be done when sensible (it's not helpful for really large arrays, but for small values like scalars it's essential!)

 function inner_alloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -29,7 +29,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_alloc!(C,A,B)
-234.700 μs (10000 allocations: 625.00 KiB)
+234.498 μs (10000 allocations: 625.00 KiB)
 
 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -39,7 +39,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_noalloc!(C,A,B)
-2.413 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 

Why does the array here get heap-allocated? It isn't able to prove/guarantee at compile-time that the array's size will always be a given value, and thus it allocates it to the heap. @btime tells us this allocation occurred and shows us the total heap memory that was taken. Meanwhile, the size of a Float64 number is known at compile-time (64-bits), and so this is stored onto the stack and given a specific location that the compiler will be able to directly address.

Note that one can use the StaticArrays.jl library to get statically-sized arrays and thus arrays which are stack-allocated:

 using StaticArrays
 function static_inner_alloc!(C,A,B)
@@ -50,7 +50,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime static_inner_alloc!(C,A,B)
-2.411 μs (0 allocations: 0 bytes)
+2.388 μs (0 allocations: 0 bytes)
 

Mutation to Avoid Heap Allocations

Many times you do need to write into an array, so how can you write into an array without performing a heap allocation? The answer is mutation. Mutation is changing the values of an already existing array. In that case, no free memory has to be found to put the array (and no memory has to be freed by the garbage collector).

In Julia, functions which mutate the first value are conventionally noted by a !. See the difference between these two equivalent functions:

 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -60,7 +60,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_noalloc!(C,A,B)
-2.413 μs (0 allocations: 0 bytes)
+2.385 μs (0 allocations: 0 bytes)
 
 function inner_alloc(A,B)
   C = similar(A)
@@ -71,7 +71,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_alloc(A,B)
-6.807 μs (2 allocations: 78.17 KiB)
+6.803 μs (2 allocations: 78.17 KiB)
 

To use this algorithm effectively, the ! algorithm assumes that the caller already has allocated the output array to put as the output argument. If that is not true, then one would need to manually allocate. The goal of that interface is to give the caller control over the allocations to allow them to manually reduce the total number of heap allocations and thus increase the speed.

Julia's Broadcasting Mechanism

Wouldn't it be nice to not have to write the loop there? In many high level languages this is simply called vectorization. In Julia, we will call it array vectorization to distinguish it from the SIMD vectorization which is common in lower level languages like C, Fortran, and Julia.

In Julia, if you use . on an operator it will transform it to the broadcasted form. Broadcast is lazy: it will build up an entire .'d expression and then call broadcast! on composed expression. This is customizable and documented in detail. However, to a first approximation we can think of the broadcast mechanism as a mechanism for building fused expressions. For example, the Julia code:

 A .+ B .+ C;
 

under the hood lowers to something like:

@@ -86,29 +86,29 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime unfused(A,B,C);
-6.622 μs (4 allocations: 156.34 KiB)
+9.294 μs (4 allocations: 156.34 KiB)
 
 fused(A,B,C) = A .+ B .+ C
 @btime fused(A,B,C);
 
-4.630 μs (2 allocations: 78.17 KiB)
+4.433 μs (2 allocations: 78.17 KiB)
 

Note that we can also fuse the output by using .=. This is essentially the vectorized version of a ! function:

 D = similar(A)
 fused!(D,A,B,C) = (D .= A .+ B .+ C)
 @btime fused!(D,A,B,C);
 
-3.436 μs (0 allocations: 0 bytes)
+3.441 μs (0 allocations: 0 bytes)
 

Note on Broadcasting Function Calls

Julia allows for broadcasting the call () operator as well. .() will call the function element-wise on all arguments, so sin.(A) will be the elementwise sine function. This will fuse Julia like the other operators.

Note on Vectorization and Speed

In articles on MATLAB, Python, R, etc., this is where you will be told to vectorize your code. Notice from above that this isn't a performance difference between writing loops and using vectorized broadcasts. This is not abnormal! The reason why you are told to vectorize code in these other languages is because they have a high per-operation overhead (which will be discussed further down). This means that every call, like +, is costly in these languages. To get around this issue and make the language usable, someone wrote and compiled the loop for the C/Fortran function that does the broadcasted form (see numpy's Github repo). Thus A .+ B's MATLAB/Python/R equivalents are calling a single C function to generally avoid the cost of function calls and thus are faster.

But this is not an intrinsic property of vectorization. Vectorization isn't "fast" in these languages, it's just close to the correct speed. The reason vectorization is recommended is because looping is slow in these languages. Because looping isn't slow in Julia (or C, C++, Fortran, etc.), loops and vectorization generally have the same speed. So use the one that works best for your code without a care about performance.

(As a small side effect, these high level languages tend to allocate a lot of temporary variables since the individual C kernels are written for specific numbers of inputs and thus don't naturally fuse. Julia's broadcast mechanism is just generating and JIT compiling Julia functions on the fly, and thus it can accommodate the combinatorial explosion in the amount of choices just by only compiling the combinations that are necessary for a specific code)

Heap Allocations from Slicing

It's important to note that slices in Julia produce copies instead of views. Thus for example:

 A[50,50]
 
-0.17778218519466293
+0.4378374168708473
 

allocates a new output. This is for safety, since if it pointed to the same array then writing to it would change the original array. We can demonstrate this by asking for a view instead of a copy.

 @show A[1]
 E = @view A[1:5,1:5]
 E[1] = 2.0
 @show A[1]
 
-A[1] = 0.5530951950733606
+A[1] = 0.37233258859606955
 A[1] = 2.0
 2.0
 

However, this means that @view A[1:5,1:5] did not allocate an array (it does allocate a pointer if the escape analysis is unable to prove that it can be elided. This means that in small loops there will be no allocation, while if the view is returned from a function for example it will allocate the pointer, ~80 bytes, but not the memory of the array. This means that it is O(1) in cost but with a relatively small constant).

Asymptotic Cost of Heap Allocations

Heap allocations have to locate and prepare a space in RAM that is proportional to the amount of memory that is calculated, which means that the cost of a heap allocation for an array is O(n), with a large constant. As RAM begins to fill up, this cost dramatically increases. If you run out of RAM, your computer may begin to use swap, which is essentially RAM simulated on your hard drive. Generally when you hit swap your performance is so dead that you may think that your computation froze, but if you check your resource use you will notice that it's actually just filled the RAM and starting to use the swap.

But think of it as O(n) with a large constant factor. This means that for operations which only touch the data once, heap allocations can dominate the computational cost:

@@ -130,7 +130,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe plot(ns,alloc,label="=",xscale=:log10,yscale=:log10,legend=:bottomright, title="Micro-optimizations matter for BLAS1") plot!(ns,noalloc,label=".=") -

However, when the computation takes O(n^3), like in matrix multiplications, the high constant factor only comes into play when the matrices are sufficiently small:

+

However, when the computation takes O(n^3), like in matrix multiplications, the high constant factor only comes into play when the matrices are sufficiently small:

 using LinearAlgebra, BenchmarkTools
 function alloc_timer(n)
     A = rand(n,n)
@@ -149,7 +149,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe plot(ns,alloc,label="*",xscale=:log10,yscale=:log10,legend=:bottomright, title="Micro-optimizations only matter for small matmuls") plot!(ns,noalloc,label="mul!") -

Though using a mutating form is never bad and always is a little bit better.

Optimizing Memory Use Summary

  • Avoid cache misses by reusing values

  • Iterate along columns

  • Avoid heap allocations in inner loops

  • Heap allocations occur when the size of things is not proven at compile-time

  • Use fused broadcasts (with mutated outputs) to avoid heap allocations

  • Array vectorization confers no special benefit in Julia because Julia loops are as fast as C or Fortran

  • Use views instead of slices when applicable

  • Avoiding heap allocations is most necessary for O(n) algorithms or algorithms with small arrays

  • Use StaticArrays.jl to avoid heap allocations of small arrays in inner loops

Julia's Type Inference and the Compiler

Many people think Julia is fast because it is JIT compiled. That is simply not true (we've already shown examples where Julia code isn't fast, but it's always JIT compiled!). Instead, the reason why Julia is fast is because the combination of two ideas:

  • Type inference

  • Type specialization in functions

These two features naturally give rise to Julia's core design feature: multiple dispatch. Let's break down these pieces.

Type Inference

At the core level of the computer, everything has a type. Some languages are more explicit about said types, while others try to hide the types from the user. A type tells the compiler how to to store and interpret the memory of a value. For example, if the compiled code knows that the value in the register is supposed to be interpreted as a 64-bit floating point number, then it understands that slab of memory like:

Importantly, it will know what to do for function calls. If the code tells it to add two floating point numbers, it will send them as inputs to the Floating Point Unit (FPU) which will give the output.

If the types are not known, then... ? So one cannot actually compute until the types are known, since otherwise it's impossible to interpret the memory. In languages like C, the programmer has to declare the types of variables in the program:

void add(double *a, double *b, double *c, size_t n){
+

Though using a mutating form is never bad and always is a little bit better.

Optimizing Memory Use Summary

  • Avoid cache misses by reusing values

  • Iterate along columns

  • Avoid heap allocations in inner loops

  • Heap allocations occur when the size of things is not proven at compile-time

  • Use fused broadcasts (with mutated outputs) to avoid heap allocations

  • Array vectorization confers no special benefit in Julia because Julia loops are as fast as C or Fortran

  • Use views instead of slices when applicable

  • Avoiding heap allocations is most necessary for O(n) algorithms or algorithms with small arrays

  • Use StaticArrays.jl to avoid heap allocations of small arrays in inner loops

Julia's Type Inference and the Compiler

Many people think Julia is fast because it is JIT compiled. That is simply not true (we've already shown examples where Julia code isn't fast, but it's always JIT compiled!). Instead, the reason why Julia is fast is because the combination of two ideas:

  • Type inference

  • Type specialization in functions

These two features naturally give rise to Julia's core design feature: multiple dispatch. Let's break down these pieces.

Type Inference

At the core level of the computer, everything has a type. Some languages are more explicit about said types, while others try to hide the types from the user. A type tells the compiler how to to store and interpret the memory of a value. For example, if the compiled code knows that the value in the register is supposed to be interpreted as a 64-bit floating point number, then it understands that slab of memory like:

Importantly, it will know what to do for function calls. If the code tells it to add two floating point numbers, it will send them as inputs to the Floating Point Unit (FPU) which will give the output.

If the types are not known, then... ? So one cannot actually compute until the types are known, since otherwise it's impossible to interpret the memory. In languages like C, the programmer has to declare the types of variables in the program:

void add(double *a, double *b, double *c, size_t n){
   size_t i;
   for(i = 0; i < n; ++i) {
     c[i] = a[i] + b[i];
@@ -172,7 +172,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define i64 @julia_f_3082(i64 signext %0, i64 signext %1) #0 {
+define i64 @julia_f_3070(i64 signext %0, i64 signext %1) #0 {
 top:
 ; ┌ @ int.jl:87 within `+`
    %2 = add i64 %1, %0
@@ -184,7 +184,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define double @julia_f_3084(double %0, double %1) #0 {
+define double @julia_f_3072(double %0, double %1) #0 {
 top:
 ; ┌ @ float.jl:409 within `+`
    %2 = fadd double %0, %1
@@ -204,7 +204,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define i64 @julia_g_3086(i64 signext %0, i64 signext %1) #0 {
+define i64 @julia_g_3074(i64 signext %0, i64 signext %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 6 within `g`
@@ -249,7 +249,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define double @julia_f_3545(double %0, i64 signext %1) #0 {
+define double @julia_f_3533(double %0, i64 signext %1) #0 {
 top:
 ; ┌ @ promotion.jl:422 within `+`
 ; │┌ @ promotion.jl:393 within `promote`
@@ -290,7 +290,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define double @julia_g_3548(double %0, i64 signext %1) #0 {
+define double @julia_g_3536(double %0, i64 signext %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 5 within `g`
@@ -381,7 +381,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `ff`
-define double @julia_ff_3667(double %0, i64 signext %1) #0 {
+define double @julia_ff_3655(double %0, i64 signext %1) #0 {
 top:
 ; ┌ @ promotion.jl:422 within `+`
 ; │┌ @ promotion.jl:393 within `promote`
@@ -537,7 +537,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define void @julia_g_3806([2 x double]* noalias nocapture noundef nonnull s
+define void @julia_g_3794([2 x double]* noalias nocapture noundef nonnull s
 ret([2 x double]) align 8 dereferenceable(16) %0, [2 x double]* nocapture n
 oundef nonnull readonly align 8 dereferenceable(16) %1, [2 x double]* nocap
 ture noundef nonnull readonly align 8 dereferenceable(16) %2) #0 {
@@ -657,7 +657,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define [2 x float] @julia_g_3818([2 x float]* nocapture noundef nonnull rea
+define [2 x float] @julia_g_3806([2 x float]* nocapture noundef nonnull rea
 donly align 4 dereferenceable(8) %0, [2 x float]* nocapture noundef nonnull
  readonly align 4 dereferenceable(8) %1) #0 {
 top:
@@ -763,7 +763,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define void @julia_g_3837([2 x {}*]* noalias nocapture noundef nonnull sret
+define void @julia_g_3825([2 x {}*]* noalias nocapture noundef nonnull sret
 ([2 x {}*]) align 8 dereferenceable(16) %0, [2 x {}*]* nocapture noundef no
 nnull readonly align 8 dereferenceable(16) %1, [2 x {}*]* nocapture noundef
  nonnull readonly align 8 dereferenceable(16) %2) #0 {
@@ -796,25 +796,25 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe store {}** %14, {}*** %13, align 8 %15 = bitcast {}*** %tls_pgcstack to {}*** store {}** %gcframe2.sub, {}*** %15, align 8 - call void @"j_+_3839"([2 x {}*]* noalias nocapture noundef nonnull sret( -[2 x {}*]) %9, [2 x {}*]* nocapture nonnull readonly %1, i64 signext 4) + call void @"j_+_3827"([2 x {}*]* noalias nocapture noundef nonnull sret( +[2 x {}*]) %7, [2 x {}*]* nocapture nonnull readonly %1, i64 signext 4) ; └ ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd: 6 within `g` ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd :2 within `f` - call void @"j_+_3840"([2 x {}*]* noalias nocapture noundef nonnull sret( -[2 x {}*]) %5, i64 signext 2, [2 x {}*]* nocapture readonly %9) + call void @"j_+_3828"([2 x {}*]* noalias nocapture noundef nonnull sret( +[2 x {}*]) %5, i64 signext 2, [2 x {}*]* nocapture readonly %7) ; └ ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd: 7 within `g` ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd :2 within `f` - call void @"j_+_3841"([2 x {}*]* noalias nocapture noundef nonnull sret( -[2 x {}*]) %7, [2 x {}*]* nocapture readonly %5, [2 x {}*]* nocapture nonnu + call void @"j_+_3829"([2 x {}*]* noalias nocapture noundef nonnull sret( +[2 x {}*]) %9, [2 x {}*]* nocapture readonly %5, [2 x {}*]* nocapture nonnu ll readonly %2) %16 = bitcast [2 x {}*]* %0 to i8* - %17 = bitcast {}** %6 to i8* + %17 = bitcast {}** %8 to i8* call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 derefer enceable(16) %16, i8* noundef nonnull align 16 dereferenceable(16) %17, i64 16, i1 false) @@ -844,28 +844,28 @@

Optimizing Serial Code

Chris Rackauckas
Septe b = MyComplex(2.0,1.0) @btime g(a,b)
-20.439 ns (1 allocation: 32 bytes)
+20.388 ns (1 allocation: 32 bytes)
 MyComplex(9.0, 2.0)
 
 a = MyParameterizedComplex(1.0,1.0)
 b = MyParameterizedComplex(2.0,1.0)
 @btime g(a,b)
 
-21.304 ns (1 allocation: 32 bytes)
+21.142 ns (1 allocation: 32 bytes)
 MyParameterizedComplex{Float64}(9.0, 2.0)
 
 a = MySlowComplex(1.0,1.0)
 b = MySlowComplex(2.0,1.0)
 @btime g(a,b)
 
-121.043 ns (5 allocations: 96 bytes)
+113.240 ns (5 allocations: 96 bytes)
 MySlowComplex(9.0, 2.0)
 
 a = MySlowComplex2(1.0,1.0)
 b = MySlowComplex2(2.0,1.0)
 @btime g(a,b)
 
-853.314 ns (14 allocations: 288 bytes)
+519.620 ns (14 allocations: 288 bytes)
 MySlowComplex2(9.0, 2.0)
 

Note on Julia

Note that, because of these type specialization, value types, etc. properties, the number types, even ones such as Int, Float64, and Complex, are all themselves implemented in pure Julia! Thus even basic pieces can be implemented in Julia with full performance, given one uses the features correctly.

Note on isbits

Note that a type which is mutable struct will not be isbits. This means that mutable structs will be a pointer to a heap allocated object, unless it's shortlived and the compiler can erase its construction. Also, note that isbits compiles down to bit operations from pure Julia, which means that these types can directly compile to GPU kernels through CUDAnative without modification.

Function Barriers

Since functions automatically specialize on their input types in Julia, we can use this to our advantage in order to make an inner loop fully inferred. For example, take the code from above but with a loop:

 function r(x)
@@ -880,7 +880,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime r(x)
-5.756 μs (300 allocations: 4.69 KiB)
+5.878 μs (300 allocations: 4.69 KiB)
 604.0
 

In here, the loop variables are not inferred and thus this is really slow. However, we can force a function call in the middle to end up with specialization and in the inner loop be stable:

 s(x) = _s(x[1],x[2])
@@ -896,7 +896,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe end @btime s(x)
-299.899 ns (1 allocation: 16 bytes)
+296.107 ns (1 allocation: 16 bytes)
 604.0
 

Notice that this algorithm still doesn't infer:

 @code_warntype s(x)
@@ -928,7 +928,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `fff`
-define i64 @julia_fff_3931(i64 signext %0) #0 {
+define i64 @julia_fff_3919(i64 signext %0) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 8 within `fff`
@@ -942,7 +942,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `fff`
-define double @julia_fff_3933(double %0) #0 {
+define double @julia_fff_3921(double %0) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 8 within `fff`
@@ -957,7 +957,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe C[i,j] = A[i,j] + B[i,j] end
-769.551 μs (30000 allocations: 468.75 KiB)
+792.921 μs (30000 allocations: 468.75 KiB)
 

This is very slow because the types of A, B, and C cannot be inferred. Why can't they be inferred? Well, at any time in the dynamic REPL scope I can do something like C = "haha now a string!", and thus it cannot specialize on the types currently existing in the REPL (since asynchronous changes could also occur), and therefore it defaults back to doing a type check at every single function which slows it down. Moral of the story, Julia functions are fast but its global scope is too dynamic to be optimized.

Summary

  • Julia is not fast because of its JIT, it's fast because of function specialization and type inference

  • Type stable functions allow inference to fully occur

  • Multiple dispatch works within the function specialization mechanism to create overhead-free compile time controls

  • Julia will specialize the generic functions

  • Making sure values are concretely typed in inner loops is essential for performance

Overheads of Individual Operations

Now let's dig even a little deeper. Everything the processor does has a cost. A great chart to keep in mind is this classic one. A few things should immediately jump out to you:

  • Simple arithmetic, like floating point additions, are super cheap. ~1 clock cycle, or a few nanoseconds.

  • Processors do branch prediction on if statements. If the code goes down the predicted route, the if statement costs ~1-2 clock cycles. If it goes down the wrong route, then it will take ~10-20 clock cycles. This means that predictable branches, like ones with clear patterns or usually the same output, are much cheaper (almost free) than unpredictable branches.

  • Function calls are expensive: 15-60 clock cycles!

  • RAM reads are very expensive, with lower caches less expensive.

Bounds Checking

Let's check the LLVM IR on one of our earlier loops:

 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -969,7 +969,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `inner_noalloc!`
-define nonnull {}* @"japi1_inner_noalloc!_3942"({}* %function, {}** noalias
+define nonnull {}* @"japi1_inner_noalloc!_3930"({}* %function, {}** noalias
  nocapture noundef readonly %args, i32 %nargs) #0 {
 top:
   %stackargs = alloca {}**, align 8
@@ -1997,7 +1997,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe ; â”” ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd: 6 within `inner_noalloc!` - ret {}* inttoptr (i64 139788390752264 to {}*) + ret {}* inttoptr (i64 139763090710536 to {}*) oob: ; preds = %L5.us.us.us.po stloop, %L2.split.us.split, %L2.split.us.split.us.split, %L5.us.us.us.postl @@ -2098,17 +2098,17 @@

Optimizing Serial Code

Chris Rackauckas
Septe end @btime inner_noalloc!(C,A,B)
-2.411 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 
 @btime inner_noalloc_ib!(C,A,B)
 
-2.347 μs (0 allocations: 0 bytes)
+2.342 μs (0 allocations: 0 bytes)
 

SIMD

Now let's inspect the LLVM IR again:

 @code_llvm inner_noalloc_ib!(C,A,B)
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `inner_noalloc_ib!`
-define nonnull {}* @"japi1_inner_noalloc_ib!_3978"({}* %function, {}** noal
+define nonnull {}* @"japi1_inner_noalloc_ib!_3966"({}* %function, {}** noal
 ias nocapture noundef readonly %args, i32 %nargs) #0 {
 top:
   %stackargs = alloca {}**, align 8
@@ -4013,7 +4013,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe br i1 %.not.not24, label %L36, label %L2 L36: ; preds = %L25 - ret {}* inttoptr (i64 139788390752264 to {}*) + ret {}* inttoptr (i64 139763090710536 to {}*) }

If you look closely, you will see things like:

%wide.load24 = load <4 x double>, <4 x double> addrspac(13)* %46, align 8
 ; â””
@@ -4022,7 +4022,7 @@ 

Optimizing Serial Code

Chris Rackauckas
Septe @code_llvm fma(2.0,5.0,3.0)
 ;  @ floatfuncs.jl:439 within `fma`
-define double @julia_fma_3979(double %0, double %1, double %2) #0 {
+define double @julia_fma_3967(double %0, double %1, double %2) #0 {
 common.ret:
 ; ┌ @ floatfuncs.jl:434 within `fma_llvm`
    %3 = call double @llvm.fma.f64(double %0, double %1, double %2)
@@ -4070,7 +4070,7 @@ 

Inlining

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 4 within `qinline`
-define double @julia_qinline_3982(double %0, double %1) #0 {
+define double @julia_qinline_3970(double %0, double %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 7 within `qinline`
@@ -4107,17 +4107,17 @@ 

Inlining

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 11 within `qnoinline`
-define double @julia_qnoinline_3984(double %0, double %1) #0 {
+define double @julia_qnoinline_3972(double %0, double %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 14 within `qnoinline`
-  %2 = call double @j_fnoinline_3986(double %0, i64 signext 4)
+  %2 = call double @j_fnoinline_3974(double %0, i64 signext 4)
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 15 within `qnoinline`
-  %3 = call double @j_fnoinline_3987(i64 signext 2, double %2)
+  %3 = call double @j_fnoinline_3975(i64 signext 2, double %2)
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 16 within `qnoinline`
-  %4 = call double @j_fnoinline_3988(double %3, double %1)
+  %4 = call double @j_fnoinline_3976(double %3, double %1)
   ret double %4
 }
 
@@ -4136,7 +4136,7 @@

Inlining

-21.605 ns (1 allocation: 16 bytes)
+20.982 ns (1 allocation: 16 bytes)
 9.0
 
@@ -4148,7 +4148,7 @@

Inlining

-25.319 ns (1 allocation: 16 bytes)
+25.017 ns (1 allocation: 16 bytes)
 9.0
 
@@ -4193,7 +4193,7 @@

Note on Benchmarking

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `cheat`
-define double @julia_cheat_4016() #0 {
+define double @julia_cheat_4004() #0 {
 top:
   ret double 9.000000e+00
 }
diff --git a/_weave/lecture03/jl_V4oHyq/sciml_35_1.png b/_weave/lecture03/jl_V4oHyq/sciml_35_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..9805a40c302333b9eea0f14fe0412c31cdf725ec
GIT binary patch
literal 26059
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≦$B-i)vwbwFefq2GV8a&qbVvc#
z88b&mDjFIN(n!hEztYULK+XkLxQ!?m7i^II0YChAdz(VYg_>{oUCs(1wnQF2l#`Vm
z9~(Ob57o4$kEQ$CCnG>*2@|MxTT(67(qdPeun+SyLZ2VjPRsMr^rC|
z=N?S*@WA0ea#WISTwEFJ9L?(P*aG5s();RMvw)WDZT*2*x5
zc>TRwQ8Z`=+gLI>x{`}#NF6Y-uw-Rq!rJB)J}|Gm<08`e}J%gXYd1b6Z=_r5`klp-cT~wg($98v@YiFkGVYO|hJ*
zk&*B2qKdArF3b$f8Ze9K>MqUCH;Xyh**#WKS=c>-nb@-ITAY1o?D$5XdzLk31Cr9P8tTx7U$)eEiS*M6eu_;
zaJ2%bzk@>oA|p}w8ADpXZ4mSgVcEHanSa(|10X1$MJvv<*wqkqXJ=;}op{(+KW2J*
zY@H5!8Do5UL>|P4MOor_4TwV%2jwF&AwQ#}qJm)UuawLJgBI}6EXocQ>ib^WL5hok
zF@gTw{tf=ckiJZL|36t8AykAfUl;;yhRZjaK*#9vl_Hnf9lZ18|2gaeKiHa0a#zYE
Q{_-;wWL0H~5oSUE1MI+;ssI20

literal 0
HcmV?d00001

diff --git a/_weave/lecture03/jl_gD21qX/sciml_36_1.png b/_weave/lecture03/jl_V4oHyq/sciml_36_1.png
similarity index 100%
rename from _weave/lecture03/jl_gD21qX/sciml_36_1.png
rename to _weave/lecture03/jl_V4oHyq/sciml_36_1.png
diff --git a/_weave/lecture03/jl_gD21qX/sciml_37_1.png b/_weave/lecture03/jl_V4oHyq/sciml_37_1.png
similarity index 100%
rename from _weave/lecture03/jl_gD21qX/sciml_37_1.png
rename to _weave/lecture03/jl_V4oHyq/sciml_37_1.png
diff --git a/_weave/lecture03/jl_V4oHyq/sciml_41_1.png b/_weave/lecture03/jl_V4oHyq/sciml_41_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f591aadd862d84bf86bdc005093e5b4ee4f3a9d
GIT binary patch
literal 30601
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

_T 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�I93vcym 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture03/jl_gD21qX/sciml_42_1.png b/_weave/lecture03/jl_V4oHyq/sciml_42_1.png similarity index 100% rename from _weave/lecture03/jl_gD21qX/sciml_42_1.png rename to _weave/lecture03/jl_V4oHyq/sciml_42_1.png diff --git a/_weave/lecture03/jl_V4oHyq/sciml_45_1.png b/_weave/lecture03/jl_V4oHyq/sciml_45_1.png new file mode 100644 index 0000000000000000000000000000000000000000..a6fdff40ad3e36b643f08674b9abf078d762bcf1 GIT binary patch literal 31279 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture03/jl_gD21qX/sciml_35_1.png b/_weave/lecture03/jl_gD21qX/sciml_35_1.png deleted file mode 100644 index 10e54333d6c81e5ba4f783d0cf4ee797944b4b99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27445 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

7(tnADT_lB-1v%}%00H>UYQU5 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 j|02)-H2(U^nL#aw}Rxg diff --git a/_weave/lecture03/jl_gD21qX/sciml_41_1.png b/_weave/lecture03/jl_gD21qX/sciml_41_1.png deleted file mode 100644 index 0a05926cb9e6807fc280cd0ba6e759ef4d4c16cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29981 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∾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 Z1pe8RGF?WT8GJ#AlAM}sne^Ka{|imx%jEz7 diff --git a/_weave/lecture03/jl_gD21qX/sciml_45_1.png b/_weave/lecture03/jl_gD21qX/sciml_45_1.png deleted file mode 100644 index ad20ad21999fa78baee474808439761dae515a9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31347 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 EA5;t)(*OVf diff --git a/_weave/lecture03/sciml/index.html b/_weave/lecture03/sciml/index.html index defb266e..0dc0722a 100644 --- a/_weave/lecture03/sciml/index.html +++ b/_weave/lecture03/sciml/index.html @@ -19,11 +19,11 @@

Introduction to Scientific Machine Learning through Physics-Inf simpleNN(rand(10))

 5-element Vector{Float64}:
-  6.191491803887638
- 10.961350935082876
- -0.086794697902561
-  4.329029452777256
- -0.4819728343992912
+ -1.1205287975184102
+ -6.95758884176028
+  4.900880949623628
+  3.7834125354726122
+  4.191471397681439
 

This is our direct definition of a neural network. Notice that we choose to use tanh as our activation function between the layers.

Defining Neural Networks with Flux.jl

One of the main deep learning libraries in Julia is Flux.jl. Flux is an interesting library for scientific machine learning because it is built on top of language-wide automatic differentiation libraries, giving rise to a programming paradigm known as differentiable programming, which means that one can write a program in a manner that it has easily accessible fast derivatives. However, due to being built on a differentiable programming base, the underlying functionality is simply standard Julia code,

To learn how to use the library, consult the documentation. A Google search will bring up the Flux.jl Github repository. From there, the blue link on the README brings you to the package documentation. This is common through Julia so it's a good habit to learn!

In the documentation you will find that the way a neural network is defined is through a Chain of layers. A Dense layer is the kind we defined above, which is given by an input size, an output size, and an activation function. For example, the following recreates the neural network that we had above:

 using Flux
 NN2 = Chain(Dense(10 => 32,tanh),
@@ -32,11 +32,11 @@ 

Introduction to Scientific Machine Learning through Physics-Inf NN2(rand(10))

 5-element Vector{Float32}:
- -0.40341762
- -0.32073504
- -0.60663885
-  0.6273459
-  0.3418348
+ -0.41757214
+  0.3268494
+  0.6551256
+  0.3942203
+  0.5987754
 

Notice that Flux.jl as a library is written in pure Julia, which means that every piece of this syntax is just sugar over some Julia code that we can specialize ourselves (this is the advantage of having a language fast enough for the implementation of the library and the use of the library!)

For example, the activation function is just a scalar Julia function. If we wanted to replace it by something like the quadratic function, we can just use an anonymous function to define the scalar function we would like to use:

 NN3 = Chain(Dense(10 => 32,x->x^2),
             Dense(32 => 32,x->max(0,x)),
@@ -44,11 +44,11 @@ 

Introduction to Scientific Machine Learning through Physics-Inf NN3(rand(10))

 5-element Vector{Float32}:
- -0.18383282
- -0.030633058
- -0.021082453
- -0.08232622
- -0.22971418
+ -0.02532567
+ -0.14441662
+ -0.051778786
+ -0.38974416
+ -0.10805663
 

The second activation function there is what's known as a relu. A relu can be good to use because it's an exceptionally fast operation and satisfies a form of the universal approximation theorem (UAT). However, a downside is that its derivative is not continuous, which could impact the numerical properties of some algorithms, and thus it's widely used throughout standard machine learning but we'll see reasons why it may be disadvantageous in some cases in scientific machine learning.

Digging into the Construction of a Neural Network Library

Again, as mentioned before, this neural network NN2 is simply a function:

 simpleNN(x) = W[3]*tanh.(W[2]*tanh.(W[1]*x + b[1]) + b[2]) + b[3]
 
@@ -96,26 +96,26 @@ 

Introduction to Scientific Machine Learning through Physics-Inf denselayer_f(rand(32))

 32-element Vector{Float32}:
-  0.015360817
- -0.46573928
-  0.84118056
-  0.6889729
- -0.04318807
- -0.486555
-  0.7456628
- -0.1116474
- -0.8562411
-  0.14605747
+ -0.5618108
+ -0.6442508
+  0.4827489
+ -0.2672533
+ -0.87355
+ -0.030600643
+  0.333129
+ -0.16809873
+ -0.03701947
+  0.38146716
   â‹®
-  0.07789154
-  0.35291886
-  0.27881867
- -0.68542886
- -0.3935992
-  0.657173
- -0.18403019
-  0.063687466
- -0.15492688
+ -0.39834422
+  0.8219398
+ -0.55398756
+ -0.031825528
+  0.17183039
+  0.72180593
+ -0.7143836
+  0.49238148
+ -0.7401128
 

So okay, Dense objects are just functions that have weight and bias matrices inside of them. Now what does Chain do?

 @which Chain(1,2,3)
 
Chain(xs...) in Flux at /home/runner/.julia/packages/Flux/Wz6D4/src/layers/basic.jl:39

Again, for our explanations here we will look at the slightly simpler code From and earlier version of the Flux package:

@@ -169,52 +169,52 @@ 

Introduction to Scientific Machine Learning through Physics-Inf loss() = sum(abs2,sum(abs2,NN(rand(10)).-1) for i in 1:100) loss()

-4269.761f0
+2630.6902f0
 

This loss function takes 100 random points in $[0,1]^{10}$ and then computes the output of the neural network minus 1 on each of the values, and sums up the squared values (abs2). Why the squared values? This means that every computed loss value is positive, and so we know that by decreasing the loss this means that, on average our neural network outputs are closer to 1. What are the weights? Since we're using the Flux callable struct style from above, the weights are those inside of the NN chain object, which we can inspect:

 NN[1].weight # The W matrix of the first layer
 
 32×10 Matrix{Float32}:
-  0.210141   -0.0318616   0.0161381  …   0.320312   -0.331868    0.187002
- -0.102589    0.208266   -0.18678        0.259299   -0.0850212   0.325602
-  0.0954246   0.29503     0.155045      -0.13743    -0.226485   -0.352321
-  0.094165    0.147697   -0.0402125      0.376543   -0.0652325  -0.0910839
- -0.0960281  -0.239555    0.0736242      0.179233    0.0747602  -0.231401
-  0.346408   -0.229251    0.247542   …  -0.227708   -0.342039   -0.3638
- -0.0874939  -0.146548    0.162742      -0.194633   -0.280423   -0.374489
-  0.340297    0.264646    0.097446       0.312401   -0.212878    0.307074
-  0.365767   -0.366916    0.261565      -0.0138854  -0.105345   -0.265025
-  0.228151    0.247341   -0.135668       0.247047    0.140386   -0.19907
-  ⋮                                  ⋱                          
-  0.186276    0.115302   -0.303067      -0.0957572  -0.202029    0.199585
- -0.0958866   0.139531    0.182455      -0.0669233  -0.190238    0.280298
- -0.108114    0.258266    0.052903   …   0.191436   -0.12086    -0.186751
-  0.2797     -0.0260807   0.256827       0.363836   -0.308047    0.104556
- -0.132173   -0.372458    0.122993       0.102172   -0.186959    0.195341
- -0.0277494  -0.15691     0.231842      -0.35223    -0.130247   -0.256816
- -0.309665   -0.313657   -0.345432       0.338976    0.302093    0.373044
- -0.368304    0.298401   -0.0528614  …  -0.196604    0.361721    0.234842
-  0.147451    0.324855   -0.175287       0.0836575   0.331776   -0.0320324
+ -0.0912051   0.171404   -0.225056   …   0.26575    -0.2033       0.19557
+ -0.023353   -0.193015    0.33744        0.135803    0.305452     0.330386
+ -0.21381     0.222574    0.146357       0.302459   -0.204567     0.177094
+ -0.236288   -0.0437021   0.149399      -0.322455    0.0413544    0.376834
+  0.0779307  -0.363982   -0.256043       0.150865    0.310071     0.141492
+  0.310811   -0.0969612  -0.1377     …   0.377674   -0.372038     0.237749
+  0.155738   -0.272288    0.148163      -0.175247    0.0568673   -0.302354
+ -0.338095    0.375844   -0.0522764     -0.0950616   0.241941     0.177569
+ -0.313516    0.0510053  -0.283776      -0.262828    0.127621     0.0381291
+ -0.238924   -0.019392   -0.345777       0.123743   -0.171073     0.286924
+  ⋮                                  ⋱                           
+  0.361331   -0.300791    0.340877       0.051346    0.250142     0.236606
+  0.256587   -0.144472   -0.250453       0.0694984  -0.00493134   0.242588
+  0.321232    0.133965    0.080042   …   0.0232834  -0.357839     0.175813
+  0.29538     0.0922859   0.246678      -0.102874   -0.237856    -0.340453
+  0.344922    0.306977    0.155636      -0.132782    0.127812    -0.0488943
+ -0.0161293  -0.25159    -0.220966       0.190733    0.0313222   -0.100159
+  0.0903141   0.251727    0.341801       0.127054   -0.243489    -0.0498583
+  0.0220798  -0.314075   -0.0345888  …  -0.0257494   0.109001    -0.0628161
+ -0.321938    0.144522   -0.0387606      0.17195     0.200624    -0.272704
 

Now let's grab all of the parameters together:

 p = Flux.params(NN)
 
-Params([Float32[0.21014117 -0.031861562 … -0.33186755 0.18700212; -0.102589
-46 0.20826595 … -0.0850212 0.32560205; … ; -0.36830446 0.29840133 … 0.36172
-083 0.23484248; 0.14745119 0.3248552 … 0.33177575 -0.03203242], Float32[0.0
-, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 0.0, 
-0.0, 0.0, 0.0, 0.0, 0.0], Float32[-0.18695994 -0.04698782 … -0.055100985 -0
-.1341884; 0.0074982448 0.11674296 … -0.22039233 -0.05518888; … ; -0.0007238
-363 0.2559794 … -0.29302862 -0.30272353; 0.087237954 0.087170936 … 0.295242
-6 -0.19100738], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …
-  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.029854774 -0
-.06820957 … 0.09176909 -0.11000199; 0.2896224 0.2912958 … 0.034562368 -0.17
-458537; … ; 0.07649842 0.26569244 … -0.07407499 0.37595725; -0.28656915 0.0
-42809594 … -0.24949594 -0.33653733], Float32[0.0, 0.0, 0.0, 0.0, 0.0]])
+Params([Float32[-0.091205075 0.17140365 … -0.20329987 0.19557032; -0.023352
+979 -0.19301474 … 0.3054521 0.33038554; … ; 0.022079762 -0.31407467 … 0.109
+00141 -0.062816136; -0.3219381 0.14452209 … 0.20062439 -0.27270395], Float3
+2[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 
+0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.019839598 -0.2168316 … -0.2577663 
+0.30027536; -0.19628644 0.07071809 … -0.117501654 0.20883556; … ; 0.0368188
+55 -0.012596854 … -0.24249776 -0.19441248; 0.2561112 -0.19482055 … -0.30460
+06 -0.06886764], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  
+…  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[-0.12173528 -
+0.3812579 … -0.3371982 -0.27681643; 0.33429277 0.32999685 … -0.3093447 -0.2
+4638811; … ; 0.38407874 -0.25517553 … -0.011652403 0.027645448; -0.18082629
+ 0.22756484 … 0.22212335 0.053659596], Float32[0.0, 0.0, 0.0, 0.0, 0.0]])
 

That's a helper function on Chain which recursively gathers all of the defining parameters. Let's now find the optimal values p which cause the neural network to be the constant 1 function:

 Flux.train!(loss, p, Iterators.repeated((), 10000), ADAM(0.1))
 

Now let's check the loss:

 loss()
 
-3.377496f-5
+3.805218f-5
 

This means that NN(x) is now a very good function approximator to f(x) = ones(5)!

So Why Machine Learning? Why Neural Networks?

All we did was find parameters that made NN(x) act like a function f(x). How does that relate to machine learning? Well, in any case where one is acting on data (x,y), the idea is to assume that there exists some underlying mathematical model f(x) = y. If we had perfect knowledge of what f is, then from only the information of x we can then predict what y would be. The inference problem is to then figure out what function f should be. Therefore, machine learning on data is simply this problem of finding an approximator to some unknown function!

So why neural networks? Neural networks satisfy two properties. The first of which is known as the Universal Approximation Theorem (UAT), which in simple non-mathematical language means that, for any ϵ of accuracy, if your neural network is large enough (has enough layers, the weight matrices are large enough), then it can approximate any (nice) function f within that ϵ. Therefore, we can reduce the problem of finding missing functions, the problem of machine learning, to a problem of finding the weights of neural networks, which is a well-defined mathematical optimization problem.

Why neural networks specifically? That's a fairly good question, since there are many other functions with this property. For example, you will have learned from analysis that $a_0 + a_1 x + a_2 x^2 + \ldots$ arbitrary polynomials can be used to approximate any analytic function (this is the Taylor series). Similarly, a Fourier series

\[ f(x) = a_0 + \sum_k b_k \cos(kx) + c_k \sin(kx) \]

can approximate any continuous function f (and discontinuous functions also can have convergence, etc. these are the details of a harmonic analysis course).

That's all for one dimension. How about two dimensional functions? It turns out it's not difficult to prove that tensor products of universal approximators will give higher dimensional universal approximators. So for example, tensoring together two polynomials:

\[ a_0 + a_1 x + a_2 y + a_3 x y + a_4 x^2 y + a_5 x y^2 + a_6 x^2 y^2 + \ldots \]

will give a two-dimensional function approximator. But notice how we have to resolve every combination of terms. This means that if we used n coefficients in each dimension d, the total number of coefficients to build a d-dimensional universal approximator from one-dimensional objects would need $n^d$ coefficients. This exponential growth is known as the curse of dimensionality.

The second property of neural networks that makes them applicable to machine learning is that they overcome the curse of dimensionality. The proofs in this area can be a little difficult to parse, but what they boil down to is proving in many cases that the growth of neural networks to sufficiently approximate a d-dimensional function grows as a polynomial of d, rather than exponential. This means that there's some dimensional cutoff where for $d>cutoff$ it is more efficient to use a neural network. This can be problem-specific, but generally it tends to be the case at least by 8 or 10 dimensions.

Neural networks have a few other properties to consider as well:

  1. The assumptions of the neural network can be encoded into the neural architectures. A neural network where the last layer has an activation function x->x^2 is a neural network where all outputs are positive. This means that if you want to find a positive function, you can make the optimization easier by enforcing this constraint. A lot of other constraints can be enforced, like tanh activation functions can make the neural network be a smooth (all derivatives finite) function, or other activations can cause finite numbers of learnable discontinuities.

  2. Generating higher dimensional forms from one dimensional forms does not have good symmetry. For example, the two-dimensional tensor Fourier basis does not have a good way to represent $sin(xy)$. This property of the approximator is called (non)isotropy and more detail can be found in this wonderful talk about function approximation for multidimensional integration (cubature). Neural networks are naturally not aligned to a basis.

  3. Neural networks are "easy" to compute. There's good software for them, GPU-acceleration, and all other kinds of tooling that make them particularly simple to use.

  4. There are proofs that in many scenarios for neural networks the local minima are the global minima, meaning that local optimization is sufficient for training a neural network. Global optimization (which we will cover later in the course) is much more expensive than local methods like gradient descent, and thus this can be a good property to abuse for faster computation.

From Machine Learning to Scientific Machine Learning: Structure and Science

This understanding of a neural network and their libraries directly bridges to the understanding of scientific machine learning and the computation done in the field. In scientific machine learning, neural networks and machine learning are used as the basis to solve problems in scientific computing. Scientific computing, as a discipline also known as Computational Science, is a field of study which focuses on scientific simulation, using tools such as differential equations to investigate physical, biological, and other phenomena.

What we wish to do in scientific machine learning is use these properties of neural networks to improve the way that we investigate our scientific models.

Aside: Why Differential Equations?

Why do differential equations come up so often in as the model in the scientific context? This is a deep question with quite a simple answer. Essentially, all scientific experiments always have to test how things change. For example, you take a system now, you change it, and your measurement is how the changes you made caused changes in the system. This boils down to gather information about how, for some arbitrary system $y = f(x)$, how $\Delta x$ is related to $\Delta y$. Thus what you learn from scientific experiments, what is codified as scientific laws, is not "the answer", but the answer to how things change. This process of writing down equations by describing how they change precisely gives differential equations.

Solving ODEs with Neural Networks: The Physics-Informed Neural Network

Now let's get to our first true SciML application: solving ordinary differential equations with neural networks. The process of solving a differential equation with a neural network, or using a differential equation as a regularizer in the loss function, is known as a physics-informed neural network, since this allows for physical equations to guide the training of the neural network in circumstances where data might be lacking.

Background: A Method for Solving Ordinary Differential Equations with Neural Networks

This is a result first due to Lagaris et. al from 1998. The idea is to solve differential equations using neural networks by representing the solution by a neural network and training the resulting network to satisfy the conditions required by the differential equation.

Let's say we want to solve a system of ordinary differential equations

\[ u' = f(u,t) \]

with $t \in [0,1]$ and a known initial condition $u(0)=u_0$. To solve this, we approximate the solution by a neural network:

\[ NN(t) \approx u(t) \]

If $NN(t)$ was the true solution, then it would hold that $NN'(t) = f(NN(t),t)$ for all $t$. Thus we turn this condition into our loss function. This motivates the loss function:

\[ L(p) = \sum_i \left(\frac{dNN(t_i)}{dt} - f(NN(t_i),t_i) \right)^2 \]

The choice of $t_i$ could be done in many ways: it can be random, it can be a grid, etc. Anyways, when this loss function is minimized (gradients computed with standard reverse-mode automatic differentiation), then we have that $\frac{dNN(t_i)}{dt} \approx f(NN(t_i),t_i)$ and thus $NN(t)$ approximately solves the differential equation.

Note that we still have to handle the initial condition. One simple way to do this is to add an initial condition term to the cost function. This would look like:

\[ L(p) = (NN(0) - u_0)^2 + \sum_i \left(\frac{dNN(t_i)}{dt} - f(NN(t_i),t_i) \right)^2 \]

While that would work, it can be more efficient to encode the initial condition into the function itself so that it's trivially satisfied for any possible set of parameters. For example, instead of directly using a neural network, we can use:

\[ g(t) = u_0 + tNN(t) \]

as our solution. Notice that $g(t)$ is thus a universal approximator for all continuous functions such that $g(0)=u_0$ (this is a property one should prove!). Since $g(t)$ will always satisfy the initial condition, we can train $g(t)$ to satisfy the derivative function then it will automatically be a solution to the derivative function. In this sense, we can use the loss function:

\[ L(p) = \sum_i \left(\frac{dg(t_i)}{dt} - f(g(t_i),t_i) \right)^2 \]

where $p$ are the parameters that define $g$, which in turn are the parameters which define the neural network $NN$ that define $g$. Thus this reduces down, once again, to simply finding weights which minimize a loss function!

Coding Up the Method

Now let's implement this method with Flux. Let's define a neural network to be the NN(t) above. To make the problem easier, let's look at the ODE:

\[ u' = \cos 2\pi t \]

and approximate it with the neural network from a scalar to a scalar:

 using Flux
 NNODE = Chain(x -> [x], # Take in a scalar and transform it into an array
@@ -223,7 +223,7 @@ 

Introduction to Scientific Machine Learning through Physics-Inf first) # Take first value, i.e. return a scalar NNODE(1.0)

-0.10085204f0
+-0.23149511f0
 

Instead of directly approximating the neural network, we will use the transformed equation that is forced to satisfy the boundary conditions. Using u0=1.0, we have the function:

 g(t) = t*NNODE(t) + 1f0
 
@@ -247,23 +247,23 @@ 

Introduction to Scientific Machine Learning through Physics-Inf display(loss()) Flux.train!(loss, Flux.params(NNODE), data, opt; cb=cb)

-0.5089291687042273
-0.4660090972283474
-0.37798210626977025
-0.18461971004026578
-0.04371968207719241
-0.019536930111555596
-0.014619263249474197
-0.012650337532635803
-0.011729013939159596
-0.011283318492406378
-0.010946978192004615
+0.5708075887278983
+0.4843225444558577
+0.431599836626712
+0.26459017110776495
+0.05310078897817342
+0.011030989241838655
+0.007069401923025258
+0.00634987145212996
+0.00601922323173909
+0.0057577987424845736
+0.005500448268481607
 

How well did this do? Well if we take the integral of both sides of our differential equation, we see it's fairly trivial:

\[ \int g' = g = \int \cos 2\pi t = C + \frac{\sin 2\pi t}{2\pi} \]

where we defined $C = 1$. Let's take a bunch of (input,output) pairs from the neural network and plot it against the analytical solution to the differential equation:

 using Plots
 t = 0:0.001:1.0
 plot(t,g.(t),label="NN")
 plot!(t,1.0 .+ sin.(2Ï€.*t)/2Ï€, label = "True Solution")
-

We see that it matches very well, and we can keep improving this fit by increasing the size of the neural network, using more training points, and training for more iterations.

Example: Harmonic Oscillator Informed Training

Using this idea, differential equations encoding physical laws can be utilized inside of loss functions for terms which we have some basis to believe should approximately follow some physical system. Let's investigate this last step by looking at how to inform the training of a neural network using the harmonic oscillator.

Let's assume that we are taking measurements of (position,force) in some real one-dimensional spring pushing and pulling against a wall.

But instead of the simple spring, let's assume we had a more complex spring, for example, let's say $F(x) = -kx + 0.1sin(x)$ where this extra term is due to some deformities in the metal (assume mass=1). Then by Newton's law of motion we have a second order ordinary differential equation:

\[ x'' = -kx + 0.1 \sin(x) \]

We can use the DifferentialEquations.jl package to solve this differential equation and see what this system looks like:

+

We see that it matches very well, and we can keep improving this fit by increasing the size of the neural network, using more training points, and training for more iterations.

Example: Harmonic Oscillator Informed Training

Using this idea, differential equations encoding physical laws can be utilized inside of loss functions for terms which we have some basis to believe should approximately follow some physical system. Let's investigate this last step by looking at how to inform the training of a neural network using the harmonic oscillator.

Let's assume that we are taking measurements of (position,force) in some real one-dimensional spring pushing and pulling against a wall.

But instead of the simple spring, let's assume we had a more complex spring, for example, let's say $F(x) = -kx + 0.1sin(x)$ where this extra term is due to some deformities in the metal (assume mass=1). Then by Newton's law of motion we have a second order ordinary differential equation:

\[ x'' = -kx + 0.1 \sin(x) \]

We can use the DifferentialEquations.jl package to solve this differential equation and see what this system looks like:

 using DifferentialEquations
 k = 1.0
 force(dx,x,k,t) = -k*x + 0.1sin(x)
@@ -300,7 +300,7 @@ 

Introduction to Scientific Machine Learning through Physics-Inf loss() = sum(abs2,NNForce(position_data[i]) - force_data[i] for i in 1:length(position_data)) loss()

-0.0021075816164434196
+0.001639101475417397
 

Our random parameters do not do so well, so let's train!

 opt = Flux.Descent(0.01)
 data = Iterators.repeated((), 5000)
@@ -314,24 +314,24 @@ 

Introduction to Scientific Machine Learning through Physics-Inf display(loss()) Flux.train!(loss, Flux.params(NNForce), data, opt; cb=cb)

-0.0021075816164434196
-0.0016626665734058707
-0.0014129151050661442
-0.0012004879578227366
-0.001019683666213778
-0.00086574214000748
-0.0007346664655364099
-0.0006230813332454321
-0.0005281206353467606
-0.0004473482765489638
-0.00037868342158744984
+0.001639101475417397
+0.001281078565346881
+0.001078228101263855
+0.000907144491383387
+0.000762834587654969
+0.0006411230663627027
+0.0005385050286698408
+0.00045202585782455135
+0.0003791885175632164
+0.00031788120953395697
+0.0002663162119004075
 

The neural network almost exactly matched the dataset, but how well did it actually learn the real force function? Let's plot it to see:

 learned_force_plot = NNForce.(positions_plot)
 
 plot(plot_t,force_plot,xlabel="t",label="True Force")
 plot!(plot_t,learned_force_plot,label="Predicted Force")
 scatter!(t,force_data,label="Force Measurements")
-

Ouch. The problem is that a neural network can approximate any function, so it approximated a function that fits the data, but not the correct function. We somehow need to have more data... but where can we get more data?

Well, even a first year undergrad in physics will know Hooke's law, which is that the idealized spring should satisfy $F(x) = -kx$. This is a decent assumption for the evolution of the system:

+

Ouch. The problem is that a neural network can approximate any function, so it approximated a function that fits the data, but not the correct function. We somehow need to have more data... but where can we get more data?

Well, even a first year undergrad in physics will know Hooke's law, which is that the idealized spring should satisfy $F(x) = -kx$. This is a decent assumption for the evolution of the system:

 force2(dx,x,k,t) = -k*x
 prob_simplified = SecondOrderODEProblem(force2,1.0,0.0,(0.0,10.0),k)
 sol_simplified = solve(prob_simplified)
@@ -342,7 +342,7 @@ 

Introduction to Scientific Machine Learning through Physics-Inf loss_ode() = sum(abs2,NNForce(x) - (-k*x) for x in random_positions) loss_ode()

-10.993295820275433
+8.460564225345507
 

If this term is zero, then $F(x) = -kx$, which is approximately true. So now let's put these together:

 λ = 0.1
 composed_loss() = loss() + λ*loss_ode()
@@ -367,15 +367,15 @@ 

Introduction to Scientific Machine Learning through Physics-Inf plot!(plot_t,learned_force_plot,label="Predicted Force") scatter!(t,force_data,label="Force Measurements")

-1.0997082654491308
-0.0005850661060708156
-0.0005522938565740613
-0.0005232864079531569
-0.0004973263251085195
-0.00047389858231739466
-0.000452611856433718
-0.0004331665468480585
-0.00041531905615030537
-0.00039886843159506777
-0.000383654997635387
-

And there we go: we have used knowledge of physics to help inform our neural network training process!

Conclusion

In this lecture we motivated machine learning not as a process of predicting from data but as a process for learning arbitrary nonlinear functions. Neural networks were just one choice of possible function. We then demonstrated how differential equations could be solved using this function approximation technique and then put together these two domains, solving differential equations and approximating data, into a single process to allow for physical knowledge to be embedded into the training process of a neural network, thus arriving at a physics-informed neural network. This is just one method in scientific machine learning which we will be exploring in more detail, demonstrating how we can utilize scientific knowledge to improve fits and allow for data-efficient machine learning.

\ No newline at end of file +0.8463227387464513 +0.000210408027752647 +0.00020519506792570903 +0.00020026862808211442 +0.00019559928791649267 +0.00019116211020453466 +0.00018693271539265902 +0.00018289622008194647 +0.00017903277909909004 +0.00017533236251209575 +0.0001717819891828354 +

And there we go: we have used knowledge of physics to help inform our neural network training process!

Conclusion

In this lecture we motivated machine learning not as a process of predicting from data but as a process for learning arbitrary nonlinear functions. Neural networks were just one choice of possible function. We then demonstrated how differential equations could be solved using this function approximation technique and then put together these two domains, solving differential equations and approximating data, into a single process to allow for physical knowledge to be embedded into the training process of a neural network, thus arriving at a physics-informed neural network. This is just one method in scientific machine learning which we will be exploring in more detail, demonstrating how we can utilize scientific knowledge to improve fits and allow for data-efficient machine learning.

\ No newline at end of file diff --git a/_weave/lecture04/dynamical_systems/index.html b/_weave/lecture04/dynamical_systems/index.html index 71ca50c7..e5fe7753 100644 --- a/_weave/lecture04/dynamical_systems/index.html +++ b/_weave/lecture04/dynamical_systems/index.html @@ -113,7 +113,7 @@

How Iteration Works, An Introduction to Discrete Dynamics

end @time solve_system_save(lorenz,[1.0,0.0,0.0],p,1000)
-0.000050 seconds (1.00 k allocations: 86.062 KiB)
+0.000054 seconds (1.00 k allocations: 86.062 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -138,7 +138,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

 @time solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-0.012133 seconds (4.50 k allocations: 336.016 KiB, 99.40% compilation tim
+0.011324 seconds (4.50 k allocations: 336.016 KiB, 99.43% compilation tim
 e)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
@@ -164,7 +164,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

The first time Julia compiles the function, and the second is a straight call.

 @time solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-0.000057 seconds (1.01 k allocations: 99.984 KiB)
+0.000056 seconds (1.01 k allocations: 99.984 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -190,7 +190,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

using BenchmarkTools @btime solve_system_save(lorenz,[1.0,0.0,0.0],p,1000)
-38.372 μs (1001 allocations: 86.06 KiB)
+27.531 μs (1001 allocations: 86.06 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -215,7 +215,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

 @btime solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-44.223 μs (1006 allocations: 99.98 KiB)
+33.333 μs (1006 allocations: 99.98 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -248,7 +248,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

end @btime solve_system_save_matrix(lorenz,[1.0,0.0,0.0],p,1000)
-69.190 μs (2001 allocations: 179.66 KiB)
+66.845 μs (2001 allocations: 179.66 KiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -265,7 +265,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

end @btime solve_system_save_matrix_view(lorenz,[1.0,0.0,0.0],p,1000)
-34.535 μs (1002 allocations: 101.61 KiB)
+34.956 μs (1002 allocations: 101.61 KiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -282,7 +282,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

end @btime solve_system_save_matrix_resize(lorenz,[1.0,0.0,0.0],p,1000)
-1.198 ms (2318 allocations: 11.65 MiB)
+1.043 ms (2318 allocations: 11.65 MiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -388,7 +388,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

which would compute f and then take the values of du and update u with them, but that's 3 extra operations than required, whereas u,du = du,u will change u to be a pointer to the updated memory and now du is an "empty" cache array that we can refill (this decreases the computational cost by ~33%). Let's see what the cost is with this newest version:

 @btime solve_system(lorenz,[1.0,0.0,0.0],p,1000)
 
-37.210 μs (1000 allocations: 78.12 KiB)
+25.919 μs (1000 allocations: 78.12 KiB)
 3-element Vector{Float64}:
   1.4744010677851374
   0.8530017039412324
@@ -396,7 +396,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

 @btime solve_system_mutate(lorenz,[1.0,0.0,0.0],p,1000)
 
-7.301 μs (3 allocations: 240 bytes)
+7.291 μs (3 allocations: 240 bytes)
 3-element Vector{Float64}:
   1.4744010677851374
   0.8530017039412324
@@ -445,7 +445,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

 @btime solve_system_save(lorenz,@SVector[1.0,0.0,0.0],p,1000)
 
-8.766 μs (2 allocations: 23.48 KiB)
+8.850 μs (2 allocations: 23.48 KiB)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -511,7 +511,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

 @btime solve_system_save(lorenz,@SVector[1.0,0.0,0.0],p,1000)
 
-6.723 μs (2 allocations: 23.48 KiB)
+6.721 μs (2 allocations: 23.48 KiB)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -536,7 +536,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

And we can get down to non-allocating for the loop:

 @btime solve_system(lorenz,@SVector([1.0,0.0,0.0]),p,1000)
 
-6.502 μs (1 allocation: 32 bytes)
+6.496 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
   1.4744010677851374
   0.8530017039412324
@@ -552,7 +552,7 @@ 

How Iteration Works, An Introduction to Discrete Dynamics

u = Vector{typeof(@SVector([1.0,0.0,0.0]))}(undef,1000) @btime solve_system_save!(u,lorenz,@SVector([1.0,0.0,0.0]),p,1000)
-6.498 μs (0 allocations: 0 bytes)
+6.500 μs (0 allocations: 0 bytes)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
diff --git a/_weave/lecture04/jl_lshY2p/dynamical_systems_11_1.png b/_weave/lecture04/jl_fP27tE/dynamical_systems_11_1.png
similarity index 100%
rename from _weave/lecture04/jl_lshY2p/dynamical_systems_11_1.png
rename to _weave/lecture04/jl_fP27tE/dynamical_systems_11_1.png
diff --git a/_weave/lecture05/parallelism_overview/index.html b/_weave/lecture05/parallelism_overview/index.html
index 2e2ef5b4..38bf7652 100644
--- a/_weave/lecture05/parallelism_overview/index.html
+++ b/_weave/lecture05/parallelism_overview/index.html
@@ -127,7 +127,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac u = [Vector{Float64}(undef,3) for i in 1:1000] @btime solve_system_save_iip!(u,lorenz_mt!,[1.0,0.0,0.0],p,1000);
-1.202 ms (5995 allocations: 608.84 KiB)
+1.203 ms (5995 allocations: 608.84 KiB)
 

Parallelism doesn't always make things faster. There are two costs associated with this code. For one, we had to go to the slower heap+mutation version, so its implementation starting point is slower. But secondly, and more importantly, the cost of spinning a new thread is non-negligible. In fact, here we can see that it even needs to make a small allocation for the new context. The total cost is on the order of It's on the order of 50ns: not huge, but something to take note of. So what we've done is taken almost free calculations and made them ~50ns by making each in a different thread, instead of just having it be one thread with one call stack.

The moral of the story is that you need to make sure that there's enough work per thread in order to effectively accelerate a program with parallelism.

Data-Parallel Problems

So not every setup is amenable to parallelism. Dynamical systems are notorious for being quite difficult to parallelize because the dependency of the future time step on the previous time step is clear, meaning that one cannot easily "parallelize through time" (though it is possible, which we will study later).

However, one common way that these systems are generally parallelized is in their inputs. The following questions allow for independent simulations:

  • What steady state does an input u0 go to for some list/region of initial conditions?

  • How does the solution very when I use a different p?

The problem has a few descriptions. For one, it's called an embarrassingly parallel problem since the problem can remain largely intact to solve the parallelism problem. To solve this, we can use the exact same solve_system_save_iip!, and just change how we are calling it. Secondly, this is called a data parallel problem, since it parallelized by splitting up the input data (here, the possible u0 or ps) and acting on them independently.

Multithreaded Parameter Searches

Now let's multithread our parameter search. Let's say we wanted to compute the mean of the values in the trajectory. For a single input pair, we can compute that like:

 using Statistics
 function compute_trajectory_mean(u0,p)
@@ -137,7 +137,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac end @btime compute_trajectory_mean(@SVector([1.0,0.0,0.0]),p)
-7.647 μs (3 allocations: 23.52 KiB)
+7.612 μs (3 allocations: 23.52 KiB)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -151,7 +151,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac end @btime compute_trajectory_mean2(@SVector([1.0,0.0,0.0]),p)
-7.464 μs (3 allocations: 112 bytes)
+7.469 μs (3 allocations: 112 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -165,7 +165,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac end @btime compute_trajectory_mean3(@SVector([1.0,0.0,0.0]),p)
-7.424 μs (1 allocation: 32 bytes)
+7.421 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -178,7 +178,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac compute_trajectory_mean4(u0,p) = _compute_trajectory_mean4(_u_cache,u0,p) @btime compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p)
-7.429 μs (1 allocation: 32 bytes)
+7.426 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -187,50 +187,50 @@ 

The Basics of Single Node Parallel Computing

Chris Rac ps = [(0.02,10.0,28.0,8/3) .* (1.0,rand(3)...) for i in 1:1000]
 1000-element Vector{NTuple{4, Float64}}:
- (0.02, 9.106049390932355, 3.303812946184277, 2.0289376601917164)
- (0.02, 4.735606575537758, 24.169178529667747, 1.3480009969264561)
- (0.02, 1.8782542650718392, 22.557130006439053, 2.0243816234869705)
- (0.02, 5.302261087000523, 27.07006533567516, 2.655762043438801)
- (0.02, 5.670338250773302, 4.441306677748524, 0.9560932489779355)
- (0.02, 1.4007298019106085, 8.924613440852292, 1.7845149466502728)
- (0.02, 4.210072135010345, 12.885641724584875, 1.4106354338111666)
- (0.02, 6.178913314158388, 14.077198942044106, 2.6422469838602893)
- (0.02, 9.022764003735679, 5.959401451034244, 0.2915953150851287)
- (0.02, 7.637994682331543, 2.280140073955992, 1.3174704247968336)
+ (0.02, 2.0494785010995353, 12.87474091643628, 2.221379168745697)
+ (0.02, 3.0141933992052494, 5.011093405297812, 0.9795778033401599)
+ (0.02, 6.6087512736768526, 22.554785148961784, 0.3707891781853476)
+ (0.02, 0.5160432877281274, 2.1442560677379037, 1.7411393467364724)
+ (0.02, 8.558522987629267, 18.545203958656234, 1.9346962153448815)
+ (0.02, 9.54542624443799, 14.894432021569349, 2.2001967050663778)
+ (0.02, 7.164909712680697, 14.058087153752206, 1.9721359183413034)
+ (0.02, 4.503455496371159, 15.638037179709096, 0.5478848102231814)
+ (0.02, 5.0067292534598415, 23.498092029722358, 1.8321033765018702)
+ (0.02, 2.043018481497864, 24.8343511074286, 0.40227226208229894)
  â‹®
- (0.02, 1.4295983611840468, 22.723025540961917, 0.5603759621647108)
- (0.02, 3.5691263501247104, 11.614453902298626, 0.9865390285286525)
- (0.02, 6.347641844244725, 11.287551194973615, 2.170255707975067)
- (0.02, 4.3184493713301455, 24.303906061742534, 0.09612994921103102)
- (0.02, 6.080688519020651, 7.21028271370196, 0.8896348073525427)
- (0.02, 6.3902301771886725, 13.890419308445491, 0.5118246742927562)
- (0.02, 3.0250459886141687, 16.614712639906436, 2.5380729556522454)
- (0.02, 1.4197562136945086, 12.915115904241887, 2.1974841648464785)
- (0.02, 2.760108862922477, 8.843016809441602, 0.6191363970184215)
+ (0.02, 9.459925043278687, 14.913762836267876, 1.946411348397799)
+ (0.02, 3.7058223528064884, 9.956749354099607, 2.5430008163316744)
+ (0.02, 5.6913112833876225, 12.407156482636111, 1.0651014726178387)
+ (0.02, 0.09131385334597009, 7.6889382933701, 2.0354078697915967)
+ (0.02, 1.1885766367375572, 19.114097281325094, 1.381133123272827)
+ (0.02, 3.9804371550787767, 23.853405760253743, 1.185532990309715)
+ (0.02, 8.84078503958126, 5.847345434942631, 1.0196090739651438)
+ (0.02, 5.582363837550646, 11.995288548626041, 0.041581327093415034)
+ (0.02, 0.643229668934614, 23.346238053688726, 0.22539644325045938)
 

And let's get the mean of the trajectory for each of the parameters.

 serial_out = map(p -> compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p),ps)
 
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Now let's do this with multithreading:

 function tmap(f,ps)
   out = Vector{typeof(@SVector([1.0,0.0,0.0]))}(undef,1000)
@@ -243,26 +243,26 @@ 

The Basics of Single Node Parallel Computing

Chris Rac threaded_out = tmap(p -> compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p),ps)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Let's check the output:

 serial_out - threaded_out
 
@@ -296,7 +296,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac end @btime compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p)
-7.429 μs (1 allocation: 32 bytes)
+7.424 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -330,53 +330,53 @@ 

The Basics of Single Node Parallel Computing

Chris Rac
 @btime serial_out = map(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.420 ms (3 allocations: 23.50 KiB)
+7.419 ms (3 allocations: 23.50 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 
 @btime threaded_out = tmap(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.420 ms (8 allocations: 24.06 KiB)
+7.421 ms (8 allocations: 24.06 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Hierarchical Task-Based Multithreading and Dynamic Scheduling

The major change in Julia v1.3 is that Julia's Tasks, which are traditionally its green threads interface, are now the basis of its multithreading infrastructure. This means that all independent threads are parallelized, and a new interface for multithreading will exist that works by spawning threads.

This implementation follows Go's goroutines and the classic multithreading interface of Cilk. There is a Julia-level scheduler that handles the multithreading to put different tasks on different vCPU threads. A benefit from this is hierarchical multithreading. Since Julia's tasks can spawn tasks, what can happen is a task can create tasks which create tasks which etc. In Julia (/Go/Cilk), this is then seen as a single pool of tasks which it can schedule, and thus it will still make sure only N are running at a time (as opposed to the naive implementation where the total number of running threads is equal then multiplied). This is essential for numerical performance because running multiple compute threads on a single CPU thread requires constant context switching between the threads, which will slow down the computations.

To directly use the task-based interface, simply use Threads.@spawn to spawn new tasks. For example:

 function tmap2(f,ps)
   tasks = [Threads.@spawn f(ps[i]) for i in 1:1000]
@@ -385,51 +385,51 @@ 

The Basics of Single Node Parallel Computing

Chris Rac threaded_out = tmap2(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

However, if we check the timing we see:

 @btime tmap2(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.761 ms (6005 allocations: 562.70 KiB)
+7.741 ms (6005 allocations: 562.70 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Threads.@threads is built on the same multithreading infrastructure, so why is this so much slower? The reason is because Threads.@threads employs static scheduling while Threads.@spawn is using dynamic scheduling. Dynamic scheduling is the model of allowing the runtime to determine the ordering and scheduling of processes, i.e. what tasks will run run where and when. Julia's task-based multithreading system has a thread scheduler which will automatically do this for you in the background, but because this is done at runtime it will have overhead. Static scheduling is the model of pre-determining where and when tasks will run, instead of allowing this to be determined at runtime. Threads.@threads is "quasi-static" in the sense that it cuts the loop so that it spawns only as many tasks as there are threads, essentially assigning one thread for even chunks of the input data.

Does this lack of runtime overhead mean that static scheduling is "better"? No, it simply has trade-offs. Static scheduling assumes that the runtime of each block is the same. For this specific case where there are fixed number of loop iterations for the dynamical systems, we know that every compute_trajectory_mean5 costs exactly the same, and thus this will be more efficient. However, There are many cases where this might not be efficient. For example:

 function sleepmap_static()
   out = Vector{Int}(undef,24)
@@ -448,7 +448,7 @@ 

The Basics of Single Node Parallel Computing

Chris Rac @btime sleepmap_static() @btime sleepmap_spawn()
-30.057 s (104 allocations: 3.75 KiB)
+30.053 s (104 allocations: 3.75 KiB)
   2.401 s (220 allocations: 14.77 KiB)
 24-element Vector{Int64}:
   1
@@ -489,24 +489,24 @@ 

The Basics of Single Node Parallel Computing

Chris Rac A*B
 10000×10000 Matrix{Float64}:
- 2542.68  2521.97  2510.56  2497.9   …  2509.28  2525.0   2523.88  2520.4
- 2509.41  2523.24  2499.23  2466.59     2498.17  2490.19  2504.73  2494.0
- 2527.03  2511.33  2512.35  2475.63     2512.02  2515.73  2511.64  2515.72
- 2530.45  2522.96  2505.87  2477.47     2514.41  2511.84  2483.23  2504.55
- 2528.08  2517.44  2492.39  2481.61     2514.61  2518.98  2501.12  2506.62
- 2513.9   2530.09  2495.53  2463.74  …  2513.33  2488.51  2489.3   2499.18
- 2483.17  2492.63  2484.71  2445.09     2490.3   2491.62  2488.92  2477.49
- 2540.57  2535.27  2522.95  2491.98     2527.58  2505.16  2526.23  2522.58
- 2541.76  2536.15  2523.99  2492.26     2520.01  2514.86  2519.61  2519.08
- 2510.24  2515.49  2519.26  2479.86     2499.94  2503.5   2503.25  2500.0
+ 2525.87  2488.32  2499.41  2509.97  …  2511.13  2489.44  2531.07  2502.22
+ 2519.05  2496.88  2482.01  2506.51     2527.63  2493.75  2516.93  2503.97
+ 2534.89  2503.33  2495.34  2520.59     2529.65  2508.02  2536.28  2522.71
+ 2532.22  2520.62  2515.99  2518.95     2535.94  2524.97  2546.52  2512.56
+ 2514.36  2479.37  2504.63  2489.56     2507.64  2494.65  2523.54  2493.13
+ 2529.33  2500.05  2501.9   2505.24  …  2531.42  2486.38  2515.62  2512.86
+ 2540.09  2497.2   2504.75  2516.27     2521.17  2512.34  2529.63  2512.79
+ 2509.74  2477.49  2488.59  2479.55     2506.92  2485.0   2531.66  2480.12
+ 2481.75  2476.91  2490.11  2465.56     2465.47  2467.87  2509.78  2478.45
+ 2544.86  2496.0   2514.15  2517.92     2519.5   2511.37  2552.28  2527.42
     ⋮                                ⋱                             
- 2525.23  2512.21  2498.54  2488.94     2513.71  2501.97  2511.88  2520.2
- 2511.07  2491.4   2491.45  2465.4      2511.1   2497.52  2478.17  2490.51
- 2502.33  2508.4   2498.52  2465.6      2503.36  2502.37  2492.31  2501.05
- 2521.12  2509.38  2498.31  2474.29     2515.97  2501.83  2512.41  2488.79
- 2490.9   2496.69  2483.61  2441.27  …  2490.72  2471.2   2475.08  2476.2
- 2535.03  2541.2   2529.11  2497.66     2535.53  2517.47  2531.13  2519.93
- 2511.1   2510.73  2493.1   2492.95     2510.81  2499.15  2542.01  2518.67
- 2518.64  2526.74  2517.93  2488.57     2531.73  2504.55  2515.35  2513.5
- 2523.56  2519.73  2500.24  2485.91     2513.05  2535.31  2523.33  2518.76
+ 2527.82  2496.37  2506.14  2490.21     2519.83  2528.13  2550.02  2506.49
+ 2512.87  2490.24  2480.03  2490.32     2526.82  2474.85  2525.26  2493.48
+ 2494.52  2487.08  2483.83  2499.03     2504.63  2476.85  2533.39  2491.2
+ 2531.16  2499.71  2516.22  2520.35     2536.82  2507.74  2517.3   2530.53
+ 2521.62  2481.61  2484.72  2506.89  …  2514.74  2495.46  2525.36  2499.07
+ 2516.2   2491.17  2493.77  2509.39     2502.63  2491.1   2508.44  2497.63
+ 2499.9   2481.76  2506.27  2492.37     2494.85  2491.09  2518.55  2480.2
+ 2527.68  2503.12  2528.21  2511.25     2532.84  2503.63  2558.09  2533.89
+ 2540.76  2513.19  2517.55  2496.81     2522.99  2506.76  2533.4   2518.68
 

If you are using a computer that has N cores, then this will use N cores. Try it and look at your resource usage!

Array-Based Parallelism

The simplest form of parallelism is array-based parallelism. The idea is that you use some construction of an array whose operations are already designed to be parallel under the hood. In Julia, some examples of this are:

  • DistributedArrays (Distributed Computing)

  • Elemental

  • MPIArrays

  • CuArrays (GPUs)

This is not a Julia specific idea either.

BLAS and Standard Libraries

The basic linear algebra calls are all handled by a set of libraries which follow the same interface known as BLAS (Basic Linear Algebra Subroutines). It's divided into 3 portions:

  • BLAS1: Element-wise operations (O(n))

  • BLAS2: Matrix-vector operations (O(n^2))

  • BLAS3: Matrix-matrix operations (O(n^3))

BLAS implementations are highly optimized, like OpenBLAS and Intel MKL, so every numerical language and library essentially uses similar underlying BLAS implementations. Extensions to these, known as LAPACK, include operations like factorizations, and are included in these standard libraries. These are all multithreaded. The reason why this is a location to target is because the operation count is high enough that parallelism can be made efficient even when only targeting this level: a matrix multiplication can take on the order of seconds, minutes, hours, or even days, and these are all highly parallel operations. This means you can get away with a bunch just by parallelizing at this level, which happens to be a bottleneck for a lot scientific computing codes.

This is also commonly the level at which GPU computing occurs in machine learning libraries for reasons which we will explain later.

MPI

Well, this is a big topic and we'll address this one later!

Conclusion

The easiest forms of parallelism are:

  • Embarrassingly parallel

  • Array-level parallelism (built into linear algebra)

Exploit these when possible.

\ No newline at end of file diff --git a/_weave/lecture06/styles_of_parallelism/index.html b/_weave/lecture06/styles_of_parallelism/index.html index 34279380..2c0202b4 100644 --- a/_weave/lecture06/styles_of_parallelism/index.html +++ b/_weave/lecture06/styles_of_parallelism/index.html @@ -9,26 +9,26 @@

The Different Flavors of Parallelism

Chris Rackauckas< arr = [MyComplex(rand(),rand()) for i in 1:100]
 100-element Vector{MyComplex}:
- MyComplex(0.14309417245278067, 0.9215224222944839)
- MyComplex(0.6563768312045752, 0.8044767963545393)
- MyComplex(0.5728049188895872, 0.33765746952116094)
- MyComplex(0.8240073299674354, 0.9470426273216329)
- MyComplex(0.7606061347124898, 0.9327907399185521)
- MyComplex(0.755341833759231, 0.5434958255089687)
- MyComplex(0.9731438085219921, 0.31820007095612113)
- MyComplex(0.6770182257794624, 0.3681202537043393)
- MyComplex(0.08480739105077983, 0.65477879435604)
- MyComplex(0.879044108699869, 0.5694763191704512)
+ MyComplex(0.5948216400563927, 0.7202742856092115)
+ MyComplex(0.8858224485878035, 0.04464426300983426)
+ MyComplex(0.4536724251568013, 0.48301713342468633)
+ MyComplex(0.17632669227127473, 0.966975004988031)
+ MyComplex(0.7423602389573798, 0.7381611563859961)
+ MyComplex(0.5049342978821135, 0.2713721944949681)
+ MyComplex(0.8956475760986714, 0.006535319763830483)
+ MyComplex(0.5275017128612832, 0.6089884504844824)
+ MyComplex(0.22199738442114458, 0.3133842057610531)
+ MyComplex(0.7056746294589479, 0.17085581295301622)
  â‹®
- MyComplex(0.6184831153175321, 0.8420717430705801)
- MyComplex(0.11097401639991189, 0.4733738650181497)
- MyComplex(0.9598982066766498, 0.5024876345986231)
- MyComplex(0.7704581759272878, 0.06431948804402998)
- MyComplex(0.5067467487289342, 0.37276230976872216)
- MyComplex(0.22392490395791997, 0.24997212682983505)
- MyComplex(0.7938100950862472, 0.499794534596235)
- MyComplex(0.5800027114018041, 0.5539306180949976)
- MyComplex(0.8613302068792389, 0.11099259999056299)
+ MyComplex(0.8949805161160401, 0.421323667111285)
+ MyComplex(0.026053733946824753, 0.9006485214268137)
+ MyComplex(0.8300583605559014, 0.8018415810144012)
+ MyComplex(0.12592125655230224, 0.27000881231142315)
+ MyComplex(0.3618587243109601, 0.4880572513222716)
+ MyComplex(0.3150275713704259, 0.13939673381769024)
+ MyComplex(0.03638847026177339, 0.8020351165630957)
+ MyComplex(0.08217419775238222, 0.7933908176556499)
+ MyComplex(0.03792572372188874, 0.1330438491287449)
 

is represented in memory as

[real1,imag1,real2,imag2,...]

while the struct of array formats are

@@ -43,18 +43,18 @@

The Different Flavors of Parallelism

Chris Rackauckas<
-MyComplexes([0.2897241260009822, 0.48104950344076425, 0.1550991673426182, 0
-.4303053146297099, 0.9652545002585547, 0.129691784563555, 0.930721759368184
-5, 0.04236592750770252, 0.12121370563973322, 0.6554310506492863  …  0.73820
-40247731638, 0.057317858458742266, 0.20857864801382386, 0.7618557750226116,
- 0.30777823594243947, 0.19986995680131348, 0.7036870522240618, 0.5790694102
-590798, 0.14664887682564642, 0.44821427291761984], [0.46688590924729956, 0.
-11887230044632568, 0.2806224613334578, 0.43126362776131033, 0.9794620042427
-044, 0.42610522792220007, 0.3077309824326909, 0.6937145539324716, 0.8143594
-272939957, 0.7105773101090823  …  0.24229997305610995, 0.18587298385876627,
- 0.24053515975314443, 0.9719512037832595, 0.03802957417362052, 0.4362822838
-808521, 0.4088166896868569, 0.8960777149330896, 0.5386366912236279, 0.07960
-564919501523])
+MyComplexes([0.4463978501148237, 0.9534146019623043, 0.3272506411204227, 0.
+6610621659220888, 0.7518861360127663, 0.6604957283856057, 0.568257801511073
+4, 0.5249214846587075, 0.28031214215080524, 0.4019397592681623  …  0.230795
+16952268475, 0.4587073529434471, 0.38888294861698836, 0.8764601865880245, 0
+.7664927638985168, 0.5500101294037606, 0.8467864454608346, 0.75150871387712
+67, 0.9504725250659736, 0.7891716988140378], [0.2354669170781668, 0.3484246
+5928318156, 0.4847930258306149, 0.5050470303321312, 0.8805989081762119, 0.3
+6730961426487874, 0.7503851083942035, 0.7838065781271188, 0.833361952697676
+8, 0.4100985361969356  …  0.7241872639287391, 0.2685519569209651, 0.2209582
+6963895615, 0.2074266359413821, 0.6228410415260852, 0.17911567432938458, 0.
+5661141870291473, 0.9051093078571997, 0.37633923990420315, 0.29487469397695
+53])
 
@@ -73,7 +73,7 @@

The Different Flavors of Parallelism

Chris Rackauckas<
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:5 within `average`
-define void @julia_average_10372([2 x double]* noalias nocapture noundef no
+define void @julia_average_10360([2 x double]* noalias nocapture noundef no
 nnull sret([2 x double]) align 8 dereferenceable(16) %0, {}* noundef nonnul
 l align 16 dereferenceable(40) %1) #0 {
 top:
@@ -108,21 +108,21 @@ 

The Different Flavors of Parallelism

Chris Rackauckas< %.sub = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i64 0 ; ││││││││││ @ reduce.jl:432 within `_mapreduce` - store {}* inttoptr (i64 139788167070960 to {}*), {}** %.sub, al + store {}* inttoptr (i64 139762867029232 to {}*), {}** %.sub, al ign 8 %5 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6 4 1 - store {}* inttoptr (i64 139788214797840 to {}*), {}** %5, align + store {}* inttoptr (i64 139762914756112 to {}*), {}** %5, align 8 %6 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6 4 2 store {}* %1, {}** %6, align 8 %7 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6 4 3 - store {}* inttoptr (i64 139788196084624 to {}*), {}** %7, align + store {}* inttoptr (i64 139762896042896 to {}*), {}** %7, align 8 - %8 = call nonnull {}* @ijl_invoke({}* inttoptr (i64 13978817579 -4192 to {}*), {}** nonnull %.sub, i32 4, {}* inttoptr (i64 139788383992288 + %8 = call nonnull {}* @ijl_invoke({}* inttoptr (i64 13976287575 +2464 to {}*), {}** nonnull %.sub, i32 4, {}* inttoptr (i64 139761459797280 to {}*)) call void @llvm.trap() unreachable @@ -233,7 +233,7 @@

The Different Flavors of Parallelism

Chris Rackauckas< ; ││││││││││└ ; ││││││││││ @ reduce.jl:447 within `_mapreduce` ; ││││││││││┌ @ reduce.jl:277 within `mapreduce_impl` - call void @j_mapreduce_impl_10374([2 x double]* noalias nocapt + call void @j_mapreduce_impl_10362([2 x double]* noalias nocapt ure noundef nonnull sret([2 x double]) %tmpcast, {}* nonnull %1, i64 signex t 1, i64 signext %arraylen, i64 signext 1024) ; └└└└└└└└└└└ @@ -583,7 +583,7 @@

Next Level Up: Multithreading

-59.061 μs (6 allocations: 576 bytes)
+59.671 μs (6 allocations: 576 bytes)
 
@@ -596,7 +596,7 @@

Next Level Up: Multithreading

-66.033 μs (6 allocations: 576 bytes)
+66.023 μs (6 allocations: 576 bytes)
 
@@ -609,7 +609,7 @@

Next Level Up: Multithreading

-20.138 μs (6 allocations: 576 bytes)
+20.148 μs (6 allocations: 576 bytes)
 
@@ -637,17 +637,17 @@

Next Level Up: Multithreading

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:26 within `h`
-define void @julia_h_10465() #0 {
+define void @julia_h_10453() #0 {
 top:
-  %.promoted = load i64, i64* inttoptr (i64 139785926445312 to i64*), align
- 256
+  %.promoted = load i64, i64* inttoptr (i64 139761557327088 to i64*), align
+ 16
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:28 within `h`
   %0 = add i64 %.promoted, 10000
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:29 within `h`
 ; ┌ @ Base.jl within `setproperty!`
-   store i64 %0, i64* inttoptr (i64 139785926445312 to i64*), align 256
+   store i64 %0, i64* inttoptr (i64 139761557327088 to i64*), align 16
 ; â””
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:30 within `h`
@@ -673,7 +673,7 @@ 

Next Level Up: Multithreading

-2.785 ns (0 allocations: 0 bytes)
+3.095 ns (0 allocations: 0 bytes)
 
@@ -686,13 +686,13 @@

Next Level Up: Multithreading

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:3 within `h2`
-define void @julia_h2_10472() #0 {
+define void @julia_h2_10460() #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:6 within `h2`
 ; ┌ @ refvalue.jl:59 within `getindex`
 ; │┌ @ Base.jl:37 within `getproperty`
-    %0 = load i64, i64* inttoptr (i64 139787416178784 to i64*), align 32
+    %0 = load i64, i64* inttoptr (i64 139762055925984 to i64*), align 32
 ; └└
 ; ┌ @ range.jl:897 within `iterate`
 ; │┌ @ range.jl:672 within `isempty`
@@ -703,15 +703,15 @@ 

Next Level Up: Multithreading

br i1 %1, label %L34, label %L18.preheader L18.preheader: ; preds = %top - %.promoted = load i64, i64* inttoptr (i64 139785926445312 to i64*), align - 256 + %.promoted = load i64, i64* inttoptr (i64 139761557327088 to i64*), align + 16 ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:8 within `h2` %2 = add i64 %.promoted, %0 ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:7 within `h2` ; ┌ @ Base.jl within `setproperty!` - store i64 %2, i64* inttoptr (i64 139785926445312 to i64*), align 256 + store i64 %2, i64* inttoptr (i64 139761557327088 to i64*), align 16 ; └ ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:8 within `h2` @@ -742,7 +742,7 @@

Next Level Up: Multithreading

-4.939 ns (0 allocations: 0 bytes)
+4.628 ns (0 allocations: 0 bytes)
 
diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_12_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_12_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_12_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_12_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_13_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_13_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_13_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_13_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_14_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_14_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_14_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_14_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_15_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_15_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_15_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_15_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_16_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_16_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_16_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_16_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_19_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_19_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_19_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_19_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_20_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_20_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_20_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_20_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_7_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_7_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_7_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_7_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_8_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_8_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_8_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_8_1.png diff --git a/_weave/lecture07/jl_hCDNAD/discretizing_odes_9_1.png b/_weave/lecture07/jl_77KvXW/discretizing_odes_9_1.png similarity index 100% rename from _weave/lecture07/jl_hCDNAD/discretizing_odes_9_1.png rename to _weave/lecture07/jl_77KvXW/discretizing_odes_9_1.png diff --git a/_weave/lecture08/automatic_differentiation.jmd b/_weave/lecture08/automatic_differentiation.jmd index 5c0c9711..11e24ac5 100644 --- a/_weave/lecture08/automatic_differentiation.jmd +++ b/_weave/lecture08/automatic_differentiation.jmd @@ -585,7 +585,7 @@ $f(d) = f(d_0) + Je_1 \epsilon_1 + \ldots + Je_n \epsilon_n$ computes all columns of the Jacobian simultaneously. -### Array of Structs Representation +### Struct of Arrays Representation Instead of thinking about a vector of dual numbers, thus we can instead think of dual numbers with vectors for the components. But if there are vectors for the @@ -598,7 +598,7 @@ $$\Sigma = \begin{bmatrix} d_{11} & d_{12} & \cdots & d_{1n} \\ d_{21} & d_{22} & & \vdots \\ \vdots & & \ddots & \vdots \\ - d_{m1} & \hdots & \hdots & d_{mn} + d_{m1} & \cdots & \cdots & d_{mn} \end{bmatrix}$$ $$\epsilon=[\epsilon_1,\epsilon_2,\ldots,\epsilon_m]$$ diff --git a/_weave/lecture08/automatic_differentiation/index.html b/_weave/lecture08/automatic_differentiation/index.html index 711534a1..a76a4fda 100644 --- a/_weave/lecture08/automatic_differentiation/index.html +++ b/_weave/lecture08/automatic_differentiation/index.html @@ -18,9 +18,9 @@

Forward-Mode Automatic Differentiation (AD) via High Dimensiona ϵ2 = (1+ϵ) - 1 (ϵ - ϵ2)

-ϵ = 3.873035531141372e-11
-1 + ϵ = 1.0000000000387304
-3.0527602113762817e-18
+ϵ = 6.736227852961307e-11
+1 + ϵ = 1.0000000000673623
+-5.940030844987341e-17
 

See how $\epsilon$ is only rebuilt at accuracy around $10^{-16}$ and thus we only keep around 6 digits of accuracy when it's generated at the size of around $10^{-10}$!

Finite Differencing and Numerical Stability

To start understanding how to compute derivatives on a computer, we start with finite differencing. For finite differencing, recall that the definition of the derivative is:

\[ f'(x) = \lim_{\epsilon \rightarrow 0} \frac{f(x+\epsilon)-f(x)}{\epsilon} \]

Finite differencing directly follows from this definition by choosing a small $\epsilon$. However, choosing a good $\epsilon$ is very difficult. If $\epsilon$ is too large than there is error since this definition is asymptotic. However, if $\epsilon$ is too small, you receive roundoff error. To understand why you would get roundoff error, recall that floating point error is relative, and can essentially store 16 digits of accuracy. So let's say we choose $\epsilon = 10^{-6}$. Then $f(x+\epsilon) - f(x)$ is roughly the same in the first 6 digits, meaning that after the subtraction there is only 10 digits of accuracy, and then dividing by $10^{-6}$ simply brings those 10 digits back up to the correct relative size.

This means that we want to choose $\epsilon$ small enough that the $\mathcal{O}(\epsilon^2)$ error of the truncation is balanced by the $O(1/\epsilon)$ roundoff error. Under some minor assumptions, one can argue that the average best point is $\sqrt(E)$, where E is machine epsilon

 @show eps(Float64)
 @show sqrt(eps(Float64))
@@ -95,17 +95,17 @@ 

Forward-Mode Automatic Differentiation (AD) via High Dimensiona add(a, b) @btime add($(Ref(a))[], $(Ref(b))[])

-3.396 ns (0 allocations: 0 bytes)
+3.105 ns (0 allocations: 0 bytes)
 Dual{Int64}(4, 6)
 

It seems like we have lost no performance.

 @code_native add(1, 2, 3, 4)
 
 .text
 	.file	"add"
-	.globl	julia_add_13194                 # -- Begin function julia_add_13194
+	.globl	julia_add_13182                 # -- Begin function julia_add_13182
 	.p2align	4, 0x90
-	.type	julia_add_13194,@function
-julia_add_13194:                        # @julia_add_13194
+	.type	julia_add_13182,@function
+julia_add_13182:                        # @julia_add_13182
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture08/automatic_diff
 erentiation.jmd:2 within `add`
 # %bb.0:                                # %top
@@ -121,7 +121,7 @@ 

Forward-Mode Automatic Differentiation (AD) via High Dimensiona pop rbp ret .Lfunc_end0: - .size julia_add_13194, .Lfunc_end0-julia_add_13194 + .size julia_add_13182, .Lfunc_end0-julia_add_13182 ; â”” # -- End function .section ".note.GNU-stack","",@progbits @@ -130,10 +130,10 @@

Forward-Mode Automatic Differentiation (AD) via High Dimensiona

 .text
 	.file	"add"
-	.globl	julia_add_13196                 # -- Begin function julia_add_13196
+	.globl	julia_add_13184                 # -- Begin function julia_add_13184
 	.p2align	4, 0x90
-	.type	julia_add_13196,@function
-julia_add_13196:                        # @julia_add_13196
+	.type	julia_add_13184,@function
+julia_add_13184:                        # @julia_add_13184
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture08/automatic_diff
 erentiation.jmd:5 within `add`
 # %bb.0:                                # %top
@@ -152,7 +152,7 @@ 

Forward-Mode Automatic Differentiation (AD) via High Dimensiona pop rbp ret .Lfunc_end0: - .size julia_add_13196, .Lfunc_end0-julia_add_13196 + .size julia_add_13184, .Lfunc_end0-julia_add_13184 ; └└└ # -- End function .section ".note.GNU-stack","",@progbits @@ -274,7 +274,7 @@

Forward-Mode Automatic Differentiation (AD) via High Dimensiona 2-element Vector{Int64}: 6 2 -

Directional derivative and gradient of functions $f: \mathbb{R}^n \to \mathbb{R}$

For a function $f: \mathbb{R}^n \to \mathbb{R}$ the basic operation is the directional derivative:

\[ \lim_{\epsilon \to 0} \frac{f(\mathbf{x} + \epsilon \mathbf{v}) - f(\mathbf{x})}{\epsilon} = [\nabla f(\mathbf{x})] \cdot \mathbf{v}, \]

where $\epsilon$ is still a single dimension and $\nabla f(\mathbf{x})$ is the direction in which we calculate.

We can directly do this using the same simple Dual numbers as above, using the same $\epsilon$, e.g.

\[ f(x, y) = x^2 \sin(y) \]

\[ \begin{align} f(x_0 + a\epsilon, y_0 + b\epsilon) &= (x_0 + a\epsilon)^2 \sin(y_0 + b\epsilon) \\ &= x_0^2 \sin(y_0) + \epsilon[2ax_0 \sin(y_0) + x_0^2 b \cos(y_0)] + o(\epsilon) \end{align} \]

so we have indeed calculated $\nabla f(x_0, y_0) \cdot \mathbf{v},$ where $\mathbf{v} = (a, b)$ are the components that we put into the derivative component of the Dual numbers.

If we wish to calculate the directional derivative in another direction, we could repeat the calculation with a different $\mathbf{v}$. A better solution is to use another independent epsilon $\epsilon$, expanding $x = x_0 + a_1 \epsilon_1 + a_2 \epsilon_2$ and putting $\epsilon_1 \epsilon_2 = 0$.

In particular, if we wish to calculate the gradient itself, $\nabla f(x_0, y_0)$, we need to calculate both partial derivatives, which corresponds to two directional derivatives, in the directions $(1, 0)$ and $(0, 1)$, respectively.

Forward-Mode AD as jvp

Note that another representation of the directional derivative is $f'(x)v$, where $f'(x)$ is the Jacobian or total derivative of $f$ at $x$. To see the equivalence of this to a directional derivative, write it out in the standard basis:

\[ w_i = \sum_{j}^{m} J_{ij} v_{j} \]

Now write out what $J$ means and we see that:

\[ w_i = \sum_j^{m} \frac{df_i}{dx_j} v_j = \nabla f_i(x) \cdot v \]

The primitive action of forward-mode AD is $f'(x)v$!

This is also known as a Jacobian-vector product, or jvp for short.

We can thus represent vector calculus with multidimensional dual numbers as follows. Let $d =[x,y]$, the vector of dual numbers. We can instead represent this as:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 \]

where $d_0$ is the primal vector $[x_0,y_0]$ and the $v_i$ are the vectors for the dual directions. If you work out this algebra, then note that a single application of $f$ to a multidimensional dual number calculates:

\[ f(d) = f(d_0) + f'(d_0)v_1 \epsilon_1 + f'(d_0)v_2 \epsilon_2 \]

i.e. it calculates the result of $f(x,y)$ and two separate directional derivatives. Note that because the information about $f(d_0)$ is shared between the calculations, this is more efficient than doing multiple applications of $f$. And of course, this is then generalized to $m$ many directional derivatives at once by:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 + \ldots + v_m \epsilon_m \]

Jacobian

For a function $f: \mathbb{R}^n \to \mathbb{R}^m$, we reduce (conceptually, although not necessarily in code) to its component functions $f_i: \mathbb{R}^n \to \mathbb{R}$, where $f(x) = (f_1(x), f_2(x), \ldots, f_m(x))$.

Then

\[ \begin{align} f(x + \epsilon v) &= (f_1(x + \epsilon v), \ldots, f_m(x + \epsilon v)) \\ &= (f_1(x) + \epsilon[\nabla f_1(x) \cdot v], \dots, f_m(x) + \epsilon[\nabla f_m(x) \cdot v] \\ &= f(x) + [f'(x) \cdot v] \epsilon, \end{align} \]

To calculate the complete Jacobian, we calculate these directional derivatives in the $n$ different directions of the basis vectors, i.e. if

\[ d = d_0 + e_1 \epsilon_1 + \ldots + e_n \epsilon_n \]

for $e_i$ the $i$th basis vector, then

\[ f(d) = f(d_0) + Je_1 \epsilon_1 + \ldots + Je_n \epsilon_n \]

computes all columns of the Jacobian simultaneously.

Array of Structs Representation

Instead of thinking about a vector of dual numbers, thus we can instead think of dual numbers with vectors for the components. But if there are vectors for the components, then we can think of the grouping of dual components as a matrix. Thus define our multidimensional multi-partial dual number as:

\[ D_0 = [d_1,d_2,d_3,\ldots,d_n] \]

\[ \Sigma = \begin{bmatrix} d_{11} & d_{12} & \cdots & d_{1n} \\ d_{21} & d_{22} & & \vdots \\ \vdots & & \ddots & \vdots \\ d_{m1} & \hdots & \hdots & d_{mn} \end{bmatrix} \]

\[ \epsilon=[\epsilon_1,\epsilon_2,\ldots,\epsilon_m] \]

\[ D = D_0 + \Sigma \epsilon \]

where $D_0$ is a vector in $\mathbb{R}^n$, $\epsilon$ is a vector of dimensional signifiers and $\Sigma$ is a matrix in $\mathbb{R}^{n \times m}$ where $m$ is the number of concurrent differentiation dimensions. Each row of this is a dual number, but now we can use this to easily define higher dimensional primitives.

For example, let $f(x) = Ax$, matrix multiplication. Then, we can show with our dual number arithmetic that:

\[ f(D) = A*D_0 + A*\Sigma*\epsilon \]

is how one would compute the value of $f(D_0)$ and the derivative $f'(D_0)$ in all directions signified by the columns of $\Sigma$ simultaneously. Using multidimensional Taylor series expansions and doing the manipulations like before indeed implies that the arithmetic on this object should follow:

\[ f(D) = f(D_0) + f'(D_0)\Sigma \epsilon \]

where $f'$ is the total derivative or the Jacobian of $f$. This then allows our system to be highly efficient by allowing the definition of multidimensional functions, like linear algebra, to be primitives of multi-directional derivatives.

Higher derivatives

The above techniques can be extended to higher derivatives by adding more terms to the Taylor polynomial, e.g.

\[ f(a + \epsilon) = f(a) + \epsilon f'(a) + \frac{1}{2} \epsilon^2 f''(a) + o(\epsilon^2). \]

We treat this as a degree-2 (or degree-$n$, in general) polynomial and do polynomial arithmetic to calculate the new polynomials. The coefficients of powers of $\epsilon$ then give the higher-order derivatives.

For example, for a function $f: \mathbb{R}^n \to \mathbb{R}$ we have

\[ f(x + \epsilon v) = f(x) + \epsilon \left[ \sum_i (\partial_i f)(x) v_i \right] + \frac{1}{2}\epsilon^2 \left[ \sum_i \sum_j (\partial_{i,j} f) v_i v_j \right] \]

using Dual numbers with a single $\epsilon$ component. In this way we can compute coefficients of the (symmetric) Hessian matrix.

Application: solving nonlinear equations using the Newton method

As an application, we will see how to solve nonlinear equations of the form $f(x) = 0$ for functions $f: \mathbb{R}^n \to \mathbb{R}^n$.

Since in general we cannot do anything with nonlinearity, we try to reduce it (approximate it) with something linear. Furthermore, in general we know that it is not possible to solve nonlinear equations in closed form (even for polynomials of degree $\ge 5$), so we will need some kind of iterative method.

We start from an initial guess $x_0$. The idea of the Newton method is to follow the tangent line to the function $f$ at the point $x_0$ and find where it intersects the $x$-axis; this will give the next iterate $x_1$.

Algebraically, we want to solve $f(x_1) = 0$. Suppose that $x_1 = x_0 + \delta$ for some $\delta$ that is currently unknown and which we wish to calculate.

Assuming $\delta$ is small, we can expand:

\[ f(x_1) = f(x_0 + \delta) = f(x_0) + Df(x_0) \cdot \delta + \mathcal{O}(\| \delta \|^2). \]

Since we wish to solve

\[ f(x_0 + \delta) \simeq 0, \]

we put

\[ f(x_0) + Df(x_0) \cdot \delta = 0, \]

so that mathematically we have

\[ \delta = -[Df(x_0)]^{-1} \cdot f(x_0). \]

Computationally we prefer to solve the matrix equation

\[ J \delta = -f(x_0), \]

where $J := Df(x_0)$ is the Jacobian of the function; Julia uses the syntax \ ("backslash") for solving linear systems in an efficient way:

+

Directional derivative and gradient of functions $f: \mathbb{R}^n \to \mathbb{R}$

For a function $f: \mathbb{R}^n \to \mathbb{R}$ the basic operation is the directional derivative:

\[ \lim_{\epsilon \to 0} \frac{f(\mathbf{x} + \epsilon \mathbf{v}) - f(\mathbf{x})}{\epsilon} = [\nabla f(\mathbf{x})] \cdot \mathbf{v}, \]

where $\epsilon$ is still a single dimension and $\nabla f(\mathbf{x})$ is the direction in which we calculate.

We can directly do this using the same simple Dual numbers as above, using the same $\epsilon$, e.g.

\[ f(x, y) = x^2 \sin(y) \]

\[ \begin{align} f(x_0 + a\epsilon, y_0 + b\epsilon) &= (x_0 + a\epsilon)^2 \sin(y_0 + b\epsilon) \\ &= x_0^2 \sin(y_0) + \epsilon[2ax_0 \sin(y_0) + x_0^2 b \cos(y_0)] + o(\epsilon) \end{align} \]

so we have indeed calculated $\nabla f(x_0, y_0) \cdot \mathbf{v},$ where $\mathbf{v} = (a, b)$ are the components that we put into the derivative component of the Dual numbers.

If we wish to calculate the directional derivative in another direction, we could repeat the calculation with a different $\mathbf{v}$. A better solution is to use another independent epsilon $\epsilon$, expanding $x = x_0 + a_1 \epsilon_1 + a_2 \epsilon_2$ and putting $\epsilon_1 \epsilon_2 = 0$.

In particular, if we wish to calculate the gradient itself, $\nabla f(x_0, y_0)$, we need to calculate both partial derivatives, which corresponds to two directional derivatives, in the directions $(1, 0)$ and $(0, 1)$, respectively.

Forward-Mode AD as jvp

Note that another representation of the directional derivative is $f'(x)v$, where $f'(x)$ is the Jacobian or total derivative of $f$ at $x$. To see the equivalence of this to a directional derivative, write it out in the standard basis:

\[ w_i = \sum_{j}^{m} J_{ij} v_{j} \]

Now write out what $J$ means and we see that:

\[ w_i = \sum_j^{m} \frac{df_i}{dx_j} v_j = \nabla f_i(x) \cdot v \]

The primitive action of forward-mode AD is $f'(x)v$!

This is also known as a Jacobian-vector product, or jvp for short.

We can thus represent vector calculus with multidimensional dual numbers as follows. Let $d =[x,y]$, the vector of dual numbers. We can instead represent this as:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 \]

where $d_0$ is the primal vector $[x_0,y_0]$ and the $v_i$ are the vectors for the dual directions. If you work out this algebra, then note that a single application of $f$ to a multidimensional dual number calculates:

\[ f(d) = f(d_0) + f'(d_0)v_1 \epsilon_1 + f'(d_0)v_2 \epsilon_2 \]

i.e. it calculates the result of $f(x,y)$ and two separate directional derivatives. Note that because the information about $f(d_0)$ is shared between the calculations, this is more efficient than doing multiple applications of $f$. And of course, this is then generalized to $m$ many directional derivatives at once by:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 + \ldots + v_m \epsilon_m \]

Jacobian

For a function $f: \mathbb{R}^n \to \mathbb{R}^m$, we reduce (conceptually, although not necessarily in code) to its component functions $f_i: \mathbb{R}^n \to \mathbb{R}$, where $f(x) = (f_1(x), f_2(x), \ldots, f_m(x))$.

Then

\[ \begin{align} f(x + \epsilon v) &= (f_1(x + \epsilon v), \ldots, f_m(x + \epsilon v)) \\ &= (f_1(x) + \epsilon[\nabla f_1(x) \cdot v], \dots, f_m(x) + \epsilon[\nabla f_m(x) \cdot v] \\ &= f(x) + [f'(x) \cdot v] \epsilon, \end{align} \]

To calculate the complete Jacobian, we calculate these directional derivatives in the $n$ different directions of the basis vectors, i.e. if

\[ d = d_0 + e_1 \epsilon_1 + \ldots + e_n \epsilon_n \]

for $e_i$ the $i$th basis vector, then

\[ f(d) = f(d_0) + Je_1 \epsilon_1 + \ldots + Je_n \epsilon_n \]

computes all columns of the Jacobian simultaneously.

Struct of Arrays Representation

Instead of thinking about a vector of dual numbers, thus we can instead think of dual numbers with vectors for the components. But if there are vectors for the components, then we can think of the grouping of dual components as a matrix. Thus define our multidimensional multi-partial dual number as:

\[ D_0 = [d_1,d_2,d_3,\ldots,d_n] \]

\[ \Sigma = \begin{bmatrix} d_{11} & d_{12} & \cdots & d_{1n} \\ d_{21} & d_{22} & & \vdots \\ \vdots & & \ddots & \vdots \\ d_{m1} & \cdots & \cdots & d_{mn} \end{bmatrix} \]

\[ \epsilon=[\epsilon_1,\epsilon_2,\ldots,\epsilon_m] \]

\[ D = D_0 + \Sigma \epsilon \]

where $D_0$ is a vector in $\mathbb{R}^n$, $\epsilon$ is a vector of dimensional signifiers and $\Sigma$ is a matrix in $\mathbb{R}^{n \times m}$ where $m$ is the number of concurrent differentiation dimensions. Each row of this is a dual number, but now we can use this to easily define higher dimensional primitives.

For example, let $f(x) = Ax$, matrix multiplication. Then, we can show with our dual number arithmetic that:

\[ f(D) = A*D_0 + A*\Sigma*\epsilon \]

is how one would compute the value of $f(D_0)$ and the derivative $f'(D_0)$ in all directions signified by the columns of $\Sigma$ simultaneously. Using multidimensional Taylor series expansions and doing the manipulations like before indeed implies that the arithmetic on this object should follow:

\[ f(D) = f(D_0) + f'(D_0)\Sigma \epsilon \]

where $f'$ is the total derivative or the Jacobian of $f$. This then allows our system to be highly efficient by allowing the definition of multidimensional functions, like linear algebra, to be primitives of multi-directional derivatives.

Higher derivatives

The above techniques can be extended to higher derivatives by adding more terms to the Taylor polynomial, e.g.

\[ f(a + \epsilon) = f(a) + \epsilon f'(a) + \frac{1}{2} \epsilon^2 f''(a) + o(\epsilon^2). \]

We treat this as a degree-2 (or degree-$n$, in general) polynomial and do polynomial arithmetic to calculate the new polynomials. The coefficients of powers of $\epsilon$ then give the higher-order derivatives.

For example, for a function $f: \mathbb{R}^n \to \mathbb{R}$ we have

\[ f(x + \epsilon v) = f(x) + \epsilon \left[ \sum_i (\partial_i f)(x) v_i \right] + \frac{1}{2}\epsilon^2 \left[ \sum_i \sum_j (\partial_{i,j} f) v_i v_j \right] \]

using Dual numbers with a single $\epsilon$ component. In this way we can compute coefficients of the (symmetric) Hessian matrix.

Application: solving nonlinear equations using the Newton method

As an application, we will see how to solve nonlinear equations of the form $f(x) = 0$ for functions $f: \mathbb{R}^n \to \mathbb{R}^n$.

Since in general we cannot do anything with nonlinearity, we try to reduce it (approximate it) with something linear. Furthermore, in general we know that it is not possible to solve nonlinear equations in closed form (even for polynomials of degree $\ge 5$), so we will need some kind of iterative method.

We start from an initial guess $x_0$. The idea of the Newton method is to follow the tangent line to the function $f$ at the point $x_0$ and find where it intersects the $x$-axis; this will give the next iterate $x_1$.

Algebraically, we want to solve $f(x_1) = 0$. Suppose that $x_1 = x_0 + \delta$ for some $\delta$ that is currently unknown and which we wish to calculate.

Assuming $\delta$ is small, we can expand:

\[ f(x_1) = f(x_0 + \delta) = f(x_0) + Df(x_0) \cdot \delta + \mathcal{O}(\| \delta \|^2). \]

Since we wish to solve

\[ f(x_0 + \delta) \simeq 0, \]

we put

\[ f(x_0) + Df(x_0) \cdot \delta = 0, \]

so that mathematically we have

\[ \delta = -[Df(x_0)]^{-1} \cdot f(x_0). \]

Computationally we prefer to solve the matrix equation

\[ J \delta = -f(x_0), \]

where $J := Df(x_0)$ is the Jacobian of the function; Julia uses the syntax \ ("backslash") for solving linear systems in an efficient way:

 using ForwardDiff, StaticArrays
 
 function newton_step(f, x0)
diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_3_1.png b/_weave/lecture16/jl_AHYinz/probabilistic_programming_3_1.png
deleted file mode 100644
index 370748a4786d942af7cadacba7b1dbb239b6967f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 22224
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

diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_5_1.png b/_weave/lecture16/jl_AHYinz/probabilistic_programming_5_1.png
deleted file mode 100644
index 92d9fb699b7e2986d01acb9fa834b4804cf8b5a2..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 13633
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
O0FimE04o(Y@cVD%X;q&9

diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_6_1.png b/_weave/lecture16/jl_AHYinz/probabilistic_programming_6_1.png
deleted file mode 100644
index 780f52a4cbcbe0aea143784fb51bb33a1e1c1fca..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 6202
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

diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_7_1.png b/_weave/lecture16/jl_AHYinz/probabilistic_programming_7_1.png
deleted file mode 100644
index 585f2185440802d94abf8d5cce914403a3a85978..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 19593
zcmYg&byStz6YinA8|g+mrMsm;x?8%tlAU&eb=SJfKU|)3
z-u=#=J@d@WGqa6SRhC6VCPs!pAZYS(QtA*0v={^e6^sZCJ{kU}AP4?}H+wHD1$lY>
zliOaD1c6XMB_%C}$*xXwrHsh(AEq8Q1x?yldCvAFzZ}ueNrIBTbZ&+ID{Rld
z_}#wT5z=2~`JFcJvI|Y%Kp??l)On;pmRugyE+7EK_h2Z9n3x0<3?w)>6dE3gj0A@Y
zgvUlEgg{_vaM>XcL^%c%2n6%L|NDTv|I1gq*^1-8#Zy1k^8H`_28M0&B_fhjEWk}%
z3Qar=Ro?b&e>R8Pj!TG2Ni1$e!;0zei25s2e>lN`Dk&)EyulZ5O8DrT5h4M#SZh7e
z^$A9;Yt*V%ue>meiqD0_x^WQ?8Sa=O6!W5}v!hzAh*Efvhf}6z!zp+9q#{s2>W+vB2VIW8!T_a8r|Wkq(oHQjyY=?=Qd3i}
z`s`-O$cQ}u+gxt-YSb%Vi9~=vR6BRFB@F6p)I2;qbam(B-n^5EBj)iun){P4yDDHm
zmdj^gx#RY`IBdK19nV5BO~K+HJ_Fn%3L%alUA7y<+qVmkHed@
zUW`hE=8G9bZCk#(*c3@nL5$FW~Msm`d41Mjvs
z`eTy5e6d0Jn3S2BIXXJ}{Pf`WME|ZdI8Cu|W5aBA>h^HlZHJ053Pf5RIVEEhWP}2j(FAoeMySE+>Y|o
zlnja|^i%(n;VF|lvF|>f@vic`l=qp9mNv|C{^sA%*jtnO{(<_7GMW71va&0}r0i^?
z4%k{2j^qr=8w_WdslISI!b@h{mqy!~?o9WItHa1SJD!#8i
z@$y2eGq`sf8g?nvXpH}g*-&(?r*c?t{g#(Z`^J#?VNFD-7?m~?;MdG>;>#AIIV
zP}A>vwBYeQcM1x%9luD}VB0z1k}B9|>gyeCUd4MB7WN-~b3TzQPmM%LRXwL-#I9b9
z3s?3ODMdAFd)0mZ-ilDBShHcUv{4sMkuJ$&e{%b8u|}4F2UTdMNI;9zYDW(g$>8QoA$@f(o^^PRhvIv%Z35YU(c65D+dWoDpy0G
zb=~M)gdUD+o!YNwet(y|yu6$()2;P7HIk5%mhLxC5N>eY9-f<&sV4?GoLIv+;p#k@FBWhrd^qgh&~1n{;V^uGmrW4$Kq2uGfVuJ3k}CU_V%>jSVpka5Q8H$=Nkj((Y+eixkwHyf~W^@rv)67VTWujdtAW);SI*M(bLo)3)_8Qc`txZ!rL99rviY
z|JXQ`7CPjO2&@#io>vayjoDHP^lPTM_+H`E?)TLDP~tg#ebE*;hVbPldQCQ?5e6#M
zr(L8Bfeg}1Z$Ht*$vmf>U*?1H!qP34=P<1O>8Kd3bmP1qC@dzhKr_Ph@Kw`OtE4
zaRK0CWZXYn@9jtk^-+R2P-#hj887Y$QYo%faj!ILHt6HZGIClLh#)aFI{3F?I^e(~
z^e~OWqpw}h_)ZL^zrR0@gx__#B!jJOLgc~zWU2YMTFU7X#0+l@w#DgLg-)y+O~EE>
zX7jkbnN|FK3mOzDuW!w((pGcLGoJg3r9pIJOw2k}Yf~zs+ddt6c~ru}0V{1j?w7e>
ziMY@zasy+kHq$ig)NctNa9te?rHVc?tPEK%j}u+AJwMR4k-jS@LKCI@TiLJ7_wri>*+YFyVSZ`ZUp9vnBWI-Uw$gAB%oiShJN|6IE#o9g4ym!^ot{e5m6t2C7(PlL{*
zgW@~OFtanhM1y(d3B+gf^SmGyqboMOiRZ`53Es-F#*SZW?KeEwjK#qDUoQamF#j~z*IG9dep7_#uwr{Rzw@2
z!@AvF*8yM8lN*t6xjdW#c_kI8bV09^Jn5J%SN@S2V3a`*$Bo}u4G%(sVSzid2pEZ$
z<4!x&+v9|>I21L%QO@WvXmre4Y*yf4l286cqdfc7x8bU2LQv#(!V?Kn=VB!}FIy=P
zjpBb-5Z%3*NB;8u7;g6B;-WhQAxFTY#1mdtMrOeH<$ji0Ha?b^r^K^LhnbAe*+f@2
zZDQk98`%09MB155M|X6t_h=sVJ7L!j`m(e~E|A%Eo5g?t5*W6R=b3
z4Uo0mV(B_R()=);s(;Ysdg-IuI7RVLun+1jtZC>Jd@I4ss!ju>F*GN&}V?T)8%Y@W29-stiwZa{3%
z#x>1_+CI$HQr({@RL=}@lqt^Dq&|cS?*m_28jb
zOr=PayE$IGy`D9mNag6Q-wPkJ$`JPRG&9>6Bo8_o@#ceuu|exSxo#_WOg5^Od$zn%
zd@J9P&~kaZeP~A7SSsg?iU*g=!sxRTWz{peUAsMdregPDHv
zteMkF{6MWT0I_v0e-}aG(Dca2NYmc%u~(g>o>rOmQc+RG4rByjB_<|{po9PiMufGOxf0ir{g9sQ$pWalp1+5#-UOyoo{WoX?qTZq
z!JeR+Q5DVJ6?8u;KuvIcdN?c-j5Im^$yvKup-rG$mLd}vnUKtw_XEi=RG1Gjgd$v?
z1aiH}S5dYF-+8s8HG;jue|sj||Cm#I$~syJYCM{m*r;
z?;z)!o0X(PQsd%in&5QflCH-4%wOue(q79MIT{!;tKml#Nt2L1I;L3~Lb
z_so=AjA3P^a|gZ*=0mHq^Ja*Y3e87Pw=0Pw+ke%ey)ojuAn6qAS-1OMNPZnL=>rd|
zMEngG_Z+f43J+_^H{GXtO9yu3#UAXG9V6dsVh#UcP_kc|p}~#INGIp1NNPzlM5tOh&?t8cOSWAaw}1
z?7;)0#S64M{MwhcI*;Ph6wyBEr)_1IMA9mz6EOFvdZv&C7?2X0pSX!$-bij)h-P;D
z`H$xW!L+vYM(&INLmZaPIv5`9J>scV=en(H=sYGzqFHvz0*S~tUCH7x?Lcv-FjNvB
zg*Qk^xNVe;8?)YGl&D7+R0DA&l65XitwO7;wHhPC;bCA&fH?Q(=nule0*HQrv6)g#
zilyaInN;>Gi9cy~h=D9^D8L5-8{A~;Yg&HT#+q#d3&6ds8;6=+Gy7Oq%7}%v^T-kH
z18GBi{ap%9YuFg55tyln%Gt{YIP=J
z>9W7#^NmC^SVEoA0a1KYRZg~bi%NB=q6A7VTBrRo_G`+_8`@f{1~cnhzjs5RUIQW7
z&ZcAOtz%^BFHWe(!5YR#Pt&Q*PIL+}xU*FnF+mqC+(ncL6hEOEJXF36Dtc$BDoLF?
zw1;{W+)Icz95P`r{YX^1RMmraMcasvu64A}LWLBzhm^4DocYj#BqK`j>}xW|nKMUi
z?-)y{!Wj9^CtF&mI>OmNG6V`58Z7We*5y-vQ`htskyY)3eo*kewBW$FoS{-f;iTss
zDzI$_;PYz(=HW&uc@rKVNy^!cG+DGx{}dqmjq=4HLZQQno5xLab6UmE*#Tw%hU62~hZc88Xh>uJd`ZK8gcD;iWDe7?jfT)CBbaLpxR=~&V
zVVjfq&%&Q)Z0f|)$Z5^REeCwN6PNznKkgb(b?D%^sy~--
zk5qD-)l<{U%H86+p8Quj=xR%jObE+h00RS9rSRRNSrXiv0y}7@+sd6aKQtU(mfiRZ
zbis0Vh{Gqq03lLJG815vZdVj0CE9Bz6r>iBdhFAy+ZwQ}SXF0>Ds!NVl>%hKl=$V|
zOKIxXY3rP*}QSGAmtK^o`;bJh)mO3M9
zk4TNCaYasfRGrzaD}~6;V1jv2INO9*Rlcy+7Sx>5Z?U%=^xs<{1w69rr7xAC7=~kW
z1yPY&VY16yyHI-P5IxxU^5Syu8@}Dt<2R)X_1dExKo8WGZBGZ#a3*5bB-cI{oxN;5
zj!qlfcMZvFB&oY~&lK_?TM
zbEBq}r~sVWFjHlwfXCZ+@4RNpblEx|YZ~*Ry>*$Z3(|qJOOyv&+Jp;6%}pzU-I~8A
zCc;?LPWOXtu$?XeY>-k0F9#|P4olt1lA~$p9n*(wBZQj+I
zZ?9dAAg5`=TQP{IwnkrQ4ZqN8jBfh*M%|>gyx5p8!`^>9x*NlVfvS^oOXvdGCa?Jb
zYPcyPC71R1Qi})E#3dRUD+0$rSL?NlmekeN{ro9cIXlby
zLw)~pLX;dI-!&s$kOV!VS6ooq8PIO)&fL#fZ(~hmxukotjV0HfxAh&S0?&fP#SNOnMcCcURdHcCC)k*-Ziaqb6Q14|3W;p
zO;N~K9`S1k;_lR7juT-z75S~+h<81wqr2HD5Wh(n-d)<5^|!Dqfk8kws4@}f$`R1vCqYkB
zl=8tyFlqQF)8WsaUEo(yLP<$Z6kDp
z5>E6khTT(%M=NRyHsv<642Xv5mCdpyDnjRdfR6q5i#oymWa$pjE_d7CjBOvLCMN@5
z9#%yGhiB}6F74}kpDP{$Na*|P!yNyMp_Ap-Mt%EW-fCl!Qp{OA_7FJ_9pOkbvX=1?
z0po82wEXV%N*t19&4bCsI6uRoZS>qwbfl#E&Nl}E4`|L#7*E1K+3Iz=@a(l3&2RK!
z!DQ`4?@aa$9-hZxSgGJ?TkTEE+1c5At+gKuftX)SwjiDT$?qArYTg118(DxfnTpvS
zoE!hK9<04o;Zuc@$jX1OU2=e_5-!n<_Id0!-3}(MUYOo0qkBlM1vtr#03rd&p>E@4
zMA~qmZ&ZpFq3F5=jo)-gm~cw1?8h)D8xZFa`SEX|8zgZKfPO8TCVH`2
zE^9Cz!@02t-GU}*T=+rs)?jW{SM7)al(-mTNdO+}bjC?(Khx5%jtCeby)Q@|4AP9^
z8T=3sCOlIc@wz-ZSe`}e7U_!b5&MEK!;@0U?#T+4?b5OK&ym3fyyOsOsTH4h3*Ocn1q3;&LgdwqG85vIe*&K280_5NEkVI
z*K6oczg`DN5qk9B6&9~>pgANE?)4kmK?kUK)DDy%6wh`M1&vi7g@ZnO&ArM*0+fO*
z`^U0Deb=@|sP>ymUh1G|7AC#6;DOc03(wNyjgKIMBK}lAPNC_&)R}2#WxF+(ForYY
z^6*~Ki{8#a*!o@2x1fqH2h@>xQbAruq-}BrNf1y>J&fP%hpI_!)z$ptSl+%`I`pES
z9dp1kZe@SjrCQ(lIAgbK1XoOszugp>>CEvF5=|xMBQ8-
zvOi($wXsR?fVuny$>nENUteEEe_2a!&Erl#^@lUhjKe?3G1R`lnnEJb)fD%3LF&q1
zQ&B=0DB2517)=M;-qHa+JQ@|
zU;F9Ka~$iQ?!=D|lxLAZUVPIgcNw!NfqCy2^7TAn7VMr7t
zrk%{XLF8praj-xZ#l?YAWy2aU@3y`68_p;
zxj2+vg>X8ha-m~&c6@vdC=y4iyY$RoG}0o|=>0A)Snt?b
zZMu^wXN?AOD>5L0O&t5Sjc$JFN6g}QrU%tgoJiK8FDO0O21*up7DB;n--E?}
zU+}GNbI?Mi51B8Z%zTA@0cn}|i1~HCAz#qkTft?NIVs7MR4rVH7kFppm!3>v`&mP!
zcy_
zY8Ty-2goMzx(=O9c@NzVc|+KslT%Gev=%mm66jpQY@FZVxWWCNbqQ()UU0fM>_Ptq
zt=6WBQ}`d$>-T?TvSN$%gC*&s1n5r}etFv%6#u;>NZtB9g$%Ry83@iWu0v&i@)s3{
zOiYZ)o1}+M&;=iB5BO1T?`3s19O^E{;_#+d>fXez64YVMNjoi8?))4qXFdd
zE%?~sjz5zK!k
zR7$+jg`Tpq!@)RUK2HC?(?2_FgB12ZN}gWMtxlTHp#JZ}|D3hH@+nlbq?4%J{8VS$
zLDB-JtmMC(J(^iO5Xh5YMuzyhc^V<~&T+k-;exsD$%of7)}=n`w(!jbNclNb7HUzS
zIzO3a@0c?JB?Div5J3UhB;grA!Y_*pHGZ~Rbv>}{s2YHa5pXmxEgdfY@d@))*rDYZrp{3e|~3>)MRMf_k~0Z
z)O@fa*|**>)5X=#rVlOnJ4Rg>%E$@9Q2sD*sLyKdgP5ueZjd-gw4&Wg6NwJPz
z6vyi3P{ymEauJP*gof`3gNBDvl$bjoNb1SXS}+BiR^??As#T|Je62~)`!iDToJp@@
zv)+sY!ic%Tk;%T`WGE}>ZdK4t2sjWZ^Kz1PwSuz?R^o1PKxk9pAr#cPPi@OtbPph3wG={j*Wmy@-kz>Qj8NiTZsXr~#KIuu8mi%^a_VE|hE
z`FVK6D(W$A_NDBPjg4ifYA{g7$FlRf*wV#L03*XGx4EhG1!tDu66CgJFX}6@|G1=o
zWe+L!DPL;vy}V5jy*~>Eln-pED!@gkk0ss5LLb7d_$Jta8cz>7(jOa0XentV8wI3q
z+e`UrZ?6997?MCLv<8pcgqkoUMmW}dc{X2sg1%==E1=ojizydHo@ss(Zp%stzf&+>G{uY
zlYfh=&%7Xy4t%;7emv_XU^O_iRSp)wN$Lq3dphXG$68_ixIsh*UWq9rVkFLl8XCWY
z&)&M6osz=qveD=H=%Ds@A@CzAXzs7*m4IBN~&}
z%SM)$8{2;%KUisx!n%qEp{{mEd1J2bW0F>Bz;?1WBIWxO=6%qo{`m1@1iHtex991~
z6Udi~pL{`UcqE0T)%%=khvL)h`G=NlERUQM{vHbx6g*L9u`DxeM$OD04MbRUoCnBA
zL~`%&U>kSHK4l4dInGsO+|JX`(G3g?WQ(q@edeH7&QxWtvbwwpu%c4U`7G&I~?GVG>=bg(0yZ(OuA#^T=h
z4A)8umaHTyt>6xD3&ZMJMOg9468>?@L8X}%2pgk_k}t@3yB67RmZlgEJwq}k95z>P
zH`~_Mwor)%F9}M7*$PA9fmFr9){_<{TH5W-mnSD`Q3YoF!M;BC2PGRVHXBkqW#HI`
zV~3{IyOvpuJ5b+UVNe|wl@OfbfsO8
z(xNvUtyH^`E>wp(#qXrK2sBrLRsr%&9)fOX{*$Yu&4mtwXGGz1FI%o<#DjrUnIgt<
zNple{8XSClp*wEx&I6(&7M|6|hi`5dj$-s9h1Mzl^Bqr;b~J!CDPG;Zrw2@}@6DGNh{>fHnw2VJv9FXU)n
zwL?NpBFlsd`wrk2YW$bd@fs@aBugpqDCk^BqL--0Qgy2{783iJvVySE2e6RcnBYI^H0JIikN&&hX4a6;ob=eIcT0J$1
zSMXhB*1~$3Ni%NINlWb&aXTMo9at2ivqTSVP|;w7)C(>BTxvk#qIcSm@rCvQvz+~Z
zZ-wYAPmrHf#48Jl^9lAA2->
z0SKEU6ikqHtMiw}>3~e@ujx1fIQ>GuNy6yp35bkmcYK(3s6ID-5B}77+2l`kkl15J
zUN2^Hx@0`pXCQSZfXja7M6%2A$!G}yXAdT%w>@~CHG8AXO3##n0X$?}yPdNcJ6%CX
zjfUgZ3*GVQQ=B}$Y+qA)+mz{uurdb*EIC-gj%+vUH4qlZY7yEHg{xh8wHji$VRg4w
zrM~<3%`1g@*{|xv!BH)bL*|U?$zmd^T~s)%T^%OOf6ZYaWWS>DdWi$nwPtO{!Cq_0
zu+Q6z9-@|BO(8T_w4qn#Z_Q~k3^Dkl_5!Av+ci|?P<^4k!&wMxNV(!!xAWV5txdQ8
zHvY@c9M9?uJyQbqWy%_wh&KW?!_B%zVJ@rqppyKCoyeKK+~Bu*tPa)bJH
zE<2#sASJG*G0~;M_BbtU8=kwaKRt!pqvD@iDZ1W+Z&g4
zQ{)ChkS~95Q9ru8L)W1JoVC6y#f;
z5C@U^1TAbtI#6+W+=c1{^yoOT&BQ*EcKxcKmVN{pNF38ktCn7WNs{U9LVz@9NON-U*UJ
zW7m;Gg%`O~#mG@{Lk2LzU3f@m+4z~F!7>yefM`3pPQ~xQkuOr_G9o14@Tzs?#)0i8IBRLxhU!B-z}>PT
zV1@ZX)f%_%F%RlW*?`LLjC$s%SG{{(D~h&4?i7Q8(=UT0SA?iW{rh9a^*xMeeJ!
zR9TVlWT9(ofV4yX#=$}emP+2;KXq4EiX>E=>GcO^LP3NgDb%S!grT_E)>tk?sCr;P
z^s%RW7b6I15W1sVp5veAZEfVYEK6yCGHYNCqtk@nEz&xx!m65^co!oinMOc_xwxnb
z!5Ztk2iPf_cUzdlpZM;(UJB0igdx-giCC24pqyFArYSsRBXUD??1!J=kNw>+kYYuddb
zF)cIj+Aj*I^}R&*b3o9~Z!{F3KIEB`#iBd|tpz2k+h%LiEZ=25*JyS8vjz^8|KgwC
z_-lI2y9XwvIIE6Co*wQ6js*#8T+g3g*l&y5QhZWloLzo}ea$Qdy&Hv--kE
zS&)kv(-KaYsFNtr-E>Q@9l_{UWP9Zv9YvuEtooEh(9_=Q`#k@}AH)G()dsD9zh8Rj
zj+FM~5V3U6_64_ohO6M?v->^sTk9s$G*I`;VPZNXWBDuKuEqdlRGE!r?`aY6`0^7*
zGEgOajIwu!orj}SoZ8m=|VrE
zh`}yH%g%7??--as@v^9|x5{`osR@?9kzqao5c5}%`W4{Y`0&l;A6&hVBWzvf%EY1r
zzy~p4=sCpsoF499o70u4RtP_B=Yxb77e+Q_%cD`tS*v#Y3M$&C>jikF${5-#N=7S>
zq{kNqJ=3mA_DS~s{}#i93-cFk!afB9IO?F@B+>W_Z#r@CQxI^0q6NhJH@;PdB8Lr2
zT)~0>QLq&8JhgQLm5f
zSc+zJj1E}$4w*J8;r5|7CL++1tfK4^>o4oK51`NS2N~vyi0RnVPp0QcpS(F~(XA2m
zH@V5@LD~H}(q+7M%sYbAxZJ5FtMmg8{!uzL6CA7Et*zv2E6U4>t)46A`~kAPCvsyC
z5BnM1>&AV(j`gX8!)Qu(T{U
zY;9;LRxdtjyHrkO*eXnNCK-ggE@!d-(0n~gX*xmsK~_RX&xhh{kF`A~=W=_l1t$uO
z+F&SUxb~|z7JbrWzCSr+`;wIOam$XN{uC9pms^8XnmSgk1TtZ(vGjT{K|T}U$F5d7T}NF>ho7AszgOP
zNhrJc9Wp2|Z)dyO5m0Y@y4u+ZN~H>}XhB!osM@A7lcJV>O$JZ#XNA4oqT;O=3
zLU@{5vc6&NuBkd5PnE+n*C<$v~`)F{Ko2W~{6$AE=m7sFnU5wL{Q49od^z$C
zzwjU-A
zbq!(Pu`TwS+Kn4ox`|Xekny{Q?j|w7g$Cc2T3H0@m}x(tX-|BvA%BtJ!2Ausaz#x?
z=ijcj(fq?NxplRt8ZgsHuhNnpe`G%TU!QLWQ7(~=%+<%Eqkd`fR^P%|l6!-&DY@?e
z4cm71O_x99wM>(aQQci2jo4#%o0XAI^c;W@22mMB|Kh6$@7=Mmy*{B^Z~Bj$tI*=8
zNj<%Fgc^XIw)OkpKJkc0UpX4SNr`a-lUp29w{u;me2o`6d^X6EE*>C_$?J_mS;J
zB&pW%5N5^nmhU^-YaOl^#q}okXcf$C2haZ+I#RqVvxo(IpyoCyl}i5jekAO
zBwGaIDZ7H#1nJw;0-T;t*S9KSVea>yo}M2+7MISSTeJ+AdhzgAZ?Tj2yE4bwU>=nC
z#mBbz7CNE}PiQ+mQhBboYD#`$1HFaXrH8KkA7v6{syGtiHfbM<9UtUlXo#n+2GR;W{^D>?A+?3y)_Vsdv^imw{
zJO6oK(}~K3M1&8Fbr%rt|L8nF#5pfo^}BnQwo`o5BC~MS9^7I=A4Amhfuw&e-&?Q#
zkP0Yy=x0R14h6bo8HVh`JgYGslcTI~l=JE#WTQF>|rXamOwL8>uO>m>*#%bsID8__Q
z2eSPoiwRNf`gI&r3>>CUQR68;WRm`aTohRLbN#qCHhQJWoLTCbHLQILs+Ps3t
z(x40Fh$=4E6N!%!1PW1*wmvIPtwW-4Q$iVRqKCO#za?eji^uwjXATPGhZm#3FfB0~
z9{VEbt%Dyzix-_tqmCne$)0-wO=2p@Gt;~*R$Os55Qm+*M0U8ly&c7S!%%;N`{SM(
zo{GU__45k^`hX8VioRm#VZbqOxg5o*tjf8ze5llr`Lj6~ZSTL_&bfokYy=VCXh%=D
zs>4$*<=;4v$JHg%PCoa1gF}~3h^mJS$6R;b7I-X6Hht);bClGZq8T5J>ltmN+-J{%
zhz{-!Df;?IKIhy6HY_Wv?E9zx|1ZWeJ1E&x?KFowk1($iIvW-35((wIP@_zro~ypq#6>BTemS
z#2AkTSa=*b)@b_QdYr7>mBG-HVjlKD+O{YERm{$!SWuGX|rB?#7R3rz|OUOkB
znx(*{R(w}m&$5d9^B}UI%$#YOK*9$|xf}$yJfUrq<$X|s^5&ERl0v4
zw6)zxv>)mNb}`~%GKAnW@uXQw`eckTTSVAmj)sWHkMi(6i7^VS$wb=p(+wU5sT1Y!
zSqHL|R*G*t$*Oy9Ze>N6?EV`Qw~E5LT8DcXerTorar-oVhxmXM)-E8uV3#l)I
z)dN#xPsTD6k8VAebA{Ml+fPjq&p0nlvb$o3+hf|#zxAUIO#>0&n12mbLN)2)yREKbXJ4l3=j<`Ou!hC8B)(_D14AUt-Yy0n_p?}j9n%WS
z&uoH3L*#<2{%j{$dd-l>rRcrZ9Hr+NT^a(TI^@R(D<2|$v-S#
z9+D`{;|7IHLgaWVFW;0>7fm9=V`2tQ$7e2HuL{V2mF?XB<+&VWd^J~3Vj~WPA^~jA
zDKF}G(uG#4pV2G!FPP`*{Gr|Mz%A5FWPZPqIcY5tFcuh7$3JP
zx97lP+xXG&rB{9YpgCX`eJpZ+`J2&y9(mZP6qtDO=GLBmPKH}F34tr7mCtuf%Mn4&
z2NfDf@b%8rZvU<67VV4qUZ*6tjcuG`jwE3S3O%kiA#ki?=Kf%&njaxG7AhdVI_j!*5m3E#o?itTBPTi
zGV9|-xn~a6a{1>JN7x6n^qFo=A)MHoA`N1I6!@uv{1_PqM
zR!fCOxgI0JF`L{8_J4ywQ0ZR(UjXMqVhyYC{og4vbRwi0H!pS@{*6(cvK{oZ2cq4G
zkaBPhC#6rgjn!;JG`cg@jL-8B0&YCwF}<)v9ptt-q}%psx_H-}N``}Q5i}@x1j6Mg
z{f*u@iM<64k_S9*pb=LM%2TkBAB}m=Al5sjc}N
zxF1Wqo619<)TsZ4oLOn_qDTp$GvltBE3ejR1FEasJA6KmwB3-0+2LblWp?{SAW!A9
zDS9jILlw9;gB1l@1&rzg;0jf;6#YLw2X=9&{0ZH_UQhl-a3==L$Lkl{-M=A}nc2?>Y4I=S!=0`NvsjDKC2r#`(V&&#rI#<+6#aZ}?~Q)KXJVe)#yy->AGj
zSQN^XlDFA|WYUm8smj@;pEQ3m3BD$cWEI^AC?9Ruy?sM8p{1>(d{UdaB#UTjfvl#k
z!dj8kpl|GBMj#)d94mEDx(5D3hBD!EA$^S@trR{ck20fO23B|6UBUO2=Vi6hqJ;}5
zOFs3se&XBDdBlPl;=kThMA&;C9jS`qdY>?==J}x^RhJiK
z+qcMkw9U13k#^9#57?bVIqWP7r9GtauflMvC*$Y9xT-ev&&0C&I344Tzx
zu!S#2xxmnad(qTBopR>CquPmRi8Y;6t~Y!dXx5xdk39yxX=wHyVYTXm
zS}8RjEZb_Y-(h9=oLlMlu(jui{8V-+n0X0`)+LPdI`BQ$VQhO^JUtFvOYtxNC3gOj~EDwA#QQ<`R-)wVn=
zc+}QAzQ~-pJe+15So#5eTvM_w5MG6*LDtLNea|-G^m)SR-7^ER7`J5mBQ9jkjhR}Y
zgxI;Rt~U=mV72I5!88ex<*;(K0qUmy4beC2tnp!WYfHxF$u7jirixTX9;##+nq&@F
zTIxhjQ%*X?@krYL)5y8MGu{7jTvy*Dxt-#UVR9yt!<{gTRGLGp?#RA#N|sP93v)i3
zQK`ly!&tfHa62p^mcw*HMQ)~Ki6PCQ@lA3z-oU3MP_3Pkrtr6&Rp%Arwl#jTjCahB*T4rdWHWPemS84h
z?9=LLc|pjl@9piKYgs~SXq%bMi`M4ie|>O&^rtO4@rqT84`()>QFD_lYcEj~5<|{5
zuk9l>zaTlyJ$|f{LXfj}(OLOgC|AebmXLGFFp6LpC2LP$pkGIkQVcHBt->3v+}|Pc
z*N00;MpypXlid2j)~@&rb!4MBsLRV+g8LqH7W
z!^U0p83HZFHAWciKR&LdsaaHH(@?$mTS+eW6Txf{fPpKgRgg9j!R@z|6%~mgf^Y8&
zP{I7R!^4eMoP8D+i>1ss1d9B<$~LGiVH@ZyI>62VERP-H0!4>+9SfLZ|+We;pAIV(M^T$$>C0=%kPnA2V80PpZOB`
zgQU@fP=0@V9A-4oTbri4bls?HrNyOhAI$CILbPj+Y5dv!YUrn~zSK><
zkCN_~oeTdFRh0qh8!<}JCHDmox7|uh`vjbHCk8(Z5`MeIEw5HAnu0x$oWs
zc-POa6(81?=G4uK3%|@X5WnQ*=1%RuAfHa)w?2|4XlS@Tytv
zML)m5ox8AZZf;U9Q>av~v^*o!y5M|8Sj(~0N=S_WG*b1$6oM~w2E!AF!+Eqx?%oY<
z6{6Z1IBQ{%k?g^%tzj-zFsz5;f8?N;m|aUAio4OeHtWH@q0{rz=v_6wei|ND2z}T$
zE(Hb}Xb`|r0Bmu^M%3$@isK!3I|M#rVga`BHe~}dkcsLdF}PDwGBP7iujS*bbWY*rJ<0)Ag$t>DNa1wnAC^(EsEn=ME@meIokA-mST%h9t78q0k(oL6jNpdH
zBNP=U@{E~GW<2zEjbo|$*&3ndAPHLb2G$FdL7Q#(v{ru;r9LhfdZw{_y6&mGKd$lq
z;H4*VSsYQ+{MR>NX?P?I4@kJz8{pX*6N$xQV`A2!=6MV~@$L>1U@9}xYSzVP^kJ~=
zh6xu4at$g|yC>FMw9Z)|KdI9Kw0h@C~&jHjMUYyB2%
z>6s(t&hp-9SGn%l96=)%^Rpmq22<=EyMM=3ozJbzH7vE~ik3TUzfDYBrH
z&p+$wX%n};4C8qC^R+bDyZz*aVMQbosjhAdn-c&fHbqOEt=QV8Xf2Q`iOkN<&eA=#
z-@w2$zgykXzJ%}6fM6Enr0lo-EV^76S>nTqn;BrS%E}_<-{^6c$8N!ssbqals+NglP*6|_
zw!k`SVG=$flYIZd#Ts0HP?%7XVUaHcTBp3coEf*e<>m-Hxm!M2{5L#+Gs1QSsL2+!
zp5f?$370&dG82
zXamo{*cjsTb1?}?`rH8s=fSHS{;FAgY^O=<_kK!w>ZjBm=JBVnoC4CGF$8_@0ocy0
zFaJ1KiUscqRa%YL9BD2*{`{B`+=|W152?s!Gu2H{R)7C*AFDh9sq^8Ml*ucHi9`Cv
z#^giFsi0zbMAyTyu^Op7ELzy@Un?BpFvlBdS|=|`R}FY^;x-^g=JeicLI7rBd-)p1tlsUFSb8!q>w{k=-$rn81JD@d$oL!t{GOBNW&6yoO}aZ652(wkZU#8wFaPRfn*BAx
z`ik1%{_1dGWaRo`-!tlu0V)ryV6fX1VU##otCjAs4afR1{YX0o>HlB$f9JT$Tn;T^|bAw3`B?-T3woy9$2N3JPuOF}!wdk5|{rUdR0cW>p?T
zDePsfcSSO+uON_}fO^JsI;?>P=WP~N)~1x?!tXm48Nz_1-mACcJ`_G*U;{Q
z2{kHd92tBjg{=mqrQ}i3sKUk2bta#Dr;i7a$6Lqu~;tYxU=lll~+61Vm9}$>wkaXEgva>st1{;}>oSz@B+aAsa
z;DWMRTGCO+r>7~gA}P@O;stGYMzgBSh8h8C8dK22$w^6nF?+0AtD9ESX~7?wpX-3J6$`7LNPIQekQgh;|6ONA5hGWHMyXv#h8#W1-$Oie*{DW~
z5FK4vOA8MZQw-q)J$-a!3wcm0Q%A!TXAo%_PRfA*6Q=0mjKB
zVm|k*NclYCuK_LlJYLRFDP$}zEWuX3=*?+#OYvaG&_yGk4MNdz!%MbMM
zZ++d`$_fK9WMBdomB6~D+WR54V&?>zZ${6r;)z%xc=4I{cC>;)0qJ4H@98`nw8-;BC=)gkG*cmYyF?W`%JmSxU9;Jz2JBC7{B#8WF
z_@{cOy0K*X{9`8}aW$^b1Wl#4oo%6fCsCxff#%O|AP@bQx@JfyC~lvKAj>g^MKf)Z
zH&c-$lc|O>&)lm=+!g=)^1|~(&nSslItro+`;Y$}w;qO_+Q&Up{88@o$ZOeLPZTD;
zkF;?}F&~FN5uAO4_EUkT%{?qlpd(u2!|8m0!lA5ggI~bGy{1wA-e$Q2>f^z|4Q%)`
zUlHDLbd{RS`MqByyY71JJID?)B2IgaD&ahR|JqWseHQswBu}%8>cSqZa4MwwZp;P0$~$ZB47i**v{a1zDv5p7M9MNu`eau~_obAo3IK)tb4w
zY%-xUp^WVm`k51)ZH_6*Ujg+Gn@bJ$y$i-%6(baL&=<78zp0-e`c3zI*4q&GMM%He
z5s91#EV~!~z8SR3oTXB%aewmD9JxB}UU8wD^Pm)&GnLlgL6-89wsvZmJ_an(PxZ_r
z9`2_H&PB;^Qo9ba=R^VrvN~?%;0HrCIRtK6{ELNxo(2@=?wD!DCm+^AXoSt#3S&!4
zORJjs^LtvuItKp?!lK&02cGGaC1gf!QWi&o&46zhPn{
z;(SEGlQX$D$5%^ca14sqq|MLoS{nSCi*VcJaJNQ$82oKTtW=&eExeY4hsgHcES--c
zUCq*di=th>M!R>gw02SaYF<}&`-%K6sPETr(&^DrCJ+OD{rdIu=X>GaR-gL}&%Hum
z$w~ix0o?06N;JUap+coq1V30=Px-Lp*Zr>5Y-c)nfG1P{5lbIr~
zpk6jmX1(LPG*ztLc0SBYH^9NkX}{d;vDqIVn6m+J*Zyg--?t{;p!SHGlRBkZ6pe7Z
zC{cqw&Q+yDP@M1AJI7&e24_^)R4(=SWd{0lety@4L7HIZ;
zyz@BU5)&8qbSl2WF>eX`(VcZl~Hh?yz`OrH8{Z5{Kh
zda@6-1yqnmNZBYQ{mtc(*r~+HZQfq5`1#q`*qE4XTSzgh04h|%Nm(kWrd)Q|_tnbu|(^_-dpUTLLy
zkL!kGF>~=xS!o07VJ0G}>)npLJ8rfHhaA@w5Qmm!&;8Qc+S(o>FWDVY1`#Nj_lz40
z+DNria{5j|Ap6LL39COAqhMneTRpK=lgKl)6pRuYRGrn
zNNq5>62CvkslXb!aD`kw=_c_(n)h7Zt?eTHbu-H9eWTF7$(5u*GcO|ksgUGRMSKJ`c$p)Wm-{~#@(rr4a_
zZMUJ42&EsO7C6{3ur6Z9cqkxXomxuvR{$)@+~VeSlI15TiV~pDB`W*@cK3Xi*w(x0
z)U+f3OeRs!b9YKz|MFwtD_EqvhEJsj=GaL#K6by7>+H*1uXKsrT~bKh{(dUMbX!Th
z6O#Cs6Zd%{nNLEr&HNoA4yrItQ0wVserXM6hPP5zffUL0<8N!TrouwY$pR%y
z(KY(pkZP+^a`Vru@-N3doGk;)W1;M)9UZE|im^%lYMf*@T%0WtW@Tl)qmqBPS^wIU
zqMLf_#LLOf?sGL~E-zFk3>EaWFN@z)n1*XfH^heFyq;n+;sZ^E^z)bM(i_e}*GjQC
z(rB5rtFhz9vxse>w)L#!PTE=SvL?^VJ(vCI3Oa3l{WjYLjw7>H@odN4@!U7q3~7RX
ztvya6FH6S*758P!HLoR-^9W_I%^P!%VrZiGG%0x+;}$1(^VW5JrX1078#De8;HDgt
zymh+RJ2_C-gs3e4MeXVsB
zOjQMKPZo`jSG%=2oOW0;B_hERISI8_7-^Xsimy{+HPv+7nwpw=|8$?|_On*2bRdEL
zQ=tm6&&7Aijj>WZrS_khp66`FhP@F9#D8`x5;&-
z)ki__@j~$F6h&Cl#^ylR&BF*8;|~Vug#sn7$1xq5X{lSy!D_c^-Qe}ZRgn5HsU5Yk
z%DUM^|B+LdmTYJmp*kOC`vf!$%*4b*L+Wi_etwx=OJjC+cFoI|YT1kz9d1pf1l7NJrQ-#XmZ(QX39#siQ-+aU3FiOJHACD5o^IB=ySqWrqW_=63G&^3L=Q!EvOia|Q{-Wi!b>Gr
z6c0wNOlxEO_uHy=PImGKmP1e;YtMmg#=;h)eIUzU=-J4yH-8(K-B@SV*B@V9u35Dp
zuF)jqQ_$p65EHFPE1mhq2i#w)Cr9!~6CR(NAbn8o6c|i9`o0B5DY6?^wLVteI|$=W
z@#E{o+v$qIbTX?$q`s~JBpA8<|4M0h`5KY$CtyAU#-28RC?}vgneWTnDJFlHliFvT?&SLasj(c3doT{lB(yx&sv(*Ch4`@828%!#;$ILae%!8V0P%F&x-QMrrt(m$up
z<-3EG`=BNpt_IxLSm|z?qkL%SMVYlJUzngP>@c}AGc7Q*0F=o&RN8}HSFvcdXS4Mw-DmwQI2qBA5Xa)
z(@@lAJFsfY$wFf=VS{HEQkw!xmdvjFoXXbi5K5U*c|t$pHvQ&JK^zHNsca_DH_?I=;U$zwSloM#kLltgO?23I0JY>K;O;
z$y}yW_-UF(_F*r2BaB9z<^A;THwzdz!_i}ag|<-(Quw{uF57$eu>4nH18{G=xw_&5
zO-&5xj)QK6Udwzre_z7t#%W#W!Nv+<41$Jx>~M>ZiY9S8*+x;tWo>qLzJ~wdK|DyW
zDjOZ3nx~LWt}O9w*Mc>(Yq@o_>}7`&RoEX^bPCL>pTnr7lQZZwgw@$uQ}BA{(w4iw
zT~g4|tA7any6w6@_=k@4!kXG(_4%_}WmBkb2@tgOy7FJcF=*4z$fOZ`NrU)!zj+5{
zHGd7J{XtHVCpJbR%#cHFkNoK$@VubarEz%BQTw>~bE4z3{kb9w{lB>KNWz1J$S6LK
z66*JV(M)XBjMqq@Vqs3+zUz_WC
z_g9Iz`nBX$CQnB4^~B!FH+$9&IXqqz<>+k%9l(WPmVbcY{Szs#OlG!;Yz#u_cz>+;
z>!k5ZPa`h+HE&sbxs6BXs0rn_ou~Y-em)C9!T8nYMrihC3aUh>d4S
z9@$+%#O>QDT$C%fxm2CrWBHm7_nrwbsx3#Uth2x!-}cy?xbifWtsk9ZIV?A%nttGi
zFJf}qS?_~%d!L>Ow|cDD-fsV>*k!UGBc)kPMmi2RfG&ZMG#)L5oMVUo_r9Q2qg_%$
zVN<+3iRg~xWo7N6+1tbBG8d3vEH^wpu=BG<@WPRqdR}P{i3+=A;+t6)Rp>RY-u{KW
z0xh+tzW#4P*t}G3z)2z!rwJagP{uW~C#ZWqJIIqstrD7J~H
zO!@qF;r^-7nINl)taeLlkW{$sKRWx6wTRnJDPf9r8b7>&CUiBE*EU~ph7S$+Jib9L
z@BvQ47D8_f{lqyXWj}<6;5M2+z`(j{1j>S`gunP%whk#T{HVL2l2LMjP}kE?4x00h9dU
zOT-J}Qi=gIAq!AdUq=0aNww`(qbYj^Ha(MMPxiy*>u!E%-9#~VZzA)}Pj2@C74RJj
z*p#!WC^@;b`Bp@3-u;Nbt(8GYgnw}Emv6Bvv>Vo#)+ambA~aG)hyuus{i<5VQ
zMBfZOWOY_$TXre%qN~evAZ~^)N<@c*dAYy!Ujb-p>p&lOxXYPdUi&heOQU&a0rkl#
zjGCf`GK3dlosh|vj_-39E93zc5$7hp1B)X&fb&TaY2c#t{L|ge44If!-=^O)YP+_q
zeIcK@^tCqQRJ|hi0eF^f+g=K-rL~&(f@j#_~$2aeN16INc};?admYa
z&J+}6W**yBDxSJq|0;-ujlFPqHI^-*kj&DR>jAB!9hoeuPP7(+-mS
zq4;yIKS{97lv%0mL&xd2TW=EMgP)jIYn3n#O|3EQpLuk}#k*q&c|pN6J~83!1jknA{B<6AiXkI@@w-w!0TA*GFed|k=
zYwGUj-v)p+QGE#mki98={(KRNj87eb`1bA7zW(#wVcm)oFA^?`$?@EHXh5o|qta>%r10!r=;)3{CCmfnvKsa9lpVH>WXTs+q
z3d%zN2B>;%un({6L6V@KfS6M7H`q5=2eTDHrgO_Y>MAOtVq#L{vCBM=`m}V4*sA8u
z^}Hm*YH3;(0iyE(w(mNk2qe60>-CNkyE@ZWySLmB->fpS@|2X6FJHd=_LC47Pm#?_
zNJ#kh4K?3xZf?%S#pUquP`lP9%X5ym256+Zx*7!V;zC7g(4Y8_EbA(A>qS;I7nPMi
zpnI-6WciD-=ESTvXo6+XtSa>!T@O`G4JmJ#HY=tmtS+KPh(lLBjUAtNcth$(kKSg3
zS(PR^`sI~U2);2#DsE`S!l5wLzXfvi*K+#CU#4czaKw~iNYrNpJs)zPw*{XY?3Nlq
z|FKM~rr)gU@+vz{+H_Sxg$7eV(Bb@ThCQw!&qy?N*w_8is?1vt8rol9qQ6`f^*F3=
z8EsonL7s8|r@g(sjg5`J{br`8`!~;i{`^VX6ivV#iy9jrkBN%v@NjeLxbAra7yjjo
zq>PMqi|1wZ*1;hiR-_;@b<1D&6;%;OhOXr4Y51NlQDi4SJv|atal6PWKh0sAnHc@W
z+7Yz6jG-S=x$4{!-DXvNCM*xIyIM${lH}+WvnckN*Bm~C0Ye%5ULe;8w;4mJoFggh
z#ruh^&CSiFrO~JzOi|g+a&pmqCWxTxDAEDpFJO($(OR|;`N7dzH|)EM-DxS@3w3Gn
zX3FPqZh6_JwDrA?GURA#rVR}wzqj+&W%Rm@Y#bb}k9U^~b@s;*j9S$@=7RTgMXJTq
zrP|j+oGUqbc_~9%FWu+My{XgFQ#%=ae{=N^LnCt%PIQ4W+EXWp@)Y@5Io9J#Pj{iv%I9_``{9d3Q|hS
zt%uulr-p|=@WceXS5}EJP&&nY&wo|&yt;JrNi}#JzWCoF{0ld{i14}6O+orEKeX6h
zq%}~3961wKRf9$~iong!PY(0d1Dq)ZDn+;F+f8(g{sL818Oby
zd37MCpJe{c?d=C2M?Z9*kNo|zZpS`0Gc_6v@rX(2{=6XOpwJeuew{k!Sd|<+n89vu
z%z;zxgrklr7_S$4XNLCY_=SWw-b?t;=d!X1DVV9CSm2Dj7CD2L`}89%=mcm~SRi&F
zu(lr19m_OO2viZoxNY$0d>k_O%$Z6UG-3C;5aEj@6$O;n_Vpxqg1mBL-H9DV&kB9*9+6R@Fgv
zqRyVaQ7Tx1L@d>Z0it*a*5C24!1US;YJ(y1tTQMFZXhDnwawi}zD6>hnPfbQq<(4k0Nma1tJY96p77UppLu
zI;$bXg^nv7zi_a}l<5Q>oYYwQ>nXZpQtC)5|gP8Tq&mZF}3z-@xO&E@&WA<(iPKzT1z9;;HkW9VBC-o
zI|#o@2~u`@1KtJp=w|1S7$0W+*Y8HzR0)T8SCrkhMD5d|nu)Kbd&@mNi*{uUR&tTxJC}C_
zg{BIUuatx`emWZAj?PXI?0P989Xbt8z)3W7vPW*sT
zY_~(Ch}Sfps7JfKZC0yN+=VQ@2ST#iIuEI?xlgUpBtT^+*KGHG5dTXJj#k*%Y)^xm
z6$@3*3?MiqYwms?cF$&9X?!BH4~_V=WIz^8A(&=+Sjy_}*5L-_UL*fRXy7%8JEwG$
z>jLFyk;6{jJ(lxYCXvXV<#iHTyGth^V$}A*T+rFKnE%jdy1L^)0JTLbWZ5hbO(bV@Jz{9HmcHGoWVV(%4cK2B)`um0m#qAJzFo7Ns*5SR{qq
zgD$HPzUD(-M86C&Ht%dPA8fnVDn3**`T*X@mLrT8o3Xhe{0H^ynm@4xuk1`Bo^C>&
zUJwhU7+o@jopfa;F(E4>BPhy5;>=A;F8S?7BsZ^#SDyJv)W{*;Qj?>JcXA)gfyOZQrvYRT{6z3^tppzFkSz&1ozO3?VleV0sJ$;eUe_!XP!i+CHvX0
zzOOk&yt3(Ge22AJ$sHs
zIpYlhhX4T?({*hAnQLC=vg3m+zV>7w9-JOB&`9J2909yaHl>6N#SOm;YkvA}6;H%u
zlq1ZQ*sJ4EMz|A(yb1X?I}_V)|DFsSl8*(6L6OAd#ns5~Q2(VPb$^Ltq*t}#)^ZKm
zeae#pL$eJ4z(*2F@@{PA`_3}+bAe`)OecCk9b#!)C<=Y>CXoSW5
z6}1x0jV%cD^-^SkqDJYHW(y5s;cOT8Y}VxpW~5
zxkPTX4E`Po)Lc5~xZ4)Wbg88yv|U=FCu)Et%#tb7+5}ID3Gr|gYY8Lx4htgJl2tx}!+itp3I
zZH@Jez}2j=`jy97AJwI&kE-fas(ovNQCCE5Ek|M=qHpA`MU~2_eEDK&p9T{Gc6+eH
zW&%MYUm%E((qMu?-{*3wKq;Grg$046Sgn-DySG!m1wju5QK
z+{VWF&D}tKmW+OqG|O$}+mDVGDpv0ilbA3b(s#{wzM=s+0i7x6ztI2*xzpL^yP!!c
z7ZVc`@SS~n+s(ho+1c5goE#$J8m&?}IXP3o2KUo{mz2o9-715rf{TSjb?>pJWSA=B
zK1-9kvSpV>iwwo*FU+!XW#cwS-OTGEkOc31<_QsAvnZO{ySlm}^1{NvcmS7Ii{*wv
zqNKYOonVZp_33W^C2cP*RH1P@oX=G&E~Bz7n;sh@`Ha{aT%#HN@Q%*SlJyD4Y<%Ct
zS5l=^?4fIf)049hMxs%Z;)ccW4d9iKT42L(;)(AIXQ
zUu!{u#o>I7OdS>^%|KTp{MQ5|Ilq6~%~xCE;^ML-+koqOd``QPva+^m1=#o9
ztPjXs)ya|NPl^`*MkO8Hqq3#mxfVUWBH56k+l1JU&p=atmvlXtRZ>v_^EfD7-QBH^
zR|~qQCnqOgz90yH(JRa3a{Q%1w|oOSZ}pVcm#|(kLD`nse|wRiq=-Gg8vl&Wa`eQWW%-d#v
zKK4&-`Chz%g@m{0(+M-wzYh%H3z`cs7|3-n79lvB9H09b^
zKWvont*o4z-yIo)2{lT0#j5b%(%Uxu)YkV{Z9kR+u=>JcHsKmMt{%~~o&0Co6JZg$
zRlEl+VPcYI3z!=GaiK;qx3!7Lf(a}QtdXH9UbTlo!v#9>{Bg?nTs#aqd9y3_zYrjZ
zZeB=eRAi)a#-8Bh@78_|ix5R53$)fEShkAwy!UQ}x
zQfR{kx#^~DyHHo&Un7sy#P8?W6C>lBsKQq>6+>nL=+LX6^Jj>YK22=JVi(zS=24c7
z!yK?cU`i8SUNehxW=jEQ_#R}lsL%thXUD)B_UV9f<(TYkG&;%`7q$-xL(_z0!QLS?
zUshoi+do34_uk~R6gEkPry7f+y#5GvE*`#}vIl{r$!gX$^5bi$2T(%3GN9w>
zuq9H>UE=xcH#~95iulysTK73ag;~5R-KO=65Do$etgn93a{aZnn=qpf?>QNek%Fg)
zto{>|7gZs9ME!m?7N+?n@TQdJW=yJcrO=6~cAMnE{qpyqjc{tr_DDFQCNWu{$a~kL
z9rzREa}!Y^&1>pee2&si^@@ET^|^H6QnN-d-!=UauT=wHC>Zp8GxB)*njp2hyhkza
z4@92q;4WEu+6T+|r*7u5M<_@cf1p~>QTXRCd*Y?p9p03+nJ)S5Czc+KRI^<
z^z9mYlMbObUx9#6WJWifm2~8hk7#MqeZsXi9?A--j&wpQ>7O
zt88{AF<_;QCh6`7bU0|xARS>&=VhpLIfh;!MH9kg
zgQroqFVG^n6@81nX;);y-|T5{;EVw(fc(ES%=
z56YX4hpew?VPZ0*aNH>v1H03
z9>9xm$8H}MjaKNs9cYz8XVoC!YahdS=T*y)Xke`WA5r?Ia$#fkpRsDCe+A6zW#h!n
zX=!b7#u-OIt~u%aQxSM@M4-O0Q}+Qq9(N7?e^(gMihb=vpUxPA
zphgz(|Aj$jzXrEeLrfZ+W@E^>!-N1c0;G0b#CJzl=b;i``<_Cwz@%LjBb(AW^%Kt-
zyDSyBICIAWJSN_fm-(j?a(xgFxfRKzige2mOY`ucCk3UDGtf;mT_VG)9cuzPUNH&d
zr%^#2=18u#M+xEH3h~-*e<2)q1hB?fNN4jVK6igL?1w*~Li(MH!ydkHkRoQHafS@m
z`Sh}mjTn5x(0Py@uKy$FLkOL3(gfW-D%l(Z*q%-`LQ!4k~cV{NQov#?ic}}T6c+Nx)Z7F%@v_dpR
z9Y9X*0Fw}(zNHH(mQO1|e-n>f|D8@?YWH(|!=VHMV2=zO`%-HJ3oa>zP>z;@h==Gr
z%s1j!T7naumEcE&Ijydw1L)jWk8nVadb?RZi(6U#6|Cqr(3{Y9`2?)d3xjMJJsNl}
zYL&%!K|uLnL*{9_iG-845NnwX(m#wT&#`}z5=8&iTX{e2dnn;vH*8#dX`Fg6VpFd%N3wj^NhY*j2}c9Wk52@&
z#gfeUi0un4S-unRgX+u!CF;?v=S|Ww{$2lFV~$M{wKGv#@C{@I`vErN
zrN0s>&ql|XHA(smH}jupuPP|3(4
zU=2}R3OW=$&qeDV-;=+xfC0yP1CAAy+d|R!+aLdXzWJMYB=;n0CRq-5F3!pvTSkE0
z|4uIZXSs#6_I{yA3y;IPDG~<-gu8SK{$%95H>MMG6n$XjuTkXZ#
zMAS11pi6$hBzuJG@dk%NVQhXLG6tZEU6Yzh1V9Kb3xyWr6(#~YDuq;SNSEfXRHBa`g--dPte1P>UzkT_In&GW<-dn}9$
zu$Wy;<341NQ!1e|_m1UgxN0sO{ldD^;RTBsy->bATBJ3ni!m$K;Pse)1(fvX0~iNL
zmC<$!*D(9U4J40)h_i^1fO;vl{9x$O@i|0=PDNl9ISzL&uHfG7egFJt8FMHgjZMY&
z)jR?&&5TK<{^%UDk%Lm!i+7}Q-}u;r@EfXJB!pPCfg^SJ0J5R{vwzs~r24OP1-+cGN`cW&aN;wiKwP6JLlXttbP;nh&%0{&mZ!
zSb>hJeRhA!5I+gsoC!k2(r7rO*KMI|R;zYQgZdo}74A?SoxM9>7pu+tIag+1k59fs2
z=;nnsw)Jg??MMg$Nx#w$i-!I&73OX*Dk0DWyk$MGave@y8K*1aw^g+~q)7BJ8Nruf
zt5LE2>2Ea}%1j4B-ynq=tfB)&Epfy#&rNAJplSDue&^6A@NQ2nv`Zc#OhI8*h2=~W
znDq7nnx4MpOz8wgUyW&fpjOf5T7NX*TR`PsG|(H@lV?9wvvY1eTKyf11suy_
zbt_gm7!2iOW)2%Q-;j)1@Jj++`&*k87ZkwI=TCsPzuJaWbC@*
zO}I{~kwA5|sN3Qq$2=#dtBZ9)2)H}NPp(XB*YV_+89=LA1X2*`;*KUiB66&9Fm2c-t$ObB(RK5}}
z3--L4!3M)!4nYB*?0e%5U^58&H
zElDm6bsp8)+Er0eVLqI;d*=r3+~#az!owRX665+zz>kf4Vp=NlgWk)!o_PzL+f39g
z{$yhi!%L-8kzUoV{)ENuh&Y=w9B8f(x5P=V)E?J9b9p(E$$Q-F44wjEVqyk=b6fT>
zpDxh^?-W3y*Y@}_b!R7o(%hArkDHfkn0JK3S&-obI_~}>-%$vyk;6B|L(}hDD?3s0
zQU4f&r$Nff%D$Jy`q5zc^yXwO6HHSAV9aAagBi;@o4N7JV^BnX!iY~ytTXCDjQ9lx
zwC3(dVD!h$Nc`x_1xL?Xl0NIZW5Whhi_2m#(Vj44U~>47a5{MHFC*vAd+ondaGP22RDz+a`Rl*T!5QXPqvJ
zrh_HVM`X#GD^qb*2{E18b0tmXT<~^-m%2j8h=|g%vY%BLg%QM*H1M}pXn1F5B9cvQ
zo1hOpCBDTJ-f=+*SLRdmEpJ9hZ)88t$&4}v+vdr)io^8uFqd4
zJCdi$S&xKySJi9z2Dj+m+T6cp^QILBdJ9GXwA_AgX^`E|!I~v7n3p#+f@xxnRwzz!
z41#42cRo&*O7~BZuVuJb#eez>m1zk3dQ1+ZSLQjs<7804qYuX+S5aHtha1ryw9UgA
za61|R4dHgx7gCOR*08;~@N_B({1M0Xlmk=Y+*iII+1)kdEND1?aaRg~
zoD~}*k(kI$s4v#DaY!YwN&n1gR|p=|>$i=u5Okp{(*UGyFQiXOe^qwoRE%$PV!Z7L
zIvMLdf+ngM3O%YuOPe?^r*VIBg7M@a5YcmgxPme6JjQwnzwl0DhjW&Ch!}3qIhiNcGe3cK9bXIU%7o_=rG4MZ#*dRz=~5TT
z9+i@D-nO_5o#y|2VqYeS44IC^q@dw!`YLEKjBRef;quL5Sf1oVNr-$r2C;uQ9nOg-9Mp${0_0#R2VgvE`z
zhAZ)RB*8iO!=Ux)Ks-LEAunaxkpue}8cZ3^iVfa05THer$K$WO1CjMLwZY?3h4_`=
zHjXetS7lq7PiE737RdZUyxQ8yj^tFm@rD+twDi8)^8`6}tXe6y-?8(`s1aAP(vbwy
zO+M~?K1y^QZi&+%l{Fh3k+fv787|!%r?xBiobW^Fytu-EV3hkFM*kbQx9i0ju-_Bh
z5FihiS2P{QprqePR3n!X#Cw2nl3M?sB%Y%L+I<8Yl*yzYIQKA?Eon?WxJ2cCzqe@b
z2zwo^k8(o5EVnh3;V#P^g>N(az--Kd5RdN8?JaVt|Lhh#ru~|Pd~&)m?^NbUdjrn!upPV82?|Vn1k=Q3n{Muk}!905j6xaZ7I%GyEcp8D9rd3nMK8fQGe%h
zSnC8Zdc+g!-An$kVoBu~6zZctpJEjv{kj=^#xJKXHj}Vbr$ro3Cb8~tL!c;vnf8Jct{!F4+tw?azn9))U{)VIQ-9Y;8ia7=
zuxVE8B&4Uv+KsxNSlfI<&;cmvJ*B*g?-=+I2D(RP`!P<$swrja5L0S-Fzejb+
zj0_mn3k}35l(@tLzp4idTcXdUGxBtp-`A3KrCum91NR|bJ3>#A1WN^e_0dO%f3CKHvC)nCBf
zYPtMbY_U7q0-sUksayzTgkC@WZ`-J1W*K)F6o^`N9>u?urUQKb>k<94CcFVVN|oir
z@(>fB%kp%@_z(Gl*O?a#XhP9HjUmU{$gGVcHn}FoZsl(WgcE(K9eN^uECAK}T%<6#zDP!SQ|E
z+1YP_MA<1Q)P99oJ@HLEI>H;+LBYp;jPt(RfyCA$>$%FGj}k}Ou{*)-UBw~2Z&vdj
zY?KEj3cos&DR(LbP5+%Vmyc*XNX6G))^p;BlhaU%2NxnXWt67HhzHO(UL_ZsaiEKmJw57-{$7gK0
zOv}1bwjY7^19CdIL29u6>a&nLFtwkEoXJ1>z+VPWkJ$R`77JAYO%A^*UtwjnNTxub
zj=T=Xo^Ulj<7;kXw|A(y%Bk4yjo%r2k22do?LlM0SN5)e;^7QyY*EmQ~GXNeK7EQvpOu|~flL`6rHk+FR
z&P!t6%P|wi2@}RkcPmInt$~KcW4fOmI#4f6R1NmisfvhPI{0i8fYtWJhh+!^t?T9g
z1t`jQI*$_{~Af|6%@YjwXWey>gn9;qh>+yDW{TpwbMUURQ=%`pW?%xWZuZo6NxTJn6sE
zTI4#mlx5Ge$?zY-rm#mr&|6-5z2;9^?Wn?mHjN_#nYz6z1&OG?T1|aN&oZ+;g#_8C
zoF&gA5>s!T!|}Ak_I{W<)gGhM(7yCAh|jM{x2}7@dry;3n{}oN<~Z`aE~B#A==B?p
zDvUdIdC?Jdd^b+nY)SZW<+TO2&$G{cVZo*$(nvaQVFwezusUD5F;(#5bnSCfW&Xcg
z32(HB@nK!u@*Onr@og4m=q-FbTC)r;8wCz^yVjF|`&3R|erejNtm5Y)*-QpWJIL|}N|n^heeAE|@-7kZ
z$#@GZ-bDus&;JmZ^}|3SuJ^&j*YWR_#e}-{@H1(b%GyN%IVs1(_H!&n%!|^>unOYn
zJojdrKB;pZYBL(yj<-DX-;XnvxL}9$BZJ|aBz5E0w{pK9+wMnv{MP!+_oihui$B3{
zCVg0IyQ$;i%j?O9@fQ_cs@u!(snWB+g6HDTv7;`0S{T8r_355Z6VzONr`X<{I44gu
zmpftOvwvSnC4nQ8PLwP954f}-^{0f`53i(9+QSw(-E4*)%diVVdAhn>BTW}1u%8~1
zc4af(hyNLB-&NEr(lUQsZA+a=@F6nVH|&ZdXJNQm)|dMAjxQ6(|7!{_-@^st^VM$H
zv#wr^l&rgY+lK9w+_8YQEFN*)4V2WYVcQd2YJ=r4!?(waWOL`IFk2Z{B)QZ>)fC{J
zqmog04=c=xZ6Qf)fCAr1$JVLy*C*pI4gI@HVtwD|6P@Rk^H6jvh4ENEenVPo{ORw3
zHf?3e)m-e`(>*&pPsA(v>t9g9@A&R~PcCLiTh{k)>UP9;eWNodzUQYAp~rtqqVK7P
zSpMX@O{LAbkr-M{mQZ}?I~Cr1?GpgKg?Z38T}!s0b?ryRSq*nREA$JY)g{F2j8r(1
zFP*@DMe{pp8^(QarrY4)u$Q&B`S*!zqv-^1L+tzEBe{Uxnr!T7Nn%#!KrG`w{?|fK
z?=U)(q~GA7*qDwGWZ|YZ_b>#rFjF4?6CNX2nmSnB$TU4e65uw7^n!IX=MJ@cHo@8`OnGjm^`??>T#b?ft+LvLt2V5E!{k;xhGACQW=ewxSV%R
zjdd`9p%5b4*`ZU3s;7tAq_koDBDnS^Hkc)N?HhAINa*
zN9_khrY~bbsxi9JbDfraJknWBqtAM4otU;#zdxY&X!lY0tL~{^p8TB)UBmnkODOl-
zL!ivhKAo|*!VPCdC_mn)87(PgPv4x(EII1egQ4Xr6=o!!W3YV0m}N%c-#na?(s}dk
z1pV)37uP4Y4`CO>`Kf013l&cXZLZ8j9lmRFyl(bgf|)Q>6vS+ZeCQ)bx}@z`WZZ(r
zy~L#ya)=qVjP<()^=OW&%kwE`e^**i`9^qH`2*B)ss^y!y-XLDLZpq?pqrFKh{dfv
zk()XV7YeuKK2EJ$4Z_~0Y8D<3)ZJ>?aQy
zDI@IbXW@yT&UXIZpC9xY#H*HO&vsUf6lcN4Qi9_7rtT|G8M|{u(eCO^ca4vq$zP(B
zZU}%MJ+?MGht|=DJy-YARINsD5moS|wVA55F)FsuI4i;1XC8mQN97mq!PeA|M+nc*
zi7l5#9dB)3Mol@w>qT+nF}KHg8QgYK$F4fUTO3sSgy6?{`OnD2e15|2kdgDs-$)Q(
z9g|a2&%NKmi6lXJ94`zO>J7;sXaEuM-Q8VT8xpt3Qmc3Jn#T$NF__cu8MJ0bVzZH<
zXPenY(Lg`rdd>biP1mM#?w1gL)2?B=-yae>BvcoqD^DElE_%sAHx|}Cd6oX=<;~L|
zP1}Os(#hJPxGQ9Q8(&a_}F<%OcsHbRrKT
zk?{vK3@{bWC>X-hnfwAPW^Zc?Sd$>ek7;=F{BW^hQ&STkAK%BL@^1(8)Gm<6ynTFP
zcE6L?+V}sKppzs2SiQoE(R?HV8r4WxfP_=ymBS~mfPhuc)a%ftev!yh*Wl4*`Q30*
zZMlP&_Q(cIG@UuuAd!L3BPCumTl#0xH_Mn>jNqC
zDF(r^N}89nyjv}=RLPYiKGYX|qvL9(w@)}nTM|D3pL#L4+r
z0e=9%FC`_#oE6>2)wr>W&D9R+?*aNf=Fj%EAUwG+va+p>*|tTONC*uL1!tGZf=WGI
z8&~@;h+c3zJn&;~K$E#SG{mGb+VI5omQ`|7svU`z$YbBt1?fK32epoZFvnL&)$COf2a#7u3qpGVHt0t046P;iB
z*MBVtZF&|VaJXMUCUV%a`*ATR(#xpyp|`Q={kBz)x8=#rrh;hmY$3zUUJNlw1-sPu
z3Gr|xFSkVz{Nvpz3&d>KMFZfl*-ahY+7lT+mqiGd3JOX-porLt9>uXFb=Jz^pEdWcn%~e8L0}
z#y_LJrqd-SHT=1cU}{|h$C4fPG-4LRD&hDh2Bi5x_n>NjeZY8$gJxmz+2&D?j|M{M@t1%~h+93!L0
zQ6>X*bpew&X=_!~IF|CG&e-C#6Pg%Kn3Nl<;k=Kkvh%O+h$Al6+)iL6W}UBG6GSV(
zlg$$6k0~Ysr_>eAe6v%A|5=PA0HgZ%!?R!C!&IdR!PIDE@pia+k-U|=?da^FxPvbZE96?ctF1d6Ve%*$2APw>1L;z
zeIw>`fa7OcZEa)FjCRDTGqMjPM`~-8c(8zdf}J>_n2o8et^KD3`fx>)ifax0(9jVN
z>FuG9*B5add@|ceXn*Q27X!n$ZqgS@fxHU1o0+Egr&x}y8wIj&N
znwP;i9%RTQ`Ie&;NaN6wE>(M$pIVO3%H}ap-P^q4(q(CaQ4Yz3`e}rXwEKR0TMLJu
z1lMPlzgN8En<-X*IN#qV-67>@+B;{K+&uv!O65F4%c&ohqTIEG>uz|o`-)}zN7W*o
zewcGPE*7hY+~f3T-Z@_{Se_>&hBJTgG*dlOuU}DKpt(73TRUxk4JJLZBkJ{6E%VHp
ziR6jgu5%n>#G?9GU#^&a%qtb>4si-@qHFNV3dx?%&VlB+rj-qq+!%V^SUuYK>3c#6
zp_2}Mtz{ImH$PQ+-s1e$v9U<88oL1GGk*=hQ{cOCttrIH|Z^T)^G34
zg9^$@ylXE&A{FH2VFCi*H+Q?c(QU3u!Lh%--u=^0Gb;UZlOEJ)nxU?({eE&Xr?T%{
zD;VB%&2oMkVN2`fC!l=EZUY+|8Z*x6T><`#vlg_;m*s7E37@aJxN$astnr
zEyrQte5*{DekYJ=EJ-4f=xtGg+-&!aJfos?Iwcg+T;<`%5eTiYrL0!T1Cqg>o}M>u
z;CK_j_)uFo6(rc-6KpLq$}U#B-fVMCopl7VFCigcX%FFNFJb)LY}L+9(B$q)3Z=KF
zN74B??|q~Fy?3A8RzJ1Jf&tOUh}C-o1eHefP|Z7Jpqyt0JP>i`Z-CFNj=stQxxT`}!pTW{RUmx^P#O^=qh{D
zGtlFbCe2_LT6!T(xrt!yL4DXe3!b`ZqUToE)-E|Yl@{FN19LLqh%=FdmdFea4S|Qy
zdjbM~z?87M8k9e9syd6!1b9q<{b6CKh%AyI64QR}M4C>)prwo?`zt_)=H%x3;XOM3
zBd$rol20otPA+d^$(jEa$BX|RL~m_sZS7yzT{$Vgz5Qzz+~|dG-#}?$B7yR!j6$LO
zv$oM18@GOb>AXBtXq8$XgC(zb5%?t}B)(jo`f`<wRb
zP$8M}@$>WR=;(k*r(^>A9p1Fw#>PgKEC~}pjSAf-(P-9x!F{+M9sMXsk_}ZllB;xS
zH~K|V66e1y151*r$wJ1-RzFY7zR{QToWLNB6spzMcqPgq!4@vT@^p3&8fp6xk8
znP_fl(b3b>)7Mul5288Np&pn~|W(_&D#jx(0`bH%wpY
zVYes;N+NkhMISG(@rjA~xw+xgPObkoMdHZ8|LtB&Hx`!17#ZM9{Qp|!08l3WpJxB>
b+hbV`3y@6R`qR%r!?Bp4v8ZQAw>$p>@brhZ

diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_10_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_10_1.png
similarity index 100%
rename from _weave/lecture16/jl_AHYinz/probabilistic_programming_10_1.png
rename to _weave/lecture16/jl_GcpKod/probabilistic_programming_10_1.png
diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_1_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_1_1.png
similarity index 100%
rename from _weave/lecture16/jl_AHYinz/probabilistic_programming_1_1.png
rename to _weave/lecture16/jl_GcpKod/probabilistic_programming_1_1.png
diff --git a/_weave/lecture16/jl_GcpKod/probabilistic_programming_3_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_3_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..21fdcb4c4b4fac5714cc24589d87411561654404
GIT binary patch
literal 19952
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%0ApL
zJ=tKjlPNRPm<8y;pxGCRlQ$OlW^*nVP`CJ`6b+=7O&v63ix|>IGoaRrJ;IW{_*YCp
zaIi`}D!nTB9AO}*FWiuWI+eqKcigby^`^t}`%gf2p*g+}ci3bJB(nf!iKR2a6=POW=QZ#eRU+9nz%7foa};*c21}$273S(91%qR#wm;4&
z?0oMN0-$oLD#rt`b|>6)!TK%oFrXmpiU1eeKb8y5)TwAqa!jXe<`mNTKxJz;9frQ{
z<4}AjQ2d<~dpKIChTG$vF-zwJto7p@K48hI1Ue^hp*8R=1noU8GNmZ?KvyP(Nj2bV
zURSr;pc!aanKK;ngzWvE0UVoU)qmdUWZ7dDT&D9ubF){^&?7
zs%xK4(AiaOp#OSrIM9=doZUaVi33_#jZP`BpePGIEfrRZpWuQVcXs6EjDcTR?(yoH
z8ovC|+H|jaLp@8XM~ywzNCZkvPuEc3Qy$xjvjy5K)&8n@8^nBk%~o`W0@w}w9Hb3i
zKy_b0Ha~r}Guj=WS(v9f_{#0H^X7T1`!VL%?}v2p{wzlnsIMaFTymi%N9>V>U>XIk=-A7Vj~Dl
zx4!O)=PhaxAe&O4Sr64?_74J+GetfALAC;NA_zJ!TffC>%6a|064ZnTTVwueib!RZ
zCZTu5A*DkDo-gvd?+?Dcw$<2=8nx?+iZ)8K9SoYRlb%0b^!;&1P>_!N_@0Qz(BhMx
zi%Sn9;0X`af_A<0b}0y7`l~o{RW^|X@IxO!n$hK2dS*|mYkZbjK9Fx#m*$(Cg*I8t
zpF_Aa7&+lVS)~<~E2pM##4;~S9}}MTRReth4g%8)h}g1CDHjA50U~Vi^|Ko7{D9Y{
zA)BR@h)GzQ5gb%eJigBXn2rYYC#V8$*RcZ5Lf?EGT=o3p4>SaT#7_R>$B!?L#dCyD
zKVIiq=%RtJAXy+&Ew-h%Tb`dR_T$Tcb3Br1HW=T54#j?1{>fc-?BLW8UH?8~2gbp4
z{Iq6hU_clv502DAJ+-+apg`XxUD1-rHGa3wt^J&cZqy$NiJ+gI<1y=xGIVIOD6rC{
zlQ3MJ0zun4z}r!m&(-m7X7z?b5;>Mj5D|T1#wM;Zphpu+&DhOyQ-nZ+<0%h_31E+s
z@X!u%sLKt
zr^pXk_@GN)QZ`d$jEJ}uEcc1?vdumRPZ@ri71m%F0j6;Uhdy7@LKsat3J;R{64&_L
zbfH!_q}gAjjCEX2vDrytbr^t4K~%u-Tq^Gdfuj1hiQmw)UAzTMu;*xu!@rC0HjA3S{tgLBxu8AQY7>3oFF;;IY3{
z`}M)5P8d~n{jC>dqwXu2=W$-Dbg4Le=7*6_RsDn7OB-gg5sY;@h0vOtriZ3RpnR|)
z8mGnxv@O?Ku-XOe;gQYmhaCVG!}O8rodW60gkjW29GuZGf8h@I6C1RR{J>!$R9?v7
z0`#^>V{^ih_vRAed{P|p%3a7yCB~h{M#n=OAPz
z)|;fnpjN9K3X^WY6s;k#3J`^PMtQ^&r2n9jkK7=e80FdAyWo9~u46EIB
z#;9$*U}mjf2s@Rf&ld9P^;Zj0vPa(7qACwKC)0ub)k8VJ)I_)dn7bKw-LU%u_~X%3
zQ@+>vA_3qs_C}_w=}~b>6;M$uN{zP^Q|l4>d~`P6#@*sU;(%mLG)T{_Fb|1QOT|M$
zhl(D)irau^LI^voO74>*u1j$fDxff^18LFKc0h39F_VMEn*|zCG|7EYDcK1(M1n&T
zQGwTDulh<`(kVZj2C&p?$Ar}If)M~aGU)P8s3fZs@KaOt`KXEW2Q!caR?)y
zwGWyTiDX#J9J0Wz@2!+zV?0qA+FT{!f{Fup*6+4XFaUNB8(6bS%*wZ`!X8E=7g?Q1
zPiqHSQ6L}WOYqgzB}xL)^6Vj)#(EhPLJg~uwz0$h%;o~Y)la-~oI<
zExFP=nTG<&OC=H8!AAc2{bhRk6a3C5%eKJ8=Hz{5=B7x%&Hk>8_~U5m?ggr#*t)&wQH9*G!T)}04y5P;Imr0+lJjJ
zh26aWIyrmWSuoFyjg|CT;H%YTauh-jk(wNQ47KgQud$ag|S&D0gFEF`wewZ}%VhC%*miY4(!GxI4Ex(063@b*&jcAm7Rw|S*|{QM1IW|wW_aKz|k*q`Y#>DexJhco7Gn*zJ(8b6hsQ#cw<1T
zkW!EL&RG0gNQYaz)A55s{8b>4(e=+^q}p%m)Q9hrc4OjdBHhD
z+ta_3pobKqwm=>`6{ufm+^O)RHR6|doYO3^%HF0T*g=3=Yc5v={`^S6<%aipW!qLL
zlG3yA>&)BT-m~mLa|+~pSRlDo{TtX#|L72TuWyK%HYU(@R%s|1=$lpQ`Yi@)yCfd}
z(W%TpEN?4MkyeA%N6x9NGo;F;$ecfG&RGQpD2z=!^ItFC@qc~*(mgjn$sS%4?i^Q;L)mdARe%ALB6HufvY7HG21Ge0w8?<
zZKV2nMi~uY{J?Vpvv$EfUsXd^M
z3(j?;yffd{402y|T)h&qt~coqccnQ$%I-oodcQdwd!0yRDb&Ry6dL=jg1H_e|1}dF
z=*l_+@&Y8#N>kO7E!3n*Dn^q+7x*qT5K^43D0vW^Saj%^Q2p$CKc%uzxFw7T$m=Ad
zx4V^Vk=3+jb^;ipM(w(W2ZVJWlBRswQ8J1_Rj!-x*W&}7U-4;<)vLhkdK2+5umg|_Q*bu+eN^^lK+Go_
z@NO^&UWdrJFA@K$wGVU;+kP)1#|*icz030|=|o{B_{{cJ`Xl
zWef8rs%K9}2Mg|?kg;xgyy>$B@kUJgz&DBzz&Um0
z`P$pn#BJdye1qj*U3(T6*A)H*AoYJLvZ$0RU*r7ry#s2mhwYy)*uoEGT>M&YvXze4
z{u2AI1g-ruDLcpdG}E+N`3>6`dG&I0iRqO7U9BmFVxCz#6!bTqWtGaqCbz9wK@3^K94aYY(8$p=nS=AEv$MXk!x?8z4_X`M*?L;WM+9ZwYJS7#s$tXSoovtn|`Omp$
z1`a<_;6Q68TK|6fhSZAWiFIVVv)=&|7~d|E746!XTDDh2F+g37waF0)b}zG&PV*$@
z%=)3a*LGIjB}R2x6gxD+HH0s2gyMVIH%gm4Cdjr^$--jp@%e&L{KguJTBwQ%TgIqxyZy`0Fn
zhOBc8%*KXlThbwY*U!piThQP1(D4QC(6rPvA6g1Oe8lr_d}8%6fGN3n
z%kah~3G&eRlEBJy=#=%>!WqGkl_0k6OJzDuCwj+C;J79#Qwpwnh{qN
zF7%UQk>GZf&6BZe|6ZSexih41Y~=d!v|jX|Pd}_wY5ZFz=-t&ty;0dMB^FGwb87P9
z?J^Aq0J5Ywf$U$R#@m-i-tXz<;?Zi_CeM9Cnhz~4PbZvwxaJfp4h;%`>(5u(HCd_s
zgtp^lGw{a6Y&juMj%s!JU$$_nhrP-;m+Z&OWzm&Io$@~8#M%H8QQQ*otX_V9Z6lvT
z04>!l*tPw)K|>%9_70?GvjRfGAW`yA%Z?i&+~1DylJ8>77*vb@#(pLLTCB%pLc(z2
zO~O&gIZN4pthSpY_=39NAKw^!Ly+e85WF|q-TZj)=aI;7UihVfJOJ
z**;QS(_86D%E4UW*i~^!;`PR#;zG&{MO4q#TkTjD=Q#-zJ3Egb+8pRB8l6@qVB;@L
zF5Q7cH$CFw2TM&qAk!Q=D(tki`D6SqF}ME&A2~J!$Sk-(QGU1l
zLm9LEpI6xP8rM_-MSi(HFrxf9{Zn1M?eR}OeRAVnA;bCASiLU0A-fh(;Eab-O>Rv3
zXbRSTj{Wy*ABBp%uC@do4`*0Zxw#skzM4`VXaJb#fCh`JLk0_(=#2`sfin;oOzS|o>E!-K0Uc&ZS{GAm7#HT7yxUNfzfO~3j?83Ci&MB
z?NZD&!}poen8pIx;ujbe4uWsoV9XAIwgEum;nEM!UoW?s6%HLQMS(!8M-R#3W0++-
zkMFX@7mok6?j;3BuBgg!ktR8ZBhjx_ZR;2wC-U(O#awa%jMv+AKYp^GqD_jL+#TCM
zA;7o}+fT>*QJ?E@Qa~n5%{Xf_VD5vGsSZ}*Vz>(M)ooOndV*nmSB=_vC%*mijE}<*
zT5~lcc3d;rq)Ld=mU@L39R^hIrmQf6NB8#*_=~7I7~?Xw485wg-mZ+FRjC340NT>(
zB3Gus**Rx~i2%j6f~-|>x^5R@5WQjifX*XBvvY~vKO^AGK+Jrxo~*gJq!Q#a;sdRl
zZ{o>b*e8h}ohc`%)3#WdxKl5o6)#yp6#dN#pYY7idjXmAIy?R2t
zUmFM+a#03{&N;%)PP_43pU(_`?nWITSZgF~WBdujdD?aEV{r<|ty*61tklI{)bplk
z)sgB{9+;6(r{|-1*rKZEb97#VeHG#?;orX{;~31r6fx7Crb~FG?UHVu$Q~XulIjMV
z44Y^px8MvtDMtKcl<@vEwm;RGQBt&&c}-#g?YrY+omNJ+m)p$HcBo93MycMA!65_sG?5zxgOpzV&((u
zDwGQXr80W@Q<}81x4Cu#cpbpVMtD|uNVD5#M+J-UgqnXLVU)4G&)lNeg%5OfTuL{x
z<*H%R2ZORg=G2Jo#9uB>05`+hD-U)uHpV>-O@9yfBCj?Z%Mb
zgu{E}q{lLR_apyP$I}^@&1=@-x?#WDnh72W@*Mc-{TWdi)zEA|k&w
z^L0KY*xQ1nwa3Ey5`}*kvv&&oz^pPv4y7u)v;@;Z)xq$~>Rl$OJ%C1?W3y=Ea+tN_
z#_wtxR^=f-L}`EkyRw6M-#4dJS9&OkCX*7>*;+o7oW
zE3{e-}L8a2_8Px1FkP
zMf`H=Q1Zal!?LB%SZQQwc2xBFqr6RH>omd3@Z%G8Jz~)v;&6>?zX>}IQ1a-Q&_6O&Q{x%bgx!*
zE*5;y8T-JOlVdorJziJhkjYe+HwRI?pQ^cB|FB>Du=VjF$Z4Z)9cXf@8MCEpqcCXu
zsaM>2dMkw2p|A5ED@j_Bg70JS!)AChA|R+jvrTjeJ@{^4tz}VjMBvH4_MMKAfC@AolR65op$Ohvne1U@w;&)pqx*y!NYh_8Zpl&}=yezt}10$ON
zt8Fslsm&Q@rqP5m|C+D`)l2AMsKYBS^k~~poN?205UXk;*IprQvX0_n#rw>5`(E(B
zBKbcAh8%Gy5xjtI{w7%;4>PG-sf|W>7_2D?E^0qMsfwgVcsaUh<{&{7%T#7aIk?zm
z@vu&bjz+gpPS6!_QC@58i
zO2G-)`$HU;7w>*B$R!L|$9;)C1IK;QYAYO)P`pby8z7b;MrHGX4fk~Fx_AIw53
zwNjl2n}6p&GNj?G@r
z5_oAmWCs3&K-}y91<1Z2%yH^K5PzT6JLvJFG&5j9;XC~Pgo?odJ)NK0E<>M)n)9P6
zmy4VKM;~cO#(D#AejQL9(0hf@Rxr%_1!`6}&{wS~{)Plrt%V?sJta60y?Gqgcfmeo
z@YeyNPlR;GT~h5#(>tmjKeptwF2msi%k-8w3E{m1+0Nbntuda-Ak=O+eM@HI_~HFU
zH_eDM`X;)6AXFDVADI)T1C}KbN*5vv>Xv)Ny_I^ZCE<%R=Nv;h0?xi>0Y}ktJ=ah`
zK&O{d%+E`N^7Kk^VI1BgBo!5j_7Z|0KWHsUZh+e)2m|=3%m_8o5F64oiQ>Ospd+C%
zOteVGI3Oq&>?9264E}K=dLV^%2->@s2~jD437avwg=%B;iei?6dgu{AU|=}RypmkJ
z<1NCyVqVb8ZKnn`PR${L6FjIu4Hg^($DD`^YDI85dk;dT0D+8yqsRrkoX`qhPA}k5
zqmGn3?vp7rc#agpZ_T9?WZI;{6s4P`v=pUmcxo6nc{8Vy-)uPHO!o*H_3zV$e0`CV
zN=nJ0j*@c0HKs0-l0pe`;=}@BPZ!p1zL!dhx-XND8Y_O`!j5N}2yDT?8Iix`T5Zl5
zdpk9#iWcyKiKEO=UI`1*p3zc)0Vy+TgQlJaO2mhq9@`=|O?2*Giid-BsegCRb#MFS>?nnI
z>?hJux|M;!JV$cU^0c(2#ReiWcUcmxJUmxVZ
z&Wlz=_lGMpnry$#-
zU=g?HDKAA+xp1|3;#+QL>PtsEb08ms#*M;y3T}d_BRBaAIwhQ*K3H
z#D{GvFPT^5RC+H!@8K)HVgGf)jRy`3{rf+SoO?9XTN}rxiy;-0N*!u+TW-bRsN6Eq
zMNNe;6}gK@G^rtnW+I^rPNX>|MsZT+$Yoq6w;7a%gwUAE-Bja#XUt%{&#d+S@&0qx
zdjI&%KeN`}`?vS=Jiooy{(e6z1%#Ct6onG2u**2M?MSC<|CACleOj3y@eKOdJannW
z_sekOz5CN?ei^p0bq?Ge87M^T-ap&rX3kMp-?62xyXl~6RfVC=XWcZ>k&CqYVdEP`
zm@E=(I-;Ry%^I#ebgFrm)tk@dHa`tD{d6{rmZ>J`)3ptgS0Bp?dZ2Bf=||5t1R)aj
z5tPI$yrmqv)6jfe8V)h~-78Ani?G<1K|kGAk8w6g%ecbhkDKvZ6a52CiBTz}6ACfA
z)5f%Y1~B3)d~I!QP*Jyo6?Ina5IUj3`wi=SLUE1p-E~rWKn~gUG^qK#eDxmic9cs&4T&UCSVUf
z_tbCY#UkIc0Rf{U-wN|8raod9`yl%2ZNw%9k5AMEsd;U+MOwbF_43+1taI`U4p4j)vff*``uY0G#k|YO%rCfP5IWIYO;T(yj93$ye{--^BRxE5vcI-Gc$8}B
zXXj=W*ijPDmUZSgnsV9*?8qcPG30s^c~w(FZ`FC$)Mja`M4k(!o+mrh@JjtSb&Fg0^_hW|bdp$WICf+RyzDH9HC~<@2#OZIv2HKS
zPx8}Yce7sgKqr`I-|iYcd#g}
zn(O#x5=wiWojn+stEHt?&2^A{QCvI={VdjENYy5&i&Fm0uf}IvFs_RroO`0
zM(Y0i^>A8`0e0O*=_*gutc76v9cl;849#h*LW1my$z=9`1`3AI
z2@Ywmg0E`J^H%K^-#)<$q@Px&NTpHriSRPvKsm_S&De|cYhH1*^4F6bZk6`ly(^xV
zmX>B^ArVw6m9U>}5qmrn!fO&L6d5F8h4Hl{bl8Z#1q#SSP6*#?*T}sQvL>dcvubD6
zXD-h@`n?H(pa}#`Y2{uV1?crYoel}K9HT}u0us#TKa=GBl(M8lTq|uO;YR@oM9LX&o6N`E5?#|ANfQ}M4z;+RN-jhE>y;fLp
zOBr$ac$~`1ylQ7Wo(L1158eII-2*1<1>kzzUvu^Fo|9z@W98QX9|FD=;`FaZSw-~X
zuY>G*6HU{dQJujZ0S9}+r{6HURUX!oz=C52-khAfj0nL0fK8}8J6lWnp^hWtZw6yZ
z11nm?xgG-j>o7ZmIs%xWgrw8yS3uGT@!#-IOP(8B0xfCl>Pm*rvmZQLv;GT|{ni_+
zUPMMlN*CM1tYi+Nga!r62zeECF4tKE$>4y;yM&Dvli2A*qq)Lx(NwWN50_X(=&ONU
z4?}^2%xHPt{l}{|E2Y7_^SREDV^c#zL(+p%*D8~;zF=*+ol#fU@GJp_^#9|$q!2Ls
zC76g%Kz)Ej)mFD^b@ZRvlF0-zzz9^OTI4O4Zs;71`kcMpG%WU2I&r^Te?9&K;3Kt@6gc!dA^-kcu`0IvWkvG>ZZX@|>hRYodaJa?-rrayP(y*ye3c_uJ1aesWG
zML;gcj}Ls^B!1RqHUx2#WG!Wr(Fy6P3-46Sd}3%2E}j@g`OJ0#TeaOMuY-d^rBI|Sqtn?
zHNgWgDdYi4@0);k15o91x`W**raMRI8K4_2c47Q)fghf#Lgq`$Pl4~%{z7f)^pK9%
z5a3;*`B&g91l5TYEJAyZARYj88iO^>;NAcjf!OoIvyGGh@KK2pi?uj7DR}PeD<%M#
z;jBF``;Dkx)=y3lhX|Na>SL8DO`nnE1cQ}OFCmEMyF>kVhX8n@{3!(;Nu_wni9cC;U@7`WykFM4OPehVDY94fm>pjl0*MJJ@_>CA0YLN
zdFWr2kQA1-&&1Dw?==2z+p2_ntBh5NM4kXJ_7K)KaK=iyJSD;G^^wCaJ{|i^LXD)=W`mHnI_zg!P4|GU3}!rcI#LGIT7%V
zkyU2N*2?N2#j@mjBC&;&k5ALn-pJ@jW#t+F>b3mevi$t~Q_Qr{J3&_E2_&HEt0K~Y
zL*^|hj@Mc0YTAjX0QqW*mxuA9atyLG<~IEi0Qj0zELlRM1YvW#`I+A19|*nnwN^Eu
z>ZPY!dQRkm;N+@)w7{~G-_G=TSaw-$HXcmSyWdmeS#iyoxyehlNwGDh6tlbSqeetUaOi#miIVhb6`$nY|g}s5SMd^
z_SaPPJzS_}oSSQMI~&iDux-7EAtM%@J99W_X)Si#n)%(idLH@Ub;whrwx%01{O@`y
zAG!#y&5s8l!zpCyV=cG`t<^5
z*X^DHc{4JwN3N#XlR3vMUA=HI?XOt)V_{m07Y&h-D=k2mE%eu=>+2B=&1hBUyInl3mwP^YE0KhvP
zfA%)%Lrwh`IKX!|@Sd1CLXQwSyjj)PNJU-bNiaB2$}z?v)RGPb!d!hCk$?AmU
z1}OQ00C1LmLm~QyKFr`VeamkY4)g6S!48?=4**m;d(Vs=JHR3;Z{f8Offx%m!`UXS
zONA~9LWdkf9D?c|7wi92TXg=PXEZ=jT+16ZG8XVaKpy)cLJZy1L1J`k!T~Xw`Uirl
zIITVdXi7@x=DzWE`O-ZxEdVh5It@X5^9d2NA1GGNXB2f_K~`C0B*091m;$;3mo@hz
ziu$hcIUL^26tkX{v3o#mE=WK>c7ZzqFq&Era{QY+1xci$G{|!ivZug1Jal8<_?9GF
zeIIQUuoNFy%H9JCN8T;G4!}?x?ozH0!v~uI@qeT4-$4kKQP;^3gqtpBDl3ZmydbNp
zx5gkH=myp?41Jk4A9U4YK@IKqXK72Y$9x9IVL3Wa1loib(0`N6zJ^goqm4ubQT-=$
z{9oib#USi)B(Qk~@K!Fq-8a>Eo2LLUr9_1E>;qUFdjQZOU)^
zxol(gM+L?N;2kO!p;iX{7-#HXZtWSXHYkwqN0d3m?JsOw>|kf3uu+{GVn>_90sF61D{gS8Li(JO=<0(LTiR^9pxpS2)~fY+-{1ogdL0
zlEMCix2*bIDg~UL{F{M*Z^-I2?*AEBlq`(0O<3DM<8eUM@y!D51rW=^VAVCjhx}9K
zFz{()6FYM-B6!Dvs^N>K`G5`i*Ezp$$aCy!e_oR!!dvNJF+x!N0(PoH8&vhb7&^Wx
z&~vPRGx^F0p=Yq3%;Dq}d?-;{!5TjV{%7_O!Se-Aj)K
zH=uW7G$?a_|NaGCNUixKEv1;Ap5DoId3pIDOY2?J*~nsr@!&Aes`eL>%WmZjrq2@i
zfG_PwMc?GSdY9uB{-Zj>o-@6>KZK194elNudR`aP9!K@t54XpqA{hCF%EW^3e)wxk
zA{;+#sz?iHq~EP*dwi6y1LLv!rN
z)A(H_dI4hU|9F{6n$=I#BoUek)qjO#u*%{Y)kp%_goIk$FDxJ=FJEdkIv*n5Qt;aU
zIQOfqtsT8?N(~>cIQ}Uj#%_>&*7Sif^|(q@t47N*O-1WSqp
zuRh+dD&$X6+|Kp>Ik`HlUG?5dHdU?sg9WOo1
zD=@6-M)!Qw+uM9-`lS)4?ynrKFV~>w!T(>LN4y?I)g!w?W4|$801RuURFe>kD$+kh
z41#K+W`Og{oHY~`z}D19FAJT7;}dG}M^L3pLHFbV52;WKS&`MdgRnnIVS%#4pCYuw
z<}lO%4f->>lCMflc*|$ma0nvcuLMeUD#-fpJ6yt3<8wHUj=`Tof@7BmgEE3@yd{);
zdLz;dbio>6Z5GccaoHGOz{@LuWu)i2;j!vpG!o%Yx~85UL8mYn8&4^kl(*waL~Y7BPqh`@;iGY;>@?2xlDl
z_~3!#GazOHfpo~JfGY@ESp?Nw$ny?tFmOf$NlKO;T436dg(q@RV-{GjEeXRygC!fUEn2~-R~cT3@*@+G
z%ndjw5@v|ug|#uZCrqBDq5Gh>d-oxLw#@>mvK?$30t#{uqJI}WCdS6iLMtNyhZ7F>
z@Nz{cA4uV@5H$*H0drs{BpBZY{@io$xrOt#VB$#-=|m_&xl%2m{&1lEhAQVf{S0*C
zuU>TELAmNt#KjgwX#~NM0lQ=I)_-TlVABHt)HA>eBSPN>2Pf$)sUZX3$KfI=iBq(}
zmmljG3cf0j-q8FN3^-t7vT!2MTDa+;*P5UJX0BOitR-uR#0$fQg5boU5T*cl)v_;4
zRCNzmK;Rz$uM3T_!vA@*wSd=c7Vf1+aIp5nkn@e__us)Jo&0q|(@J(^g$9iM9&+dT
zu564+X_9eezLh9U;QV7hFtxZkHSw=Ai6z`W*Ze(ilV;9w@zCoRaAv_N-#5DHuK`%x7?I$`3I`302%?ZBHe+!L15nSjHjr@@G{kdv7
z2^GY=R}vL+8c8NCe3O&gLnN1Y;FPAoD)+Xq%+3ZUsgskXXTN8Y_9_cnmaXC7=n{7
zg&Q5-v&g(CBStOOJN;;Rpx2Vu6Ex?f_~^D6x_aXyT6PKy0!;7eaGkL-Q18Dr1EE#
z)z|E(2*A&RE>`W$ioH%9F}@o|aOfr>4vcE~{6wAJr&#xK9NMW_YrJ>1Rd6vAXnk~|
z7CbKqvp)}C&TTDpKFhkI7g7JY@iT?U+W-6)x(Q4WCOBXkHi~ABI!KTp7fAo+C0Vt2
zkIf1Nuhh^(pH$}|dxI*|iEhVG^)*fqb(?e@aT3pGfjQRM;k0@y>xEr%pMc;d!>5Dj
ztYGx0jLN7NalZfSK7OW6vphDZp3lN4X(IBQc}~s@>fGhsT5PwwZ9K{Bby~fBaT&yz
zRE#m5L~YkD$1rz%MI}8U12+=TY6|w#z&uAutxQ}gt3BuwfZ;*xBgxHJw!Nxj3+L6)
z%nbNaRSo@$RGME-5QUO)LDG{w6$f7R_?u%eLS$vs2?92xWjV$MD2@y_6}%z^dtdHw
zHL4EO;ARJF2I942=zrQzAt~DE;rpshN)oboNk`mD)OQvn>deEHYSkO4^xQ<
zrA1X0w^Dej*I}hEv09khs4ntAlW9X>l`cueLOZ|5#Ph>hFNz$cQFeHRpM3xo81qli
zm$s_6mm5d=Bup$_@m>DK5ZfTsmEG{B=uj;U2e5_zsmh)
zLOJnlUAYX|Jjj)itWtQQlu)}x+c<_Hvs$&7(Alvk%!xLaR@}sKITTb7B~)S-%Z
zn%Y}Lt7uBG;`^+A^hl@Ft)=b*T{=86zSybyaN#M-l4Ors-jxLxFviGL`enz(R-YLUuoGyWRkHGEOH$IHz3)IJkNFm04#-KE#>fy+k6nk4m=dho
zGMM<(t*Y0T{1N>9`N>?z<9N4uRz;515YHTgf3vaaL+&Fbr#0x3KRMA~R|;^BaV
zu7OL5r>+ApUZWzJjA9DCBS6d+v(oSX!NW
znJ0T)fT=qoZE|K7NS?IEBCB6B!#q_QYQ;=1HA?Xkx1Vi(#=Oc&@hXAC#QAig|JRgj#AKl~Xb~8CNU63szle>r^2eLSBc4}m
z-*`zB$>k?r9o3Owo$2!&N1f+*w
zV9KkWoD~Ak!!i2QMFmTu8kRF^MGx~==J>%?T;1I_`c&%L-*{J_!6cqkFlXAFOJZeP&Gs#goY|wksFcInvBY~Eq%ied4~!bAB`iJN{(4hRt%eO~Z
zm%Flx{PpzwWx`8p%>isq%{26nTUy(g(C0-m(xtwt
z4`XPC-4-LJPC}h63KK~r=9SM!0s^uEUMN;zi;z(Jj~F}JS0UTFU`GO}$NV4GP`JgY
z{Z$s0Hu`2bDij{VgO_HX$4vvU<%**)B>mj#kz6P4TrCq)l)d11y(?@CvrVi_)>*3*
z!;C0Z(6xCp<)cgZ4)gf$V3A`ne@a>2mnN0w=1HSHKy8d9d8xcEDn7fXxcGYu2Ravy09{r2g*KAQri>cVaoLn5
zWI5dPyCOh7;u??YN|$S~VqoX;aPjhHw)LSv>h|)uRJqb5Wt(n03ikBnObTW$#n4I(
z=5RL5CU{r%)#wCpO_%IDXSSp5XCEvyulY#I-1N=izuVc-IrKWq`cAWSyR;9bqo
zv=po!d~^Li%`5EgklWXEnW+rB@636x`G(>{YK8&7Oz`rfj1nr)z#_lJN3GrkzAQ+cmWC&4x0;C+Wc?M
z3Gxr^50ac6_;^YB^COP9a+{=+CQ>HKXgSuaczDuBZ+`q7(o1EazpN!>K+JFMNtzGu
zWxSNdNt_e6{eUM?}!E-AWG6uvntj>_)AKrktJ{p*p_uleH5_9m#x
zycNRED_Sz1@y=)LGYrddCYw=FPintC_5G-&ighp{!{T*VJqtTr+;&^Zi4tls9nFyD
zvP`*lhsNqB0>QSFRg
z&9OEx;z40Gm0xfDZ>a7cKI{ED9H8Cz7Ija;Yjg`gLF8=4eln*m8a&v5H)qvRLDj7o
zDLNXz!rDWVAl~(`p+*Xr8DLUChr}k`p?rc_%K|w4X4*ZB&bIx1BWGVD?%MNZeB!0o
zMien6f2GS(U$D2iA>0$Y>y3d^=bfudAI}3d=imML)GjM@%>3
z03R_T$f1?JSk3+8l$DL_>p=+mnlN+Ir;En%_P=2*m>={bdEX71odc;kPwcAZkP3}_
zaXuOKzWf?dA&%q3+}WZxpU{TO_OzB4*;jS!3W3`LwY-Dt`~$2;XCe|OnuG%OG%E3i
z-6Z{eyOt=FZV+9M7SQ~V^K|aOX*J9xgOkx6Gd$Q|f1%XFt1Mku-GT{%PwTHgSMZ=r
zvdrR3FtLNco%6J(C0}V>jQR&l;|kGh41%9e%(y5q
zSh@I!1gIKS(an|bmWzr_QT8DPAvm_*H$I6?P4WQgQrVtJ-@)xq-(ez=^WIRJV0?1n
zXQ<|4@{HjSzMu^H`+$QjnI;NaWwZevKvNDBbBKISat%?vTLwE!e%MUtudK%E4*uTm
zzQlH=`Zf0VX@90R`S@3=jBQ0s{KjwIi}7)7r3+}@U^vkRXemr%s0uwXdmljoo!t6z
zofBL5O4W>bb>5#7Qp!&^$87l`I=EcOOQ%egFX$B0H(?U&tToN^tqRm2F4+py!c1!$jKf=`*^s?fdT*=oZB0?D$W|INKj&J>Y%E=koNGBNh>G
zLa;K9M+$%KJ`>O`{?MjmET?Ev6sW89Gvle52B>-hTy3K-m?VJbo>Ma=GlB1K~7O3-MoT8;YafRo@lD5H)4G6=5qEm*JF+e9mK8!V1%N
zw!g4J2BtI&pUxR!$(qLSrBXxmB`lP`z71$TJopVW+mz{EfhUXhhBGC2~?ism+WW}g+>+QXoNemqwC2BI|#JbYFY7V4^yNY7e
z*Iep3kK8bUSQ5UrujEO>Q!hPDl9w$5%%{tFPk6M+tv2*L4bV8^oM@%MXL*x8`Je(H
zFv+1Q!A&nxBeNk7bNfi8#~VlX-^cm|y>HHC>k&0AS}HjsU$@vIZ^-6zqf>L2U#
z_jm%_YOanLzeXIy3ZF^b40`eM^ZHy-=LIlfHs}GYDo9U>9L6+wxzLJg!X$_38}{W?
zN1Y)CoB0oCGc?*!FvXb2+))sOjgCv;JYvD?ZpuhBP#3(KV4FTgNNex
z*9o=ptxPE%qUBkPH9_}zU)~;CQ`oP6LkJ9wf$f~PHLa-m103V)Vlb)btPAcc
z^nQXT0~E*exag{X*f0Z}wc^iN<%W(zsM@J4JIybwN1nj~Ck;wl-_FSkehzX@a9x)M
zm#0!i?A6#ap$@NC0pDa29&l01#obr`0(4|c2fA9v1jBK4cbfAvQeu3uLjtEcG!znY
z&kCnpHy5U@94*q=+@1p_=1Txo@e*G5N$2F=XTtbyN&bb0nM>rw`fUoP8fUbrt8XIF
z9omkxAvARWR6gP*^uWXW1W{c9Ne4(}s90a>+aIBX7|7pm7Z1{Ne$4UXOD#|zO!+!u
z{IL)XI5YnZC_VflpX))vWQADxEIv|E*}*k_{_*a{tzS&yZut0lO=Z;M6a{bnxgpyv72R|ft59`6e4&)~
z6i|PcgOW6}a+2sLXSqzEpQPeWp`XN#%YmCNzAc^8EHn2hyMm>r+h=_^;3Bt5`hNRx
z@NQ0}pUt)|O6xkW6HFR;*ex0Z>gv^B^^>cP>x1;3)f6-FGdih*$<0VqMg}Xbqy*Mt
zVmo7O9JadIHnU*5H^n_OxV+v<(ji3C>jEcIq69Xo^Re@-*s
zdI_u@s~9k8*s0lQjwbP~oSWD-cKofBVgE3;^OR;|KgFKms*dA!7qZ9Qn$Y%`gIL`4
z@vXmc_p0l3%xcQ(I_eqQI=37L6{Hc!PXQAjBj0rGW^fyEZS4DOTiU$>^2_K$)A-wY
zrW`cOLlcK-cTfs>cVvI_WJ{h9vnGF@1hV5B9go}j_MG$1OQX!2ZuZsmU}cM?mwe;s
zq81hg>f0av(STfVAO^|Yx#!Ai({=vH_?y2>XDq(Z8lD!+&Q6Tu%Nt8Ot!o=;1o79k1=@Uc}$_+
zzQhG?2?%SS+FwkTgQFVe5V`8cO9nlWOu<$SMowwu)Dv
z#fy+dKB=j5;rBg@#au7D3=(I4knJ};&^io@Lm#;IG{A%GK-Y{|{c
zr%F$CQgXRsDzs!5mSZOl3o`V{rRnwh>1vM
zaJRR%xb7h;d_+a8tA)q=vUVA-RJ8!Js9HMmnl7rKy#3=!vHJDOSjg6n1mZg#)RO+4
z2<+UJ@vzLgtVspj@yCTzwWzA}LVxP)I;q6s*jXF=Mz!Jb!
zaiU4t3g%@DVL=LecaTd>WjrcFmpd?8!ix^?;xBFOFSweB6zMbT0P}S6*T(QOfu%P;
z%!fB#4{Y17?0ko5(<;gi6(w`x;hEzO{Yb{ZO6H8ku4EQKPZ11f+)=Jw`t~(Ac>Y`L
z;|l>{w>P36SsgXz93~Eq9Rq(5`=xQ{vNyWLnB6c>E^w=Gr$+S3B>nhgEN~`lcj}fm
zKY3+(T$F5b_k}UzL~qdjM#=ypc)w2b&#OZNJJZ?kB`JB+vW7fVyB-l>E~$g(f*!!w!AZVKb=
z0@%?J4VhsEEtjUR;2q(khsVv|Qp3g4m4{k^{ru*7xuq%@?Rh1O-f`>~9P}o^N&RR_
zT|+bvz*hG`6xMEcDAM%K?>VyCCrXt8`aLYJ9(P}BS==eD(Tu1~9E-5WyKr^2P5_LG
zfpKuu7@c3#*QaI5DG4!k2%KurwW2Sqp(<-qJ%x|??$v7OU86K)Gn&@F0@ofXN;Rq
z1pLXk78(b10*sOowk!s&<1&XBB%)t>rkf<#MB3A@89kZVPS1_Z{89W2Y)TW-W;R3j
zD`#hA-LqHU>)0+mrD?`cvcbEs>DtO%?GHl+7cY#6&0mM;3$<2^%4xe3cd__Wf)a_3
zIY|APD|;7Y^a2DB=ObflTWsWES*;#C2yWu|T=w*%F>;c@0OP<-+8KTR>+R7y
zzWyRi9*KHRdrSQ*>SFM$>VoAs7-0mlC2ZdNv`}tO0!kxl
zT8JHS#Asq7#?z#H`qydwmeD6~h4rY*6Kv!+CfPGhUYNxJnLnJqOzL_Rzl4f$e#=>T
zhBv-O?Q=ML)8F1PuKHoO(3V|yRVOFLT}yEY>^Xe^WgoUxF#Y1ddUR}9$z9l+
zUMFVozPu-_2?|-bBKIs34v%$=rz69ZT>}>_XPEiq|MMs
zc(RCA#pj6OIHSR28qtv~(^%oh$G3^-HOr2yHV3MdYG$bE4t4Fe1r+#f+?;&1aUDs_9fv>=TBZ54_E6q@kVQX
zYUh2U#|h&DYWXbDj_d{opT}eD3SE2_4P6GN`hMj%%!cU@67#zZx=OI-n4S8(kwRsV
z9i|^jH((x0Pf63t2}ug*J)l2Lx|?W_x{56cD@b!*rJ_&tVacKmT!k%&`bL|$+l4RF
zXZC4iKHSk?Ncyb(Hgybnba;-#E)hj@xNXsj8DG1)3JJz4x@-Q8C)
zA8f`i?HrC7TO9l<>+m&NyK{Ij?W3@~h#5bF|Cot??)$vq!1sZ}fgkev#p7-4ITg;9
z1i-cJ8dlOB5MDy;rWcFMwgC))hHG98yFe;sf
zDuN@pjGa_G8&4>i0$i{Xr;bkiplom{yg+!4SnKcuYevt#8m+OfQ-Z
zF331bGqYvtmH4th{?l
Z*ygh5lGkFL!tJ{A47e;{3W# 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 MKFEueh(N#oA5EtdZ~y=R literal 0 HcmV?d00001 diff --git a/_weave/lecture16/jl_GcpKod/probabilistic_programming_5_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_5_1.png new file mode 100644 index 0000000000000000000000000000000000000000..7807235fbb2153bcf9ae84704d7bde76ec1492ca GIT binary patch literal 12894 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture16/jl_GcpKod/probabilistic_programming_6_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_6_1.png new file mode 100644 index 0000000000000000000000000000000000000000..c9151c90d1b0214b3a25e0f1d872a15fe94c2c09 GIT binary patch literal 5873 zcmeHLX;4#Xo4o;%78=l2q!omwje?C57X-r=ZKN?G(1L8DEGn{t>`Q>qZm1|o5RpYd z5JXvJl|V=om91H1CoGX2fe;7~wh+E2^UYMvk8h@Hs;2);O{ljLxViVa+~+;#Ip^H} z-B3?VWVZ-{AY%A4r;QOr;3R^e!oCxLztk_EJp*r>ujuQYMmG3AsgzeS2(mL1fBMvg zfFx!=!6)_(WqeQ#?exQypSRwZyrG!7ck6M*ddKrh$3|D;}S_dhl9SAk1l3p zcVu*w(v5RGk_2NsR5MduMMyzOki;z`G}$oA%q1xJM{k zlW5PHo?fa58>z0!{3)?>z0fJX!g9+0Dj0t*(@L#O&a33P z0B0gSO8m#eKe14~h4%XUV-6fRP{+T-)2B}#Jb1vrgd~o-IyQchl#t#kODc+OmZ@}_ z8oBiBJx{){I6G_ z{IQc#C9XY%V`F1b?M-oUab{-bu{=u}Le8?QzT45>ewtYGS~p_n@#Dv1V`E3x6NJ7` zjc|5xA+YNdA|71XNX^U2a&&Q-dg#LW@n01B6zU~Pi%ieWWv));GYagg{Z5!!TK0_8 z#pvtnmsP*5I*by$_u|EiL2g87(!lBKNNupT^Ws&(3mdJlMQ@nVjgn4hK4K;IXgRF zZ%jDuMlWU(9Z`Z`i?lf1SJzlnqH3ZWF0fim?OcM&0X!)oAz`pOTuMsHV*@X+ndTsk z^}437-%u@q>kDQ3Q`h?atLC3ykb>v9=l9=#zwvpOL!VEZ0iL7@S99sg+oKiaLyp<> zeV90QvE5|6>3K!a;$)&b`P)L95$52*bN*2d@&YaLT0sjZ;DlEOiK+z7^mqzUn~4?J zd@}rb;8?t{T=49X8kx2-4+h8EEXQJwCMdyOXAz0>jcTuqQnYV;rIE>G=CR*s$cNhN zNAdc@e%O{$;;pWG+xEVB^Jc$Ma^+$Yw9wo-FvEr#y1u$d7>zZrZ)hm1uJjYwyj3SK zFwpWm>5_VYhD`RmoE*Ita{^JP<{uAy!jLpVoj!uTTfv|OkR09Dp8Bz#~Pr-pq(5Y zvtGZ(4&L4#smZP#ZHU|8j^k`91Lnd|f_dc0H{MNHhk?q#=PEmF^}HuvzZwWx>IX1( zyz?Bcudi1hPSDt0zzN!iH@>inPe6w*-d;&W^+L(1e5$pL4I{L$pr8OaV%_izvMiv@5RH#`@Zjv(vrm;Tll_=TwM0Y%yFZYM`L(m zx7+CS1kUYBr-otxYX;?}go>J)!_i$E_cwNAWMrhQSS921XrCYMI(GffXK4K7=yMM1^$eXr9SUw3ae@j_J^A9WINV-?4cY^8&BG9q&dedVG$<)40VoFObalXmLUKS_X6Al8sj#qcWq#bo^OL0cCC^V~ zRg1av%{XdImjG=IDz>(^zBYN~K5s&pDIY&n)<y(dZM~6i_uvDw!E-e}xci@=v<&%CZ8;<$}!Px_ScfLD$ z20;J+>o1$4M{SA2ba@%Xxy7dkAUhujo0fXg442{z^V z^xr6RpQWFdZbx{!;R2=h4NvKGI!rCMzISu2`j|bqQ0^PkY!aU^PB4qxW`JT!T)M<} z9UTeBaqSfHg~FDgj-;fd)TJAzQnW)vL`3$Vdj@;D3T7i3&s(m+g|1K8;1*hRL`6k0 z;X zyseS7!dY2buHegPJ2BRpS3f_@&aWb`Ujqz=goHFFYf&ermr6=YuOE%??Cg}u57%Tr zOjgc16k|yZ4WTP7pY-Ga*GIS?NaUMCeSU_Ugo!bUhot9(${18O?y%G~Ea~F7#VQ+|l?yftT6TZHRWJ z4$8&J3CphC3#GK|KHOgYw-={EMB&`C0>>|DmPOYoFhOv`+HGmV95$|vWw>7r)yDt7EtUPazNQNQqGw|fPX^YxYl;6DAgM?oN2 z=GC_#+KEfQZu$w068ziN%d}!)*cF99r!hP?EWmJmr+$1=Cj#81)457JuM*J`%`k9=2s^)E!|Uy0Pe6ZSr?A18&eDxDq?%{xoblg+uzt$lhZ*C{_Wd+Xul z!iMYQMTSSCrFZK#dE~ab=cw zDR3Zg+_wp4cUGFX^$f6!0coVT&2VitzUX=*z?{-_V)!8%pI1JXASiy~s}yzllc5Il z$x=8ZVhJ>EbVy_F+F zpCUXci=F&^y?)KkZm=?N9=3ga!xzv$Zf=d&jwY&kG|dGWclGrpT`K~Ep|6w}9LV$K zivTToiG;+&WAW!sojOI=A?Lvu92y!jqMkEEO#zO)z2`e)3=9lX+FybQ))B{!JTWos zA?JYPESDi)CeT2Zcv0W@#Ky%b@D8*&iI6bO)VzOJ0dUE{!1AzB#aCfsGACvS!{yvJ zmK)U=uI}zL4C;m|#?r#V!qoIQ4Czba!ibgc!jS?oX;ok~cY3iCJWqH@tGS;eUHfo* zOBCogAPN$?9GJ4^;jW?v6l|nDrkg1ld#?`si)K!!QLb^$lSF? zk5F$=Uen~MJPECZmSU$ib?;x0;M8<~d23*xxXXS$HO)An{QU+A?X9iGmX`5dcF3#~ zd)CZYS66pZh~_s&*^2rOPecAst=Ec76q2F?BZ9u2ZCia;On8sa@8_daXxO+p(bn{2eO#>ULELsq3PDL-pQ{ejv_ z3j5c~c)IHk`=jxnKYu2XNX6kJ&i!OxJjs9{bQ1kmkh8|v;B6!k?at#Z={jDqJ*{-J z0VSp+68%x!g6{$M^13p&=ba;*Y2wZEZsH8_giR;W|67c}Ct_s!hN?>CDvNvX43U zl48yG;jdm4UiuG<`$@bz;kZj-k1Jg%HGAHzb#z&}F8q=p2TX2C2&acn7QEG|UOJs! z_<9%|6shi7>HMh`pHMTuQ}H<-F?sujd(m=UBHbW=C2-tX_kOZLf{GJ}bKF`_Gy8b< zEpE=HZoAQigC9Y#zRJm&?xOO1N$8G_4hD}?#up%%8YO9DV#tdu#A!D%+7mSKf+;5# z@bwRXGngwcZ8H?jL9uq!`rZBQ3J@`TWjA0va6RBKL1ru_GPq+knDPzY8g=%AD7XWb zTuEvwSpO`=F7p?MEDJ}N);bUT_B$y}x$SQ<$oBM1k8D*{3(eM%1=e86gqVxt1WKR>s z)>SLjQ$2W8gGlVyvjZlX!K?OOXU{Cn^s9kcUmUqBCe1gmfpwsDg5KX0vbVQqDuD3f zX98dsFGVJ!@g^oFV1M$hDPEPbY{$|1d|>C9@{LhH6a!B<8CqOgS_J~Jf>~-s*I^Io zv*QY#JM1WgC1qu0Ly$szT57HY9_$5A$nO~^ia}seKoxSPx;zr&a2m}cdvYG+kS~I{;Hgz3D zmVK~V_tX0yNGkDGnu+4ls?#yDh+tS{gANU`U5i+qy0ClqZcu#GgO-Ia)6%Z=z4HXL z>JzrHKp?F!J%}1tKHh^ULX&w29e-{tQe!3nTvdfXD-_-!HhAxZp??AWHxRnUTwGk# z0II7~NV#NXmB>k>rB%hV_QhRdL&^lUCs{~HXs=#ml7_!KJtBYs2fQF9Xo^3kz?4V# zxcYII=en#3%+yo2-ehN|gsy%omWob-bfPWOK>Em))AshGwb6Sw=Awu%DT)PIWi-Bh z%*DI_%?mLFLla!3k3uxM?!z5X{>fJ(vHRh=coNJzEhDYPc5s^S7PU5=>7VW?VgX-! z1P@E&N$p)-4C2PRu*|G|z@xxl(gp<9Wgz88#)9g>o!=H?8)UN<~R zCU7Kr4a{u&YZIkkerX*j8Lq!x#8{v4*L$?vckqr>6?m4I{RX?WgO~9N&!2GevIK-( zSJ;dRGHm(5Yj;>gDeXLTH`eYXLWSPKN zv!+{}A)&G0$*CLbtTJLS+#F}J!)3Dl^*6(z+uIGFK@Cn z_(b-d;y?gbit`-4BW0bHmuE=0V0&JWlP*GziE_ z7P|x>)>{Ct0BYl#;T8A<-&p!XSk}VIO2dDq58{#mR-%EquI?uz;%XZ|4FDYm0K7={ z`Nq$nWF;-y{f8{7f>-DzE@jCFAqzch(e))%D$U<-Mx?N3+Qdk!1WExW*D$(LbD+%* zSy#R*EcE(#Z(DzbzY)O@ZgewET$$T8f$Bc;-vy=r`Pe#fE!lO|@omf_$gmN-uHost IUu|#x0}tKI_W%F@ literal 0 HcmV?d00001 diff --git a/_weave/lecture16/jl_GcpKod/probabilistic_programming_7_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_7_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5cd7f11a17b22712bdda5e4458bb907488ccd207 GIT binary patch literal 19335 zcmYg&1yq$?u=b(51O=o7l$P$621)6XF6r(@I;0z*}_IA|Skk7xM@ahWIPO1_eRL-@g6!xoB+ZtQDEBiR~>p3Y%l$Lpzz? zW=6*FBVpXb&L7WXo=BQ-crfDlfH@MFia8b@GX#PxMWYXaV1HBW1W(@N_`yJgg+*ZC zAbx&9u!vyxCUg~i3tA$fiY^n)>q3Uk7U}Ied>#^;%VR7<%qRJl)X%q9h-6ti)|}}Qc{wC+ zrB+W8p;*HQ;5ON4xz%BCZ*4S5^u)~Awhn(1tf;gCJR;*QK%Q@8)v?;yo{ z{?NSEdq_}j^hn5Tetfn`7;H1l_NLx% z$0z*yX<=+uRh7{zlk2w|8i=4yfSU^7}3ubTU`T2HzceTbO@a{S~t}hs@3FZRa}lq z?z``;jFBvgz>G(jvfWr|gcU`>I7G2K#XfG5%tZN_2NYDTzxTwmEP9v{`0JGq&!6M= zDDpG6^I9VBIo+sege(8OkluW`GLPn1AP`auw@p2Qk36-M%r5zdysAgpwpjp4tzny9 zQo8HX1Rfa$=)hVZ%4e~3o?g2X7wAoQJLV!W(T=a;vWM%u5-~{{>!|X4)4q5q4QYH` zI009iWM1=|;XcM`J$PW?se!rKRO;rkwbXPD@BSF6861v!{j4(2{2eGZo4u@YP3X5me#06hR zv+gqKfB(eA%WT$tju2hdYP4GmA@iC0sjM&bCM7=JadRNn=kd<{AyKIlqSgD^X|!?0 z#6w&o#{X+^R*>6wKNeJz3sI(IU@xyLh*7;pipSQUZead)->V_x5%-T|@?B@T{>dTP zY({_09=hXh+sbe@p>DumrLlxI2;|CtR>_A>YgD&X_RZ`IEe2-KIlPpqi0v0{_mEOi (+#* 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture16/jl_GcpKod/probabilistic_programming_8_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_8_1.png new file mode 100644 index 0000000000000000000000000000000000000000..93cb9b043b3192bc96319dec10ca230a8cac60dc GIT binary patch literal 21363 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture16/jl_AHYinz/probabilistic_programming_9_1.png b/_weave/lecture16/jl_GcpKod/probabilistic_programming_9_1.png similarity index 100% rename from _weave/lecture16/jl_AHYinz/probabilistic_programming_9_1.png rename to _weave/lecture16/jl_GcpKod/probabilistic_programming_9_1.png diff --git a/_weave/lecture16/probabilistic_programming/index.html b/_weave/lecture16/probabilistic_programming/index.html index 2801b464..2454a0c7 100644 --- a/_weave/lecture16/probabilistic_programming/index.html +++ b/_weave/lecture16/probabilistic_programming/index.html @@ -24,7 +24,7 @@

From Optimization to Probabilistic Programming

Chris R prob1 = ODEProblem(lotka_volterra,u0,tspan,_θ) sol = solve(prob1,Tsit5()) plot(sol) -

and from which we can get an ensemble of solutions:

+

and from which we can get an ensemble of solutions:

 prob_func = function (prob,i,repeat)
   remake(prob,p=rand.(θ))
 end
@@ -34,21 +34,21 @@ 

From Optimization to Probabilistic Programming

Chris R using DiffEqBase.EnsembleAnalysis plot(EnsembleSummary(sol)) -

From just a few variables having probabilities, every variable has an induced probability: there is a probability distribution on the integrator states, the output at time t_i, etc.

Bayesian Estimation with Point Estimates: Bayes' Rule, Maximum Likelihood, and MAP

Recall from our previous studies that the difficult part of modeling is not necessarily the forward modeling approach, rather it's the incorporation of data or the estimation problem that is difficult. When your variables are now random distributions, how do you "fit" them?

The answer comes from Bayes' rule, which is the following. Assume you had a prior distribution $p(\theta)$ for the probability that $X$ is a given value $\theta$. Then the posterior probability distribution, $p(\theta|D)$, or the distribution which is updated to include data, is given by:

\[ p(\theta|D) = \frac{p(D|\theta)p(\theta)}{\int_\Omega p(D|\theta)p(\theta)d\theta} \]

The scaling factor on the denominator is simply a constant to make the distribution integrate 1 (so that the resulting function is a probability distribution!). The numerator is simply the prior distribution multiplied by the likelihood of seeing the data given the value of the random variable. The prior distribution must be given but notice that the likelihood has another name: the likelihood is the model.

The reason why it's the same thing is because the model is what tells you the expected outcomes given a value of the random variable, and your data is on an expected outcome! However, the likelihood encodes a little bit more information in that it again is a distribution and not a point estimate. We need to make a choice for our measurement distribution on our model's results.

Quick Question: Why is this referred to as measurement noise? Why is it not process noise?

A common choice for the measurement distribution is the Normal distribution. This comes from the Central Limit Theorem (CLT) which essentially states that, given enough interacting mechanisms, the average values of things "tend to become normally distributed". The true statement of the CLT is much more complex, but that is a decent working definition for practical use. The normal distribution is defined by two parameters, $\mu$ and $\sigma$, and is given by the following function:

\[ f(x;\mu,\sigma) = \frac{1}{\sigma\sqrt{2\pi}}\exp\left(\frac{-(x-\mu)^2}{2\sigma^2}\right) \]

This is a bell curve centered at $\mu$ with a variance of $\sigma$. Our best guess for the output, i.e. the model's prediction, should be the average measurement, meaning that $\mu$ is the result from the simulator. $\sigma$ is a parameter for how much measurement error we expect (some intuition on $\sigma$ will come soon).

Let's return to thinking about the ODE example. In this case, we have $\theta$ as a vector of random variables. This means that $u(t;\theta)$ is a random variable for the ODE $u'= ...$'s solution at a given point in time $t$. If we have a measurement at a time $t_i$ and assume our measurement noise is normally distributed with some constant measurement noise $\sigma$, then the likelihood of our data would be $f(x_i;u(t_i;\theta),\sigma)$ at each data point $(t_i,x_i)$. From probability we know that seeing the composition of events is given by the multiplication of probabilities, so the probability of seeing the full dataset given observations $D = (t_i,x_i)$ along the timeseries is:

\[ p(D|\theta) = \prod_i f(x_i;u(t_i;\theta),\sigma) \]

This can be read as: solve the model with the given parameters, and the probability of having seen the measurement is thus given by a product of normal distribution calculations. Note that in many cases the product is not numerically stable (and grows exponentially), and so the likelihood is transformed to the log-likelihood. To get this expression, we take the log of both sides and notice that the product becomes a summation, and thus:

\[ \begin{align} \log p(D|\theta) &= \sum_i \log f(x_i;u(t_i;\theta),\sigma)\\ &= \frac{N}{\log(\sqrt{2\pi}\sigma)} + \frac{1}{2\sigma^2} \sum_i -(x_i - u(t_i; \theta))^2 \end{align} \]

Notice that maximizing this log-likelihood is equivalent to minimizing the L2 norm of the solution against the data!. Thus we can see a few things:

  1. Previous parameter estimation by minimizing a norm against data can be seen as maximum likelihood with some measurement distribution. L2 norm corresponds to assuming measurement noise is normally distributed and all of the measurements have the same error variance.

  2. By the same derivation, having different error variances with normally distributed errors is equivalent to doing weighted L2 estimation.

This reformulation (generalization?) to likelihoods of probability distributions is known as maximum likelihood estimation (MLE), but is equivalent to our previous forms of parameter estimation using point estimates against data. However, this calculation is ignoring Bayes' rule, and is thus not finding the parameters which have the highest probability. To do that, we need to go back to Bayes' rule which states that:

\[ \log p(\theta|D) = \log p(D|\theta) + \log p(\theta) - C \]

Thus, maximizing the log-likelihood is "almost" the same as finding the most probable parameters, except that we need to add weights given $\log p(\theta)$ from our prior distribution! If we assume our prior distribution is flat, like a uniform distribution, then we have a non-informative prior and the maximum posterior point matches that of the maximum likelihood estimation. However, this formulation allows us to get point estimates in a way that takes into account prior knowledge, and is call maximum a posteriori estimation (MAP).

Bayesian Estimation of Posterior Distributions with Monte Carlo

The previous discussion still solely focused on getting point estimates for the most probable parameters. However, what if we wanted to find the distributions of the parameters, i.e. the full $p(D|\theta)$? Outside of very few small models, this cannot be done analytically and is thus the basic problem of probabilistic programming. There are two general approaches:

  1. Sampling-based approaches. Sample parameters $\theta_i$ in such a manner that the array $[\theta_i]$ converges to an array sampled from the true distribution, and thus with enough samples one can capture the distribution numerically.

  2. Variational inference. Find some way to represent the probability distribution and push forward the distributions at every step of the program.

Recovering Distributions from Sampled Points

It's clear from above that if you have a distribution, like Normal(5,1), that you can sample from the distribution to get an array of values which follow the distribution. However, in order for the following sampling approaches to make sense, we need to see how to recover a distribution from discrete samples. So let's say you had a bunch of normally distributed points:

+

From just a few variables having probabilities, every variable has an induced probability: there is a probability distribution on the integrator states, the output at time t_i, etc.

Bayesian Estimation with Point Estimates: Bayes' Rule, Maximum Likelihood, and MAP

Recall from our previous studies that the difficult part of modeling is not necessarily the forward modeling approach, rather it's the incorporation of data or the estimation problem that is difficult. When your variables are now random distributions, how do you "fit" them?

The answer comes from Bayes' rule, which is the following. Assume you had a prior distribution $p(\theta)$ for the probability that $X$ is a given value $\theta$. Then the posterior probability distribution, $p(\theta|D)$, or the distribution which is updated to include data, is given by:

\[ p(\theta|D) = \frac{p(D|\theta)p(\theta)}{\int_\Omega p(D|\theta)p(\theta)d\theta} \]

The scaling factor on the denominator is simply a constant to make the distribution integrate 1 (so that the resulting function is a probability distribution!). The numerator is simply the prior distribution multiplied by the likelihood of seeing the data given the value of the random variable. The prior distribution must be given but notice that the likelihood has another name: the likelihood is the model.

The reason why it's the same thing is because the model is what tells you the expected outcomes given a value of the random variable, and your data is on an expected outcome! However, the likelihood encodes a little bit more information in that it again is a distribution and not a point estimate. We need to make a choice for our measurement distribution on our model's results.

Quick Question: Why is this referred to as measurement noise? Why is it not process noise?

A common choice for the measurement distribution is the Normal distribution. This comes from the Central Limit Theorem (CLT) which essentially states that, given enough interacting mechanisms, the average values of things "tend to become normally distributed". The true statement of the CLT is much more complex, but that is a decent working definition for practical use. The normal distribution is defined by two parameters, $\mu$ and $\sigma$, and is given by the following function:

\[ f(x;\mu,\sigma) = \frac{1}{\sigma\sqrt{2\pi}}\exp\left(\frac{-(x-\mu)^2}{2\sigma^2}\right) \]

This is a bell curve centered at $\mu$ with a variance of $\sigma$. Our best guess for the output, i.e. the model's prediction, should be the average measurement, meaning that $\mu$ is the result from the simulator. $\sigma$ is a parameter for how much measurement error we expect (some intuition on $\sigma$ will come soon).

Let's return to thinking about the ODE example. In this case, we have $\theta$ as a vector of random variables. This means that $u(t;\theta)$ is a random variable for the ODE $u'= ...$'s solution at a given point in time $t$. If we have a measurement at a time $t_i$ and assume our measurement noise is normally distributed with some constant measurement noise $\sigma$, then the likelihood of our data would be $f(x_i;u(t_i;\theta),\sigma)$ at each data point $(t_i,x_i)$. From probability we know that seeing the composition of events is given by the multiplication of probabilities, so the probability of seeing the full dataset given observations $D = (t_i,x_i)$ along the timeseries is:

\[ p(D|\theta) = \prod_i f(x_i;u(t_i;\theta),\sigma) \]

This can be read as: solve the model with the given parameters, and the probability of having seen the measurement is thus given by a product of normal distribution calculations. Note that in many cases the product is not numerically stable (and grows exponentially), and so the likelihood is transformed to the log-likelihood. To get this expression, we take the log of both sides and notice that the product becomes a summation, and thus:

\[ \begin{align} \log p(D|\theta) &= \sum_i \log f(x_i;u(t_i;\theta),\sigma)\\ &= \frac{N}{\log(\sqrt{2\pi}\sigma)} + \frac{1}{2\sigma^2} \sum_i -(x_i - u(t_i; \theta))^2 \end{align} \]

Notice that maximizing this log-likelihood is equivalent to minimizing the L2 norm of the solution against the data!. Thus we can see a few things:

  1. Previous parameter estimation by minimizing a norm against data can be seen as maximum likelihood with some measurement distribution. L2 norm corresponds to assuming measurement noise is normally distributed and all of the measurements have the same error variance.

  2. By the same derivation, having different error variances with normally distributed errors is equivalent to doing weighted L2 estimation.

This reformulation (generalization?) to likelihoods of probability distributions is known as maximum likelihood estimation (MLE), but is equivalent to our previous forms of parameter estimation using point estimates against data. However, this calculation is ignoring Bayes' rule, and is thus not finding the parameters which have the highest probability. To do that, we need to go back to Bayes' rule which states that:

\[ \log p(\theta|D) = \log p(D|\theta) + \log p(\theta) - C \]

Thus, maximizing the log-likelihood is "almost" the same as finding the most probable parameters, except that we need to add weights given $\log p(\theta)$ from our prior distribution! If we assume our prior distribution is flat, like a uniform distribution, then we have a non-informative prior and the maximum posterior point matches that of the maximum likelihood estimation. However, this formulation allows us to get point estimates in a way that takes into account prior knowledge, and is call maximum a posteriori estimation (MAP).

Bayesian Estimation of Posterior Distributions with Monte Carlo

The previous discussion still solely focused on getting point estimates for the most probable parameters. However, what if we wanted to find the distributions of the parameters, i.e. the full $p(D|\theta)$? Outside of very few small models, this cannot be done analytically and is thus the basic problem of probabilistic programming. There are two general approaches:

  1. Sampling-based approaches. Sample parameters $\theta_i$ in such a manner that the array $[\theta_i]$ converges to an array sampled from the true distribution, and thus with enough samples one can capture the distribution numerically.

  2. Variational inference. Find some way to represent the probability distribution and push forward the distributions at every step of the program.

Recovering Distributions from Sampled Points

It's clear from above that if you have a distribution, like Normal(5,1), that you can sample from the distribution to get an array of values which follow the distribution. However, in order for the following sampling approaches to make sense, we need to see how to recover a distribution from discrete samples. So let's say you had a bunch of normally distributed points:

 X = Normal(5,1)
 x = [rand(X) for i in 1:100]
 scatter(x,[1 for i in 1:100])
-

Notice that there are more points in the areas of higher probability. Thus the density of sampled points gives us an estimate for the probability of having points in a given area. We can then count the number of points in a bin and divide by the total number of points in order to get the probability of being in a specific region. This is depicted by a histogram:

+

Notice that there are more points in the areas of higher probability. Thus the density of sampled points gives us an estimate for the probability of having points in a given area. We can then count the number of points in a bin and divide by the total number of points in order to get the probability of being in a specific region. This is depicted by a histogram:

 histogram(x)
-

and we see this converges when we get more points:

+

and we see this converges when we get more points:

 histogram([rand(X) for i in 1:10000],normed=true)
 using StatsPlots
 plot!(X,lw=5)
-

A continuous form of this is the kernel density estimate, which is essentially a smoothed binning approach.

+

A continuous form of this is the kernel density estimate, which is essentially a smoothed binning approach.

 using KernelDensity
 plot(kde([rand(X) for i in 1:10000]),lw=5)
 plot!(X,lw=5)
-

Thus, for the sampling-based approaches, we simply need to arrive at an array which is sampled according to the distribution that we want to estimate, and from that array we can recover the distribution.

Sampling Distributions with the Metropolis-Hastings Algorithm

The Metropolis-Hastings algorithm is the simplest form of Markov Chain Monte Carlo (MCMC) which gives a way of sampling the $\theta$ distribution. To see how this algorithm works, let's understand the ratio between two points in the posterior probability. If we have $x_i$ and $x_j$, the ratio of the two probabilities would be given by:

\[ \frac{p(x_i|D)}{p(x_j|D)} = \frac{p(D|x_i)p(x_i)}{p(D|x_j)p(x_j)} \]

(notice that the integration constant cancels). This motivates the idea that all we have to do is ensure we only go to a point $x_j$ from $x_i$ with probability difference that matches that ratio, and over time if we do this between "all points" we will have the right number of "each point" in the distribution (quotes because it's continuous). With a bit more rigour we arrive at the following algorithm:

  1. Starting at $x_i$, take $x_{i+1}$ from a sampling algorithm $g(x_{i+1}|x_i)$.

  2. Calculate $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})g(x_i|x_{i+1})}{p(D|x_i)p(x_i)g(x_{i+1}|x_i)}\right)$. Notice that if we require $g$ to be symmetric, then this simplifies to the probability ratio $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})}{p(D|x_i)p(x_i)}\right)$

  3. Use a random number to accept the step with a probability $A$. Go back to step 1, incrementing $i$ if accepted, otherwise just repeat.

I.e, we just walk around the space biasing the acceptance of a step by the factor $\frac{p(x_i|D)}{p(x_j|D)}$ and sooner or later we will have spent the right amount of time in each area, giving the correct distribution.

(This can be rigorously proven, and those details are left out.)

The Cost of Bayesian Estimation

Let's take a quick moment to understand the high cost of Bayesian posterior estimations. While before we were getting point estimates, now we are trying to recover a full probability distribution, and each accept/reject probability calculation requires evaluating the likelihood at some point. Remember, the likelihood is generated by our simulator, and thus every evaluation here is an ODE solver call or a neural network forward pass! This means that to get good distributions, we are solving the ODE hundreds of thousands of times, i.e. even more than when doing parameter estimation! This is something to keep in mind.

However, notice that this process is trivially parallelizable. We can just have parallel chains on going, i.e. start 16 processes all doing Metropolis-Hastings, and in the end they are all sampling from the same distribution, so the final array can simply be the pooled results of each chain.

Hamiltonian Monte Carlo

Metropolis-Hastings is easy to motivate and implement. However, it does not do well in high dimensional spaces because it searches in all directions. For example, it's common for the sampling distribution $g$ to be a multivariable distribution (i.e. normal in all directions). However, high dimensional objects commonly sit on low dimensional manifolds (known as the manifold hypothesis). If that's the case, the most probable set of parameters is something that is low dimensional. For example, parameters may compensate for one another, and so $\theta_1^2 + \theta_2^2 + \theta_3^2 = 1$ might be the manifold on which all of the most probable choices for $\theta$ lie, in which case we need sample on the sphere instead of all of $\mathbb{R}^3$.

However, it's quick to see that this will give Metropolis-Hastings some trouble, since it will use a normal distribution around the current point, and thus even if we start on the sphere, it will have a high chance of trying a point not on the sphere in the next round! This can be depicted as:

Recall that every single rejection is still evaluating the likelihood (since it's calculating an acceptance probability, finding it near zero, rejecting and starting again), and every likelihood call is calling our simulator, and so this is sllllllooooooooooooooowwwwwwwww in high dimensions!

What we need to do instead is ensure that we walk along the path of high probability. What we want to do is thus build a vector field that matches our high probability regions

and follow said vector field (following a vector field is solving what kind of equation?). The first idea one might have is to use the gradient. However, while this idea has the right intentions, the issue is that the gradient of the probability will average out all of the possible probabilities, and will thus flow towards the mode of the distribution:

To overcome this issue, we look to physical systems and see that a satellite orbiting a planet always nicely stays on some manifold instead of following the gradient:

The reason why it does is because it has momentum. Recall from basic physics that one way to describe a physical system is through Hamiltonian mechanics, where $H(x,p)$ is the energy associated with the state $(x,p)$ (normally $x$ is location and $p$ is momentum). Due to conservation of energy, the solution of the dynamical equations leads to $H(x,p)$ being constant, and thus the dynamics follow the level sets of $H$. From the Hamiltonian the dynamics of the system are:

\[ \begin{align} \frac{dx}{dt} &= \frac{dH}{dp}\\ &= -\frac{dH}{dx} \end{align} \]

Here we want our Hamiltonian to be our posterior probability, so that way we stay on the manifold of high probability. This means:

\[ H(x,p) = - \log \pi(x,p) \]

where $\pi(x,p) = \pi(p|x)\pi(x)$ (where I am now using $pi$ for probability since $p$ is momentum!). So to lift from a probability over parameters to one that includes momentum, we simply need to choose a conditional distribution $\pi(p|x)$. This would mean that

\[ \begin{align} H(x,p) &= -log \pi(p|x) - \log \pi(x)\\ &= K(p,x) + V(x) \end{align} \]

where $K$ is the kinetic energy and $V$ is the potential. Thus the potential energy is directly given by the posterior calculation, and the kinetic energy is thus a choice that is used to build the correct Hamiltonian. Hamiltonian Monte Carlo methods then dig into good ways to choose the kinetic energy function. This is done at the start (along with the choice of ODE solver time step) in such a way that it maximizes acceptance probabilities.

Connections to Differentiable Programming

\[ -\frac{dH}{dx} \]

requires calculating the gradient of the likelihood function with respect to the parameters, so we are once again using the gradient of our simulator! This means that all of our previous discussion on automatic differentiation and differentiable programming applies to the Hamiltonian Monte Carlo context.

There's another thread to follow that transformations of probability distributions are pushforwards of the Jacobian transformations (given the transformation of an integral formula), and this is used when doing variational inference.

Symplectic and Geometric Integration

One way to integrate the system of ODEs which result from the Hamiltonian system is to convert it to a system of first order ODEs and solve it directly. However, this loses information and can result in drift. This is demonstrated by looking at the long time solution of the pendulum:

+

Thus, for the sampling-based approaches, we simply need to arrive at an array which is sampled according to the distribution that we want to estimate, and from that array we can recover the distribution.

Sampling Distributions with the Metropolis-Hastings Algorithm

The Metropolis-Hastings algorithm is the simplest form of Markov Chain Monte Carlo (MCMC) which gives a way of sampling the $\theta$ distribution. To see how this algorithm works, let's understand the ratio between two points in the posterior probability. If we have $x_i$ and $x_j$, the ratio of the two probabilities would be given by:

\[ \frac{p(x_i|D)}{p(x_j|D)} = \frac{p(D|x_i)p(x_i)}{p(D|x_j)p(x_j)} \]

(notice that the integration constant cancels). This motivates the idea that all we have to do is ensure we only go to a point $x_j$ from $x_i$ with probability difference that matches that ratio, and over time if we do this between "all points" we will have the right number of "each point" in the distribution (quotes because it's continuous). With a bit more rigour we arrive at the following algorithm:

  1. Starting at $x_i$, take $x_{i+1}$ from a sampling algorithm $g(x_{i+1}|x_i)$.

  2. Calculate $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})g(x_i|x_{i+1})}{p(D|x_i)p(x_i)g(x_{i+1}|x_i)}\right)$. Notice that if we require $g$ to be symmetric, then this simplifies to the probability ratio $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})}{p(D|x_i)p(x_i)}\right)$

  3. Use a random number to accept the step with a probability $A$. Go back to step 1, incrementing $i$ if accepted, otherwise just repeat.

I.e, we just walk around the space biasing the acceptance of a step by the factor $\frac{p(x_i|D)}{p(x_j|D)}$ and sooner or later we will have spent the right amount of time in each area, giving the correct distribution.

(This can be rigorously proven, and those details are left out.)

The Cost of Bayesian Estimation

Let's take a quick moment to understand the high cost of Bayesian posterior estimations. While before we were getting point estimates, now we are trying to recover a full probability distribution, and each accept/reject probability calculation requires evaluating the likelihood at some point. Remember, the likelihood is generated by our simulator, and thus every evaluation here is an ODE solver call or a neural network forward pass! This means that to get good distributions, we are solving the ODE hundreds of thousands of times, i.e. even more than when doing parameter estimation! This is something to keep in mind.

However, notice that this process is trivially parallelizable. We can just have parallel chains on going, i.e. start 16 processes all doing Metropolis-Hastings, and in the end they are all sampling from the same distribution, so the final array can simply be the pooled results of each chain.

Hamiltonian Monte Carlo

Metropolis-Hastings is easy to motivate and implement. However, it does not do well in high dimensional spaces because it searches in all directions. For example, it's common for the sampling distribution $g$ to be a multivariable distribution (i.e. normal in all directions). However, high dimensional objects commonly sit on low dimensional manifolds (known as the manifold hypothesis). If that's the case, the most probable set of parameters is something that is low dimensional. For example, parameters may compensate for one another, and so $\theta_1^2 + \theta_2^2 + \theta_3^2 = 1$ might be the manifold on which all of the most probable choices for $\theta$ lie, in which case we need sample on the sphere instead of all of $\mathbb{R}^3$.

However, it's quick to see that this will give Metropolis-Hastings some trouble, since it will use a normal distribution around the current point, and thus even if we start on the sphere, it will have a high chance of trying a point not on the sphere in the next round! This can be depicted as:

Recall that every single rejection is still evaluating the likelihood (since it's calculating an acceptance probability, finding it near zero, rejecting and starting again), and every likelihood call is calling our simulator, and so this is sllllllooooooooooooooowwwwwwwww in high dimensions!

What we need to do instead is ensure that we walk along the path of high probability. What we want to do is thus build a vector field that matches our high probability regions

and follow said vector field (following a vector field is solving what kind of equation?). The first idea one might have is to use the gradient. However, while this idea has the right intentions, the issue is that the gradient of the probability will average out all of the possible probabilities, and will thus flow towards the mode of the distribution:

To overcome this issue, we look to physical systems and see that a satellite orbiting a planet always nicely stays on some manifold instead of following the gradient:

The reason why it does is because it has momentum. Recall from basic physics that one way to describe a physical system is through Hamiltonian mechanics, where $H(x,p)$ is the energy associated with the state $(x,p)$ (normally $x$ is location and $p$ is momentum). Due to conservation of energy, the solution of the dynamical equations leads to $H(x,p)$ being constant, and thus the dynamics follow the level sets of $H$. From the Hamiltonian the dynamics of the system are:

\[ \begin{align} \frac{dx}{dt} &= \frac{dH}{dp}\\ &= -\frac{dH}{dx} \end{align} \]

Here we want our Hamiltonian to be our posterior probability, so that way we stay on the manifold of high probability. This means:

\[ H(x,p) = - \log \pi(x,p) \]

where $\pi(x,p) = \pi(p|x)\pi(x)$ (where I am now using $pi$ for probability since $p$ is momentum!). So to lift from a probability over parameters to one that includes momentum, we simply need to choose a conditional distribution $\pi(p|x)$. This would mean that

\[ \begin{align} H(x,p) &= -log \pi(p|x) - \log \pi(x)\\ &= K(p,x) + V(x) \end{align} \]

where $K$ is the kinetic energy and $V$ is the potential. Thus the potential energy is directly given by the posterior calculation, and the kinetic energy is thus a choice that is used to build the correct Hamiltonian. Hamiltonian Monte Carlo methods then dig into good ways to choose the kinetic energy function. This is done at the start (along with the choice of ODE solver time step) in such a way that it maximizes acceptance probabilities.

Connections to Differentiable Programming

\[ -\frac{dH}{dx} \]

requires calculating the gradient of the likelihood function with respect to the parameters, so we are once again using the gradient of our simulator! This means that all of our previous discussion on automatic differentiation and differentiable programming applies to the Hamiltonian Monte Carlo context.

There's another thread to follow that transformations of probability distributions are pushforwards of the Jacobian transformations (given the transformation of an integral formula), and this is used when doing variational inference.

Symplectic and Geometric Integration

One way to integrate the system of ODEs which result from the Hamiltonian system is to convert it to a system of first order ODEs and solve it directly. However, this loses information and can result in drift. This is demonstrated by looking at the long time solution of the pendulum:

 using ParameterizedFunctions
 u0 = [1.,0.]
 harmonic! = @ode_def HarmonicOscillator begin
diff --git a/_weave/lecture17/global_sensitivity/index.html b/_weave/lecture17/global_sensitivity/index.html
index 6913e00c..8a81b84c 100644
--- a/_weave/lecture17/global_sensitivity/index.html
+++ b/_weave/lecture17/global_sensitivity/index.html
@@ -9,4 +9,4 @@ 

Global Sensitivity Analysis

Chris Rackauckas
using LatinHypercubeSampling p = LHCoptim(120,2,1000) scatter(p[1][:,1],p[1][:,2]) -

For a reference library with many different quasi-Monte Carlo samplers, check out QuasiMonteCarlo.jl.

Fourier Amplitude Sensitivity Sampling (FAST) and eFAST

The FAST method is a change to the Sobol method to allow for faster convergence. First transform the variables $x_i$ onto the space $[0,1]$. Then, instead of the linear decomposition, one decomposes into a Fourier basis:

\[ f(x_i,x_2,\ldots,x_n) = \sum_{m_1 = -\infty}^{\infty} \ldots \sum_{m_n = -\infty}^{\infty} C_{m_1m_2\ldots m_n}\exp\left(2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

where

\[ C_{m_1m_2\ldots m_n} = \int_0^1 \ldots \int_0^1 f(x_i,x_2,\ldots,x_n) \exp\left(-2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

The ANOVA like decomposition is thus

\[ f_0 = C_{0\ldots 0} \]

\[ f_j = \sum_{m_j \neq 0} C_{0\ldots 0 m_j 0 \ldots 0} \exp (2\pi i m_j x_j) \]

\[ f_{jk} = \sum_{m_j \neq 0} \sum_{m_k \neq 0} C_{0\ldots 0 m_j 0 \ldots m_k 0 \ldots 0} \exp \left(2\pi i (m_j x_j + m_k x_k)\right) \]

The first order conditional variance is thus:

\[ V_j = \int_0^1 f_j^2 (x_j) dx_j = \sum_{m_j \neq 0} |C_{0\ldots 0 m_j 0 \ldots 0}|^2 \]

or

\[ V_j = 2\sum_{m_j = 1}^\infty \left(A_{m_j}^2 + B_{m_j}^2 \right) \]

where $C_{0\ldots 0 m_j 0 \ldots 0} = A_{m_j} + i B_{m_j}$. By Fourier series we know this to be:

\[ A_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\cos(2\pi m_j x_j)dx \]

\[ B_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\sin(2\pi m_j x_j)dx \]

Implementation via the Ergodic Theorem

Define

\[ X_j(s) = \frac{1}{2\pi} (\omega_j s \mod 2\pi) \]

By the ergodic theorem, if $\omega_j$ are irrational numbers, then the dynamical system will never repeat values and thus it will create a solution that is dense in the plane (Let's prove a bit later). As an animation:

(here, $\omega_1 = \pi$ and $\omega_2 = 7$)

This means that:

\[ A_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\sin(m_j \omega_j s)ds \]

i.e. the multidimensional integral can be approximated by the integral over a single line.

One can satisfy this approximately to get a simpler form for the integral. Using $\omega_i$ as integers, the integral is periodic and so only integrating over $2\pi$ is required. This would mean that:

\[ A_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\sin(m_j \omega_j s)ds \]

It's only approximate since the sequence cannot be dense. For example, with $\omega_1 = 11$ and $\omega_2 = 7$:

A higher period thus gives a better fill of the space and thus a better approximation, but may require a more points. However, this transformation makes the true integrals simple one dimensional quadratures which can be efficiently computed.

To get the total index from this method, one can calculate the total contribution of the complementary set, i.e. $V_{c_i} = \sum_{j \neq i} V_j$ and then

\[ S_{T_i} = 1 - S_{c_i} \]

Note that this then is a fast measure for the total contribution of variable $i$, including all higher-order nonlinear interactions, all from one-dimensional integrals! (This extension is called extended FAST or eFAST)

Proof of the Ergodic Theorem

Look at the map $x_{n+1} = x_n + \alpha (\text{mod} 1)$, where $\alpha$ is irrational. This is the irrational rotation map that corresponds to our problem. We wish to prove that in any interval $I$, there is a point of our orbit in this interval.

First let's prove a useful result: our points get arbitrarily close. Assume that for some finite $\epsilon$ that no two points are $\epsilon$ apart. This means that we at most have spacings of $\epsilon$ between the points, and thus we have at most $\frac{2\pi}{\epsilon}$ points (rounded up). This means our orbit is periodic. This means that there is a $p$ such that

\[ x_{n+p} = x_n \]

which means that $p \alpha = 1$ or $p = \frac{1}{\alpha}$ which is a contradiction since $\alpha$ is irrational.

Thus for every $\epsilon$ there are two points which are $\epsilon$ apart. Now take any arbitrary $I$. Let $\epsilon < d/2$ where $d$ is the length of the interval. We have just shown that there are two points $\epsilon$ apart, so there is a point that is $x_{n+m}$ and $x_{n+k}$ which are $<\epsilon$ apart. Assuming WLOG $m>k$, this means that $m-k$ rotations takes one from $x_{n+k}$ to $x_{n+m}$, and so $m-k$ rotations is a rotation by $\epsilon$. If we do $\frac{1}{\epsilon}$ rounded up rotations, we will then cover the space with intervals of length epsilon, each with one point of the orbit in it. Since $\epsilon < d/2$, one of those intervals is completely encapsulated in $I$, which means there is at least one point in our orbit that is in $I$.

Thus for every interval we have at least one point in our orbit that lies in it, proving that the rotation map with irrational $\alpha$ is dense. Note that during the proof we essentially showed as well that if $\alpha$ is rational, then the map is periodic based on the denominator of the map in its reduced form.

A Quick Note on Parallelism

Very quick note: all of these are hyper parallel since it does the same calculation per parameter or trajectory, and each calculation is long. For quasi-Monte Carlo, after generating "good enough" trajectories, one can evaluate the model at all points in parallel, and then simply do the GSA index measurement. For FAST, one can do each quadrature in parallel.

\ No newline at end of file +

For a reference library with many different quasi-Monte Carlo samplers, check out QuasiMonteCarlo.jl.

Fourier Amplitude Sensitivity Sampling (FAST) and eFAST

The FAST method is a change to the Sobol method to allow for faster convergence. First transform the variables $x_i$ onto the space $[0,1]$. Then, instead of the linear decomposition, one decomposes into a Fourier basis:

\[ f(x_i,x_2,\ldots,x_n) = \sum_{m_1 = -\infty}^{\infty} \ldots \sum_{m_n = -\infty}^{\infty} C_{m_1m_2\ldots m_n}\exp\left(2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

where

\[ C_{m_1m_2\ldots m_n} = \int_0^1 \ldots \int_0^1 f(x_i,x_2,\ldots,x_n) \exp\left(-2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

The ANOVA like decomposition is thus

\[ f_0 = C_{0\ldots 0} \]

\[ f_j = \sum_{m_j \neq 0} C_{0\ldots 0 m_j 0 \ldots 0} \exp (2\pi i m_j x_j) \]

\[ f_{jk} = \sum_{m_j \neq 0} \sum_{m_k \neq 0} C_{0\ldots 0 m_j 0 \ldots m_k 0 \ldots 0} \exp \left(2\pi i (m_j x_j + m_k x_k)\right) \]

The first order conditional variance is thus:

\[ V_j = \int_0^1 f_j^2 (x_j) dx_j = \sum_{m_j \neq 0} |C_{0\ldots 0 m_j 0 \ldots 0}|^2 \]

or

\[ V_j = 2\sum_{m_j = 1}^\infty \left(A_{m_j}^2 + B_{m_j}^2 \right) \]

where $C_{0\ldots 0 m_j 0 \ldots 0} = A_{m_j} + i B_{m_j}$. By Fourier series we know this to be:

\[ A_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\cos(2\pi m_j x_j)dx \]

\[ B_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\sin(2\pi m_j x_j)dx \]

Implementation via the Ergodic Theorem

Define

\[ X_j(s) = \frac{1}{2\pi} (\omega_j s \mod 2\pi) \]

By the ergodic theorem, if $\omega_j$ are irrational numbers, then the dynamical system will never repeat values and thus it will create a solution that is dense in the plane (Let's prove a bit later). As an animation:

(here, $\omega_1 = \pi$ and $\omega_2 = 7$)

This means that:

\[ A_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\sin(m_j \omega_j s)ds \]

i.e. the multidimensional integral can be approximated by the integral over a single line.

One can satisfy this approximately to get a simpler form for the integral. Using $\omega_i$ as integers, the integral is periodic and so only integrating over $2\pi$ is required. This would mean that:

\[ A_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\sin(m_j \omega_j s)ds \]

It's only approximate since the sequence cannot be dense. For example, with $\omega_1 = 11$ and $\omega_2 = 7$:

A higher period thus gives a better fill of the space and thus a better approximation, but may require a more points. However, this transformation makes the true integrals simple one dimensional quadratures which can be efficiently computed.

To get the total index from this method, one can calculate the total contribution of the complementary set, i.e. $V_{c_i} = \sum_{j \neq i} V_j$ and then

\[ S_{T_i} = 1 - S_{c_i} \]

Note that this then is a fast measure for the total contribution of variable $i$, including all higher-order nonlinear interactions, all from one-dimensional integrals! (This extension is called extended FAST or eFAST)

Proof of the Ergodic Theorem

Look at the map $x_{n+1} = x_n + \alpha (\text{mod} 1)$, where $\alpha$ is irrational. This is the irrational rotation map that corresponds to our problem. We wish to prove that in any interval $I$, there is a point of our orbit in this interval.

First let's prove a useful result: our points get arbitrarily close. Assume that for some finite $\epsilon$ that no two points are $\epsilon$ apart. This means that we at most have spacings of $\epsilon$ between the points, and thus we have at most $\frac{2\pi}{\epsilon}$ points (rounded up). This means our orbit is periodic. This means that there is a $p$ such that

\[ x_{n+p} = x_n \]

which means that $p \alpha = 1$ or $p = \frac{1}{\alpha}$ which is a contradiction since $\alpha$ is irrational.

Thus for every $\epsilon$ there are two points which are $\epsilon$ apart. Now take any arbitrary $I$. Let $\epsilon < d/2$ where $d$ is the length of the interval. We have just shown that there are two points $\epsilon$ apart, so there is a point that is $x_{n+m}$ and $x_{n+k}$ which are $<\epsilon$ apart. Assuming WLOG $m>k$, this means that $m-k$ rotations takes one from $x_{n+k}$ to $x_{n+m}$, and so $m-k$ rotations is a rotation by $\epsilon$. If we do $\frac{1}{\epsilon}$ rounded up rotations, we will then cover the space with intervals of length epsilon, each with one point of the orbit in it. Since $\epsilon < d/2$, one of those intervals is completely encapsulated in $I$, which means there is at least one point in our orbit that is in $I$.

Thus for every interval we have at least one point in our orbit that lies in it, proving that the rotation map with irrational $\alpha$ is dense. Note that during the proof we essentially showed as well that if $\alpha$ is rational, then the map is periodic based on the denominator of the map in its reduced form.

A Quick Note on Parallelism

Very quick note: all of these are hyper parallel since it does the same calculation per parameter or trajectory, and each calculation is long. For quasi-Monte Carlo, after generating "good enough" trajectories, one can evaluate the model at all points in parallel, and then simply do the GSA index measurement. For FAST, one can do each quadrature in parallel.

\ No newline at end of file diff --git a/_weave/lecture17/jl_TDBscy/global_sensitivity_2_1.png b/_weave/lecture17/jl_TDBscy/global_sensitivity_2_1.png deleted file mode 100644 index 9420cf0efc52a3ddc5cdb495e828033150345cbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33377 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 diff --git a/_weave/lecture17/jl_l1lcWF/global_sensitivity_2_1.png b/_weave/lecture17/jl_l1lcWF/global_sensitivity_2_1.png new file mode 100644 index 0000000000000000000000000000000000000000..08f752b6427b7fd99fa0e41492bbe6a7d3b213d5 GIT binary patch literal 33238 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�FxdV$gTJj?6zQJg?Lyb#r4} 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 literal 0 HcmV?d00001 diff --git a/_weave/lecture18/jl_OKpj4T/code_profiling_5_1.png b/_weave/lecture18/jl_bQVm2t/code_profiling_5_1.png similarity index 100% rename from _weave/lecture18/jl_OKpj4T/code_profiling_5_1.png rename to _weave/lecture18/jl_bQVm2t/code_profiling_5_1.png diff --git a/_weave/lecture19/jl_gJdXoT/uncertainty_programming_1_1.png b/_weave/lecture19/jl_1YHT0C/uncertainty_programming_1_1.png similarity index 100% rename from _weave/lecture19/jl_gJdXoT/uncertainty_programming_1_1.png rename to _weave/lecture19/jl_1YHT0C/uncertainty_programming_1_1.png diff --git a/notes/02-Optimizing_Serial_Code/index.html b/notes/02-Optimizing_Serial_Code/index.html index 13f96603..81cedc22 100644 --- a/notes/02-Optimizing_Serial_Code/index.html +++ b/notes/02-Optimizing_Serial_Code/index.html @@ -10,7 +10,7 @@ end @btime inner_rows!(C,A,B)
-7.654 μs (0 allocations: 0 bytes)
+7.587 μs (0 allocations: 0 bytes)
 
 function inner_cols!(C,A,B)
   for j in 1:100, i in 1:100
@@ -19,7 +19,7 @@
 end
 @btime inner_cols!(C,A,B)
 
-2.414 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 

Lower Level View: The Stack and the Heap

Locally, the stack is composed of a stack and a heap. The stack requires a static allocation: it is ordered. Because it's ordered, it is very clear where things are in the stack, and therefore accesses are very quick (think instantaneous). However, because this is static, it requires that the size of the variables is known at compile time (to determine all of the variable locations). Since that is not possible with all variables, there exists the heap. The heap is essentially a stack of pointers to objects in memory. When heap variables are needed, their values are pulled up the cache chain and accessed.

Heap Allocations and Speed

Heap allocations are costly because they involve this pointer indirection, so stack allocation should be done when sensible (it's not helpful for really large arrays, but for small values like scalars it's essential!)

 function inner_alloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -29,7 +29,7 @@
 end
 @btime inner_alloc!(C,A,B)
 
-234.700 μs (10000 allocations: 625.00 KiB)
+234.498 μs (10000 allocations: 625.00 KiB)
 
 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -39,7 +39,7 @@
 end
 @btime inner_noalloc!(C,A,B)
 
-2.413 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 

Why does the array here get heap-allocated? It isn't able to prove/guarantee at compile-time that the array's size will always be a given value, and thus it allocates it to the heap. @btime tells us this allocation occurred and shows us the total heap memory that was taken. Meanwhile, the size of a Float64 number is known at compile-time (64-bits), and so this is stored onto the stack and given a specific location that the compiler will be able to directly address.

Note that one can use the StaticArrays.jl library to get statically-sized arrays and thus arrays which are stack-allocated:

 using StaticArrays
 function static_inner_alloc!(C,A,B)
@@ -50,7 +50,7 @@
 end
 @btime static_inner_alloc!(C,A,B)
 
-2.411 μs (0 allocations: 0 bytes)
+2.388 μs (0 allocations: 0 bytes)
 

Mutation to Avoid Heap Allocations

Many times you do need to write into an array, so how can you write into an array without performing a heap allocation? The answer is mutation. Mutation is changing the values of an already existing array. In that case, no free memory has to be found to put the array (and no memory has to be freed by the garbage collector).

In Julia, functions which mutate the first value are conventionally noted by a !. See the difference between these two equivalent functions:

 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -60,7 +60,7 @@
 end
 @btime inner_noalloc!(C,A,B)
 
-2.413 μs (0 allocations: 0 bytes)
+2.385 μs (0 allocations: 0 bytes)
 
 function inner_alloc(A,B)
   C = similar(A)
@@ -71,7 +71,7 @@
 end
 @btime inner_alloc(A,B)
 
-6.807 μs (2 allocations: 78.17 KiB)
+6.803 μs (2 allocations: 78.17 KiB)
 

To use this algorithm effectively, the ! algorithm assumes that the caller already has allocated the output array to put as the output argument. If that is not true, then one would need to manually allocate. The goal of that interface is to give the caller control over the allocations to allow them to manually reduce the total number of heap allocations and thus increase the speed.

Julia's Broadcasting Mechanism

Wouldn't it be nice to not have to write the loop there? In many high level languages this is simply called vectorization. In Julia, we will call it array vectorization to distinguish it from the SIMD vectorization which is common in lower level languages like C, Fortran, and Julia.

In Julia, if you use . on an operator it will transform it to the broadcasted form. Broadcast is lazy: it will build up an entire .'d expression and then call broadcast! on composed expression. This is customizable and documented in detail. However, to a first approximation we can think of the broadcast mechanism as a mechanism for building fused expressions. For example, the Julia code:

 A .+ B .+ C;
 

under the hood lowers to something like:

@@ -86,29 +86,29 @@
 end
 @btime unfused(A,B,C);
 
-6.622 μs (4 allocations: 156.34 KiB)
+9.294 μs (4 allocations: 156.34 KiB)
 
 fused(A,B,C) = A .+ B .+ C
 @btime fused(A,B,C);
 
-4.630 μs (2 allocations: 78.17 KiB)
+4.433 μs (2 allocations: 78.17 KiB)
 

Note that we can also fuse the output by using .=. This is essentially the vectorized version of a ! function:

 D = similar(A)
 fused!(D,A,B,C) = (D .= A .+ B .+ C)
 @btime fused!(D,A,B,C);
 
-3.436 μs (0 allocations: 0 bytes)
+3.441 μs (0 allocations: 0 bytes)
 

Note on Broadcasting Function Calls

Julia allows for broadcasting the call () operator as well. .() will call the function element-wise on all arguments, so sin.(A) will be the elementwise sine function. This will fuse Julia like the other operators.

Note on Vectorization and Speed

In articles on MATLAB, Python, R, etc., this is where you will be told to vectorize your code. Notice from above that this isn't a performance difference between writing loops and using vectorized broadcasts. This is not abnormal! The reason why you are told to vectorize code in these other languages is because they have a high per-operation overhead (which will be discussed further down). This means that every call, like +, is costly in these languages. To get around this issue and make the language usable, someone wrote and compiled the loop for the C/Fortran function that does the broadcasted form (see numpy's Github repo). Thus A .+ B's MATLAB/Python/R equivalents are calling a single C function to generally avoid the cost of function calls and thus are faster.

But this is not an intrinsic property of vectorization. Vectorization isn't "fast" in these languages, it's just close to the correct speed. The reason vectorization is recommended is because looping is slow in these languages. Because looping isn't slow in Julia (or C, C++, Fortran, etc.), loops and vectorization generally have the same speed. So use the one that works best for your code without a care about performance.

(As a small side effect, these high level languages tend to allocate a lot of temporary variables since the individual C kernels are written for specific numbers of inputs and thus don't naturally fuse. Julia's broadcast mechanism is just generating and JIT compiling Julia functions on the fly, and thus it can accommodate the combinatorial explosion in the amount of choices just by only compiling the combinations that are necessary for a specific code)

Heap Allocations from Slicing

It's important to note that slices in Julia produce copies instead of views. Thus for example:

 A[50,50]
 
-0.17778218519466293
+0.4378374168708473
 

allocates a new output. This is for safety, since if it pointed to the same array then writing to it would change the original array. We can demonstrate this by asking for a view instead of a copy.

 @show A[1]
 E = @view A[1:5,1:5]
 E[1] = 2.0
 @show A[1]
 
-A[1] = 0.5530951950733606
+A[1] = 0.37233258859606955
 A[1] = 2.0
 2.0
 

However, this means that @view A[1:5,1:5] did not allocate an array (it does allocate a pointer if the escape analysis is unable to prove that it can be elided. This means that in small loops there will be no allocation, while if the view is returned from a function for example it will allocate the pointer, ~80 bytes, but not the memory of the array. This means that it is O(1) in cost but with a relatively small constant).

Asymptotic Cost of Heap Allocations

Heap allocations have to locate and prepare a space in RAM that is proportional to the amount of memory that is calculated, which means that the cost of a heap allocation for an array is O(n), with a large constant. As RAM begins to fill up, this cost dramatically increases. If you run out of RAM, your computer may begin to use swap, which is essentially RAM simulated on your hard drive. Generally when you hit swap your performance is so dead that you may think that your computation froze, but if you check your resource use you will notice that it's actually just filled the RAM and starting to use the swap.

But think of it as O(n) with a large constant factor. This means that for operations which only touch the data once, heap allocations can dominate the computational cost:

@@ -130,7 +130,7 @@
 plot(ns,alloc,label="=",xscale=:log10,yscale=:log10,legend=:bottomright,
      title="Micro-optimizations matter for BLAS1")
 plot!(ns,noalloc,label=".=")
-

However, when the computation takes O(n^3), like in matrix multiplications, the high constant factor only comes into play when the matrices are sufficiently small:

+

However, when the computation takes O(n^3), like in matrix multiplications, the high constant factor only comes into play when the matrices are sufficiently small:

 using LinearAlgebra, BenchmarkTools
 function alloc_timer(n)
     A = rand(n,n)
@@ -149,7 +149,7 @@
 plot(ns,alloc,label="*",xscale=:log10,yscale=:log10,legend=:bottomright,
      title="Micro-optimizations only matter for small matmuls")
 plot!(ns,noalloc,label="mul!")
-

Though using a mutating form is never bad and always is a little bit better.

Optimizing Memory Use Summary

  • Avoid cache misses by reusing values

  • Iterate along columns

  • Avoid heap allocations in inner loops

  • Heap allocations occur when the size of things is not proven at compile-time

  • Use fused broadcasts (with mutated outputs) to avoid heap allocations

  • Array vectorization confers no special benefit in Julia because Julia loops are as fast as C or Fortran

  • Use views instead of slices when applicable

  • Avoiding heap allocations is most necessary for O(n) algorithms or algorithms with small arrays

  • Use StaticArrays.jl to avoid heap allocations of small arrays in inner loops

Julia's Type Inference and the Compiler

Many people think Julia is fast because it is JIT compiled. That is simply not true (we've already shown examples where Julia code isn't fast, but it's always JIT compiled!). Instead, the reason why Julia is fast is because the combination of two ideas:

  • Type inference

  • Type specialization in functions

These two features naturally give rise to Julia's core design feature: multiple dispatch. Let's break down these pieces.

Type Inference

At the core level of the computer, everything has a type. Some languages are more explicit about said types, while others try to hide the types from the user. A type tells the compiler how to to store and interpret the memory of a value. For example, if the compiled code knows that the value in the register is supposed to be interpreted as a 64-bit floating point number, then it understands that slab of memory like:

Importantly, it will know what to do for function calls. If the code tells it to add two floating point numbers, it will send them as inputs to the Floating Point Unit (FPU) which will give the output.

If the types are not known, then... ? So one cannot actually compute until the types are known, since otherwise it's impossible to interpret the memory. In languages like C, the programmer has to declare the types of variables in the program:

void add(double *a, double *b, double *c, size_t n){
+

Though using a mutating form is never bad and always is a little bit better.

Optimizing Memory Use Summary

  • Avoid cache misses by reusing values

  • Iterate along columns

  • Avoid heap allocations in inner loops

  • Heap allocations occur when the size of things is not proven at compile-time

  • Use fused broadcasts (with mutated outputs) to avoid heap allocations

  • Array vectorization confers no special benefit in Julia because Julia loops are as fast as C or Fortran

  • Use views instead of slices when applicable

  • Avoiding heap allocations is most necessary for O(n) algorithms or algorithms with small arrays

  • Use StaticArrays.jl to avoid heap allocations of small arrays in inner loops

Julia's Type Inference and the Compiler

Many people think Julia is fast because it is JIT compiled. That is simply not true (we've already shown examples where Julia code isn't fast, but it's always JIT compiled!). Instead, the reason why Julia is fast is because the combination of two ideas:

  • Type inference

  • Type specialization in functions

These two features naturally give rise to Julia's core design feature: multiple dispatch. Let's break down these pieces.

Type Inference

At the core level of the computer, everything has a type. Some languages are more explicit about said types, while others try to hide the types from the user. A type tells the compiler how to to store and interpret the memory of a value. For example, if the compiled code knows that the value in the register is supposed to be interpreted as a 64-bit floating point number, then it understands that slab of memory like:

Importantly, it will know what to do for function calls. If the code tells it to add two floating point numbers, it will send them as inputs to the Floating Point Unit (FPU) which will give the output.

If the types are not known, then... ? So one cannot actually compute until the types are known, since otherwise it's impossible to interpret the memory. In languages like C, the programmer has to declare the types of variables in the program:

void add(double *a, double *b, double *c, size_t n){
   size_t i;
   for(i = 0; i < n; ++i) {
     c[i] = a[i] + b[i];
@@ -172,7 +172,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define i64 @julia_f_3082(i64 signext %0, i64 signext %1) #0 {
+define i64 @julia_f_3070(i64 signext %0, i64 signext %1) #0 {
 top:
 ; ┌ @ int.jl:87 within `+`
    %2 = add i64 %1, %0
@@ -184,7 +184,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define double @julia_f_3084(double %0, double %1) #0 {
+define double @julia_f_3072(double %0, double %1) #0 {
 top:
 ; ┌ @ float.jl:409 within `+`
    %2 = fadd double %0, %1
@@ -204,7 +204,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define i64 @julia_g_3086(i64 signext %0, i64 signext %1) #0 {
+define i64 @julia_g_3074(i64 signext %0, i64 signext %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 6 within `g`
@@ -249,7 +249,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `f`
-define double @julia_f_3545(double %0, i64 signext %1) #0 {
+define double @julia_f_3533(double %0, i64 signext %1) #0 {
 top:
 ; ┌ @ promotion.jl:422 within `+`
 ; │┌ @ promotion.jl:393 within `promote`
@@ -290,7 +290,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define double @julia_g_3548(double %0, i64 signext %1) #0 {
+define double @julia_g_3536(double %0, i64 signext %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 5 within `g`
@@ -381,7 +381,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `ff`
-define double @julia_ff_3667(double %0, i64 signext %1) #0 {
+define double @julia_ff_3655(double %0, i64 signext %1) #0 {
 top:
 ; ┌ @ promotion.jl:422 within `+`
 ; │┌ @ promotion.jl:393 within `promote`
@@ -537,7 +537,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define void @julia_g_3806([2 x double]* noalias nocapture noundef nonnull s
+define void @julia_g_3794([2 x double]* noalias nocapture noundef nonnull s
 ret([2 x double]) align 8 dereferenceable(16) %0, [2 x double]* nocapture n
 oundef nonnull readonly align 8 dereferenceable(16) %1, [2 x double]* nocap
 ture noundef nonnull readonly align 8 dereferenceable(16) %2) #0 {
@@ -657,7 +657,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define [2 x float] @julia_g_3818([2 x float]* nocapture noundef nonnull rea
+define [2 x float] @julia_g_3806([2 x float]* nocapture noundef nonnull rea
 donly align 4 dereferenceable(8) %0, [2 x float]* nocapture noundef nonnull
  readonly align 4 dereferenceable(8) %1) #0 {
 top:
@@ -763,7 +763,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `g`
-define void @julia_g_3837([2 x {}*]* noalias nocapture noundef nonnull sret
+define void @julia_g_3825([2 x {}*]* noalias nocapture noundef nonnull sret
 ([2 x {}*]) align 8 dereferenceable(16) %0, [2 x {}*]* nocapture noundef no
 nnull readonly align 8 dereferenceable(16) %1, [2 x {}*]* nocapture noundef
  nonnull readonly align 8 dereferenceable(16) %2) #0 {
@@ -796,25 +796,25 @@
    store {}** %14, {}*** %13, align 8
    %15 = bitcast {}*** %tls_pgcstack to {}***
    store {}** %gcframe2.sub, {}*** %15, align 8
-   call void @"j_+_3839"([2 x {}*]* noalias nocapture noundef nonnull sret(
-[2 x {}*]) %9, [2 x {}*]* nocapture nonnull readonly %1, i64 signext 4)
+   call void @"j_+_3827"([2 x {}*]* noalias nocapture noundef nonnull sret(
+[2 x {}*]) %7, [2 x {}*]* nocapture nonnull readonly %1, i64 signext 4)
 ; â””
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 6 within `g`
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd
 :2 within `f`
-   call void @"j_+_3840"([2 x {}*]* noalias nocapture noundef nonnull sret(
-[2 x {}*]) %5, i64 signext 2, [2 x {}*]* nocapture readonly %9)
+   call void @"j_+_3828"([2 x {}*]* noalias nocapture noundef nonnull sret(
+[2 x {}*]) %5, i64 signext 2, [2 x {}*]* nocapture readonly %7)
 ; â””
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 7 within `g`
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd
 :2 within `f`
-   call void @"j_+_3841"([2 x {}*]* noalias nocapture noundef nonnull sret(
-[2 x {}*]) %7, [2 x {}*]* nocapture readonly %5, [2 x {}*]* nocapture nonnu
+   call void @"j_+_3829"([2 x {}*]* noalias nocapture noundef nonnull sret(
+[2 x {}*]) %9, [2 x {}*]* nocapture readonly %5, [2 x {}*]* nocapture nonnu
 ll readonly %2)
    %16 = bitcast [2 x {}*]* %0 to i8*
-   %17 = bitcast {}** %6 to i8*
+   %17 = bitcast {}** %8 to i8*
    call void @llvm.memcpy.p0i8.p0i8.i64(i8* noundef nonnull align 8 derefer
 enceable(16) %16, i8* noundef nonnull align 16 dereferenceable(16) %17, i64
  16, i1 false)
@@ -844,28 +844,28 @@
 b = MyComplex(2.0,1.0)
 @btime g(a,b)
 
-20.439 ns (1 allocation: 32 bytes)
+20.388 ns (1 allocation: 32 bytes)
 MyComplex(9.0, 2.0)
 
 a = MyParameterizedComplex(1.0,1.0)
 b = MyParameterizedComplex(2.0,1.0)
 @btime g(a,b)
 
-21.304 ns (1 allocation: 32 bytes)
+21.142 ns (1 allocation: 32 bytes)
 MyParameterizedComplex{Float64}(9.0, 2.0)
 
 a = MySlowComplex(1.0,1.0)
 b = MySlowComplex(2.0,1.0)
 @btime g(a,b)
 
-121.043 ns (5 allocations: 96 bytes)
+113.240 ns (5 allocations: 96 bytes)
 MySlowComplex(9.0, 2.0)
 
 a = MySlowComplex2(1.0,1.0)
 b = MySlowComplex2(2.0,1.0)
 @btime g(a,b)
 
-853.314 ns (14 allocations: 288 bytes)
+519.620 ns (14 allocations: 288 bytes)
 MySlowComplex2(9.0, 2.0)
 

Note on Julia

Note that, because of these type specialization, value types, etc. properties, the number types, even ones such as Int, Float64, and Complex, are all themselves implemented in pure Julia! Thus even basic pieces can be implemented in Julia with full performance, given one uses the features correctly.

Note on isbits

Note that a type which is mutable struct will not be isbits. This means that mutable structs will be a pointer to a heap allocated object, unless it's shortlived and the compiler can erase its construction. Also, note that isbits compiles down to bit operations from pure Julia, which means that these types can directly compile to GPU kernels through CUDAnative without modification.

Function Barriers

Since functions automatically specialize on their input types in Julia, we can use this to our advantage in order to make an inner loop fully inferred. For example, take the code from above but with a loop:

 function r(x)
@@ -880,7 +880,7 @@
 end
 @btime r(x)
 
-5.756 μs (300 allocations: 4.69 KiB)
+5.878 μs (300 allocations: 4.69 KiB)
 604.0
 

In here, the loop variables are not inferred and thus this is really slow. However, we can force a function call in the middle to end up with specialization and in the inner loop be stable:

 s(x) = _s(x[1],x[2])
@@ -896,7 +896,7 @@
 end
 @btime s(x)
 
-299.899 ns (1 allocation: 16 bytes)
+296.107 ns (1 allocation: 16 bytes)
 604.0
 

Notice that this algorithm still doesn't infer:

 @code_warntype s(x)
@@ -928,7 +928,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `fff`
-define i64 @julia_fff_3931(i64 signext %0) #0 {
+define i64 @julia_fff_3919(i64 signext %0) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 8 within `fff`
@@ -942,7 +942,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `fff`
-define double @julia_fff_3933(double %0) #0 {
+define double @julia_fff_3921(double %0) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 8 within `fff`
@@ -957,7 +957,7 @@
   C[i,j] = A[i,j] + B[i,j]
 end
 
-769.551 μs (30000 allocations: 468.75 KiB)
+792.921 μs (30000 allocations: 468.75 KiB)
 

This is very slow because the types of A, B, and C cannot be inferred. Why can't they be inferred? Well, at any time in the dynamic REPL scope I can do something like C = "haha now a string!", and thus it cannot specialize on the types currently existing in the REPL (since asynchronous changes could also occur), and therefore it defaults back to doing a type check at every single function which slows it down. Moral of the story, Julia functions are fast but its global scope is too dynamic to be optimized.

Summary

  • Julia is not fast because of its JIT, it's fast because of function specialization and type inference

  • Type stable functions allow inference to fully occur

  • Multiple dispatch works within the function specialization mechanism to create overhead-free compile time controls

  • Julia will specialize the generic functions

  • Making sure values are concretely typed in inner loops is essential for performance

Overheads of Individual Operations

Now let's dig even a little deeper. Everything the processor does has a cost. A great chart to keep in mind is this classic one. A few things should immediately jump out to you:

  • Simple arithmetic, like floating point additions, are super cheap. ~1 clock cycle, or a few nanoseconds.

  • Processors do branch prediction on if statements. If the code goes down the predicted route, the if statement costs ~1-2 clock cycles. If it goes down the wrong route, then it will take ~10-20 clock cycles. This means that predictable branches, like ones with clear patterns or usually the same output, are much cheaper (almost free) than unpredictable branches.

  • Function calls are expensive: 15-60 clock cycles!

  • RAM reads are very expensive, with lower caches less expensive.

Bounds Checking

Let's check the LLVM IR on one of our earlier loops:

 function inner_noalloc!(C,A,B)
   for j in 1:100, i in 1:100
@@ -969,7 +969,7 @@
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `inner_noalloc!`
-define nonnull {}* @"japi1_inner_noalloc!_3942"({}* %function, {}** noalias
+define nonnull {}* @"japi1_inner_noalloc!_3930"({}* %function, {}** noalias
  nocapture noundef readonly %args, i32 %nargs) #0 {
 top:
   %stackargs = alloca {}**, align 8
@@ -1997,7 +1997,7 @@
 ; â””
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 6 within `inner_noalloc!`
-  ret {}* inttoptr (i64 139788390752264 to {}*)
+  ret {}* inttoptr (i64 139763090710536 to {}*)
 
 oob:                                              ; preds = %L5.us.us.us.po
 stloop, %L2.split.us.split, %L2.split.us.split.us.split, %L5.us.us.us.postl
@@ -2098,17 +2098,17 @@
 end
 @btime inner_noalloc!(C,A,B)
 
-2.411 μs (0 allocations: 0 bytes)
+2.386 μs (0 allocations: 0 bytes)
 
 @btime inner_noalloc_ib!(C,A,B)
 
-2.347 μs (0 allocations: 0 bytes)
+2.342 μs (0 allocations: 0 bytes)
 

SIMD

Now let's inspect the LLVM IR again:

 @code_llvm inner_noalloc_ib!(C,A,B)
 
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `inner_noalloc_ib!`
-define nonnull {}* @"japi1_inner_noalloc_ib!_3978"({}* %function, {}** noal
+define nonnull {}* @"japi1_inner_noalloc_ib!_3966"({}* %function, {}** noal
 ias nocapture noundef readonly %args, i32 %nargs) #0 {
 top:
   %stackargs = alloca {}**, align 8
@@ -4013,7 +4013,7 @@
   br i1 %.not.not24, label %L36, label %L2
 
 L36:                                              ; preds = %L25
-  ret {}* inttoptr (i64 139788390752264 to {}*)
+  ret {}* inttoptr (i64 139763090710536 to {}*)
 }
 

If you look closely, you will see things like:

%wide.load24 = load <4 x double>, <4 x double> addrspac(13)* %46, align 8
 ; â””
@@ -4022,7 +4022,7 @@
 @code_llvm fma(2.0,5.0,3.0)
 
 ;  @ floatfuncs.jl:439 within `fma`
-define double @julia_fma_3979(double %0, double %1, double %2) #0 {
+define double @julia_fma_3967(double %0, double %1, double %2) #0 {
 common.ret:
 ; ┌ @ floatfuncs.jl:434 within `fma_llvm`
    %3 = call double @llvm.fma.f64(double %0, double %1, double %2)
@@ -4070,7 +4070,7 @@ 

Inlining

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 4 within `qinline`
-define double @julia_qinline_3982(double %0, double %1) #0 {
+define double @julia_qinline_3970(double %0, double %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 7 within `qinline`
@@ -4107,17 +4107,17 @@ 

Inlining

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 11 within `qnoinline`
-define double @julia_qnoinline_3984(double %0, double %1) #0 {
+define double @julia_qnoinline_3972(double %0, double %1) #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 14 within `qnoinline`
-  %2 = call double @j_fnoinline_3986(double %0, i64 signext 4)
+  %2 = call double @j_fnoinline_3974(double %0, i64 signext 4)
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 15 within `qnoinline`
-  %3 = call double @j_fnoinline_3987(i64 signext 2, double %2)
+  %3 = call double @j_fnoinline_3975(i64 signext 2, double %2)
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 16 within `qnoinline`
-  %4 = call double @j_fnoinline_3988(double %3, double %1)
+  %4 = call double @j_fnoinline_3976(double %3, double %1)
   ret double %4
 }
 
@@ -4136,7 +4136,7 @@

Inlining

-21.605 ns (1 allocation: 16 bytes)
+20.982 ns (1 allocation: 16 bytes)
 9.0
 
@@ -4148,7 +4148,7 @@

Inlining

-25.319 ns (1 allocation: 16 bytes)
+25.017 ns (1 allocation: 16 bytes)
 9.0
 
@@ -4193,7 +4193,7 @@

Note on Benchmarking

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture02/optimizing.jmd:
 2 within `cheat`
-define double @julia_cheat_4016() #0 {
+define double @julia_cheat_4004() #0 {
 top:
   ret double 9.000000e+00
 }
diff --git a/notes/03-Introduction_to_Scientific_Machine_Learning_through_Physics-Informed_Neural_Networks/index.html b/notes/03-Introduction_to_Scientific_Machine_Learning_through_Physics-Informed_Neural_Networks/index.html
index 6abbe5df..3f9c965b 100644
--- a/notes/03-Introduction_to_Scientific_Machine_Learning_through_Physics-Informed_Neural_Networks/index.html
+++ b/notes/03-Introduction_to_Scientific_Machine_Learning_through_Physics-Informed_Neural_Networks/index.html
@@ -19,11 +19,11 @@
 simpleNN(rand(10))
 
 5-element Vector{Float64}:
-  6.191491803887638
- 10.961350935082876
- -0.086794697902561
-  4.329029452777256
- -0.4819728343992912
+ -1.1205287975184102
+ -6.95758884176028
+  4.900880949623628
+  3.7834125354726122
+  4.191471397681439
 

This is our direct definition of a neural network. Notice that we choose to use tanh as our activation function between the layers.

Defining Neural Networks with Flux.jl

One of the main deep learning libraries in Julia is Flux.jl. Flux is an interesting library for scientific machine learning because it is built on top of language-wide automatic differentiation libraries, giving rise to a programming paradigm known as differentiable programming, which means that one can write a program in a manner that it has easily accessible fast derivatives. However, due to being built on a differentiable programming base, the underlying functionality is simply standard Julia code,

To learn how to use the library, consult the documentation. A Google search will bring up the Flux.jl Github repository. From there, the blue link on the README brings you to the package documentation. This is common through Julia so it's a good habit to learn!

In the documentation you will find that the way a neural network is defined is through a Chain of layers. A Dense layer is the kind we defined above, which is given by an input size, an output size, and an activation function. For example, the following recreates the neural network that we had above:

 using Flux
 NN2 = Chain(Dense(10 => 32,tanh),
@@ -32,11 +32,11 @@
 NN2(rand(10))
 
 5-element Vector{Float32}:
- -0.40341762
- -0.32073504
- -0.60663885
-  0.6273459
-  0.3418348
+ -0.41757214
+  0.3268494
+  0.6551256
+  0.3942203
+  0.5987754
 

Notice that Flux.jl as a library is written in pure Julia, which means that every piece of this syntax is just sugar over some Julia code that we can specialize ourselves (this is the advantage of having a language fast enough for the implementation of the library and the use of the library!)

For example, the activation function is just a scalar Julia function. If we wanted to replace it by something like the quadratic function, we can just use an anonymous function to define the scalar function we would like to use:

 NN3 = Chain(Dense(10 => 32,x->x^2),
             Dense(32 => 32,x->max(0,x)),
@@ -44,11 +44,11 @@
 NN3(rand(10))
 
 5-element Vector{Float32}:
- -0.18383282
- -0.030633058
- -0.021082453
- -0.08232622
- -0.22971418
+ -0.02532567
+ -0.14441662
+ -0.051778786
+ -0.38974416
+ -0.10805663
 

The second activation function there is what's known as a relu. A relu can be good to use because it's an exceptionally fast operation and satisfies a form of the universal approximation theorem (UAT). However, a downside is that its derivative is not continuous, which could impact the numerical properties of some algorithms, and thus it's widely used throughout standard machine learning but we'll see reasons why it may be disadvantageous in some cases in scientific machine learning.

Digging into the Construction of a Neural Network Library

Again, as mentioned before, this neural network NN2 is simply a function:

 simpleNN(x) = W[3]*tanh.(W[2]*tanh.(W[1]*x + b[1]) + b[2]) + b[3]
 
@@ -96,26 +96,26 @@
 denselayer_f(rand(32))
 
 32-element Vector{Float32}:
-  0.015360817
- -0.46573928
-  0.84118056
-  0.6889729
- -0.04318807
- -0.486555
-  0.7456628
- -0.1116474
- -0.8562411
-  0.14605747
+ -0.5618108
+ -0.6442508
+  0.4827489
+ -0.2672533
+ -0.87355
+ -0.030600643
+  0.333129
+ -0.16809873
+ -0.03701947
+  0.38146716
   â‹®
-  0.07789154
-  0.35291886
-  0.27881867
- -0.68542886
- -0.3935992
-  0.657173
- -0.18403019
-  0.063687466
- -0.15492688
+ -0.39834422
+  0.8219398
+ -0.55398756
+ -0.031825528
+  0.17183039
+  0.72180593
+ -0.7143836
+  0.49238148
+ -0.7401128
 

So okay, Dense objects are just functions that have weight and bias matrices inside of them. Now what does Chain do?

 @which Chain(1,2,3)
 
Chain(xs...) in Flux at /home/runner/.julia/packages/Flux/Wz6D4/src/layers/basic.jl:39

Again, for our explanations here we will look at the slightly simpler code From and earlier version of the Flux package:

@@ -169,52 +169,52 @@
 loss() = sum(abs2,sum(abs2,NN(rand(10)).-1) for i in 1:100)
 loss()
 
-4269.761f0
+2630.6902f0
 

This loss function takes 100 random points in $[0,1]^{10}$ and then computes the output of the neural network minus 1 on each of the values, and sums up the squared values (abs2). Why the squared values? This means that every computed loss value is positive, and so we know that by decreasing the loss this means that, on average our neural network outputs are closer to 1. What are the weights? Since we're using the Flux callable struct style from above, the weights are those inside of the NN chain object, which we can inspect:

 NN[1].weight # The W matrix of the first layer
 
 32×10 Matrix{Float32}:
-  0.210141   -0.0318616   0.0161381  …   0.320312   -0.331868    0.187002
- -0.102589    0.208266   -0.18678        0.259299   -0.0850212   0.325602
-  0.0954246   0.29503     0.155045      -0.13743    -0.226485   -0.352321
-  0.094165    0.147697   -0.0402125      0.376543   -0.0652325  -0.0910839
- -0.0960281  -0.239555    0.0736242      0.179233    0.0747602  -0.231401
-  0.346408   -0.229251    0.247542   …  -0.227708   -0.342039   -0.3638
- -0.0874939  -0.146548    0.162742      -0.194633   -0.280423   -0.374489
-  0.340297    0.264646    0.097446       0.312401   -0.212878    0.307074
-  0.365767   -0.366916    0.261565      -0.0138854  -0.105345   -0.265025
-  0.228151    0.247341   -0.135668       0.247047    0.140386   -0.19907
-  ⋮                                  ⋱                          
-  0.186276    0.115302   -0.303067      -0.0957572  -0.202029    0.199585
- -0.0958866   0.139531    0.182455      -0.0669233  -0.190238    0.280298
- -0.108114    0.258266    0.052903   …   0.191436   -0.12086    -0.186751
-  0.2797     -0.0260807   0.256827       0.363836   -0.308047    0.104556
- -0.132173   -0.372458    0.122993       0.102172   -0.186959    0.195341
- -0.0277494  -0.15691     0.231842      -0.35223    -0.130247   -0.256816
- -0.309665   -0.313657   -0.345432       0.338976    0.302093    0.373044
- -0.368304    0.298401   -0.0528614  …  -0.196604    0.361721    0.234842
-  0.147451    0.324855   -0.175287       0.0836575   0.331776   -0.0320324
+ -0.0912051   0.171404   -0.225056   …   0.26575    -0.2033       0.19557
+ -0.023353   -0.193015    0.33744        0.135803    0.305452     0.330386
+ -0.21381     0.222574    0.146357       0.302459   -0.204567     0.177094
+ -0.236288   -0.0437021   0.149399      -0.322455    0.0413544    0.376834
+  0.0779307  -0.363982   -0.256043       0.150865    0.310071     0.141492
+  0.310811   -0.0969612  -0.1377     …   0.377674   -0.372038     0.237749
+  0.155738   -0.272288    0.148163      -0.175247    0.0568673   -0.302354
+ -0.338095    0.375844   -0.0522764     -0.0950616   0.241941     0.177569
+ -0.313516    0.0510053  -0.283776      -0.262828    0.127621     0.0381291
+ -0.238924   -0.019392   -0.345777       0.123743   -0.171073     0.286924
+  ⋮                                  ⋱                           
+  0.361331   -0.300791    0.340877       0.051346    0.250142     0.236606
+  0.256587   -0.144472   -0.250453       0.0694984  -0.00493134   0.242588
+  0.321232    0.133965    0.080042   …   0.0232834  -0.357839     0.175813
+  0.29538     0.0922859   0.246678      -0.102874   -0.237856    -0.340453
+  0.344922    0.306977    0.155636      -0.132782    0.127812    -0.0488943
+ -0.0161293  -0.25159    -0.220966       0.190733    0.0313222   -0.100159
+  0.0903141   0.251727    0.341801       0.127054   -0.243489    -0.0498583
+  0.0220798  -0.314075   -0.0345888  …  -0.0257494   0.109001    -0.0628161
+ -0.321938    0.144522   -0.0387606      0.17195     0.200624    -0.272704
 

Now let's grab all of the parameters together:

 p = Flux.params(NN)
 
-Params([Float32[0.21014117 -0.031861562 … -0.33186755 0.18700212; -0.102589
-46 0.20826595 … -0.0850212 0.32560205; … ; -0.36830446 0.29840133 … 0.36172
-083 0.23484248; 0.14745119 0.3248552 … 0.33177575 -0.03203242], Float32[0.0
-, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 0.0, 
-0.0, 0.0, 0.0, 0.0, 0.0], Float32[-0.18695994 -0.04698782 … -0.055100985 -0
-.1341884; 0.0074982448 0.11674296 … -0.22039233 -0.05518888; … ; -0.0007238
-363 0.2559794 … -0.29302862 -0.30272353; 0.087237954 0.087170936 … 0.295242
-6 -0.19100738], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …
-  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.029854774 -0
-.06820957 … 0.09176909 -0.11000199; 0.2896224 0.2912958 … 0.034562368 -0.17
-458537; … ; 0.07649842 0.26569244 … -0.07407499 0.37595725; -0.28656915 0.0
-42809594 … -0.24949594 -0.33653733], Float32[0.0, 0.0, 0.0, 0.0, 0.0]])
+Params([Float32[-0.091205075 0.17140365 … -0.20329987 0.19557032; -0.023352
+979 -0.19301474 … 0.3054521 0.33038554; … ; 0.022079762 -0.31407467 … 0.109
+00141 -0.062816136; -0.3219381 0.14452209 … 0.20062439 -0.27270395], Float3
+2[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  …  0.0, 0.0, 0.0, 0.0, 
+0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[0.019839598 -0.2168316 … -0.2577663 
+0.30027536; -0.19628644 0.07071809 … -0.117501654 0.20883556; … ; 0.0368188
+55 -0.012596854 … -0.24249776 -0.19441248; 0.2561112 -0.19482055 … -0.30460
+06 -0.06886764], Float32[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0  
+…  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], Float32[-0.12173528 -
+0.3812579 … -0.3371982 -0.27681643; 0.33429277 0.32999685 … -0.3093447 -0.2
+4638811; … ; 0.38407874 -0.25517553 … -0.011652403 0.027645448; -0.18082629
+ 0.22756484 … 0.22212335 0.053659596], Float32[0.0, 0.0, 0.0, 0.0, 0.0]])
 

That's a helper function on Chain which recursively gathers all of the defining parameters. Let's now find the optimal values p which cause the neural network to be the constant 1 function:

 Flux.train!(loss, p, Iterators.repeated((), 10000), ADAM(0.1))
 

Now let's check the loss:

 loss()
 
-3.377496f-5
+3.805218f-5
 

This means that NN(x) is now a very good function approximator to f(x) = ones(5)!

So Why Machine Learning? Why Neural Networks?

All we did was find parameters that made NN(x) act like a function f(x). How does that relate to machine learning? Well, in any case where one is acting on data (x,y), the idea is to assume that there exists some underlying mathematical model f(x) = y. If we had perfect knowledge of what f is, then from only the information of x we can then predict what y would be. The inference problem is to then figure out what function f should be. Therefore, machine learning on data is simply this problem of finding an approximator to some unknown function!

So why neural networks? Neural networks satisfy two properties. The first of which is known as the Universal Approximation Theorem (UAT), which in simple non-mathematical language means that, for any ϵ of accuracy, if your neural network is large enough (has enough layers, the weight matrices are large enough), then it can approximate any (nice) function f within that ϵ. Therefore, we can reduce the problem of finding missing functions, the problem of machine learning, to a problem of finding the weights of neural networks, which is a well-defined mathematical optimization problem.

Why neural networks specifically? That's a fairly good question, since there are many other functions with this property. For example, you will have learned from analysis that $a_0 + a_1 x + a_2 x^2 + \ldots$ arbitrary polynomials can be used to approximate any analytic function (this is the Taylor series). Similarly, a Fourier series

\[ f(x) = a_0 + \sum_k b_k \cos(kx) + c_k \sin(kx) \]

can approximate any continuous function f (and discontinuous functions also can have convergence, etc. these are the details of a harmonic analysis course).

That's all for one dimension. How about two dimensional functions? It turns out it's not difficult to prove that tensor products of universal approximators will give higher dimensional universal approximators. So for example, tensoring together two polynomials:

\[ a_0 + a_1 x + a_2 y + a_3 x y + a_4 x^2 y + a_5 x y^2 + a_6 x^2 y^2 + \ldots \]

will give a two-dimensional function approximator. But notice how we have to resolve every combination of terms. This means that if we used n coefficients in each dimension d, the total number of coefficients to build a d-dimensional universal approximator from one-dimensional objects would need $n^d$ coefficients. This exponential growth is known as the curse of dimensionality.

The second property of neural networks that makes them applicable to machine learning is that they overcome the curse of dimensionality. The proofs in this area can be a little difficult to parse, but what they boil down to is proving in many cases that the growth of neural networks to sufficiently approximate a d-dimensional function grows as a polynomial of d, rather than exponential. This means that there's some dimensional cutoff where for $d>cutoff$ it is more efficient to use a neural network. This can be problem-specific, but generally it tends to be the case at least by 8 or 10 dimensions.

Neural networks have a few other properties to consider as well:

  1. The assumptions of the neural network can be encoded into the neural architectures. A neural network where the last layer has an activation function x->x^2 is a neural network where all outputs are positive. This means that if you want to find a positive function, you can make the optimization easier by enforcing this constraint. A lot of other constraints can be enforced, like tanh activation functions can make the neural network be a smooth (all derivatives finite) function, or other activations can cause finite numbers of learnable discontinuities.

  2. Generating higher dimensional forms from one dimensional forms does not have good symmetry. For example, the two-dimensional tensor Fourier basis does not have a good way to represent $sin(xy)$. This property of the approximator is called (non)isotropy and more detail can be found in this wonderful talk about function approximation for multidimensional integration (cubature). Neural networks are naturally not aligned to a basis.

  3. Neural networks are "easy" to compute. There's good software for them, GPU-acceleration, and all other kinds of tooling that make them particularly simple to use.

  4. There are proofs that in many scenarios for neural networks the local minima are the global minima, meaning that local optimization is sufficient for training a neural network. Global optimization (which we will cover later in the course) is much more expensive than local methods like gradient descent, and thus this can be a good property to abuse for faster computation.

From Machine Learning to Scientific Machine Learning: Structure and Science

This understanding of a neural network and their libraries directly bridges to the understanding of scientific machine learning and the computation done in the field. In scientific machine learning, neural networks and machine learning are used as the basis to solve problems in scientific computing. Scientific computing, as a discipline also known as Computational Science, is a field of study which focuses on scientific simulation, using tools such as differential equations to investigate physical, biological, and other phenomena.

What we wish to do in scientific machine learning is use these properties of neural networks to improve the way that we investigate our scientific models.

Aside: Why Differential Equations?

Why do differential equations come up so often in as the model in the scientific context? This is a deep question with quite a simple answer. Essentially, all scientific experiments always have to test how things change. For example, you take a system now, you change it, and your measurement is how the changes you made caused changes in the system. This boils down to gather information about how, for some arbitrary system $y = f(x)$, how $\Delta x$ is related to $\Delta y$. Thus what you learn from scientific experiments, what is codified as scientific laws, is not "the answer", but the answer to how things change. This process of writing down equations by describing how they change precisely gives differential equations.

Solving ODEs with Neural Networks: The Physics-Informed Neural Network

Now let's get to our first true SciML application: solving ordinary differential equations with neural networks. The process of solving a differential equation with a neural network, or using a differential equation as a regularizer in the loss function, is known as a physics-informed neural network, since this allows for physical equations to guide the training of the neural network in circumstances where data might be lacking.

Background: A Method for Solving Ordinary Differential Equations with Neural Networks

This is a result first due to Lagaris et. al from 1998. The idea is to solve differential equations using neural networks by representing the solution by a neural network and training the resulting network to satisfy the conditions required by the differential equation.

Let's say we want to solve a system of ordinary differential equations

\[ u' = f(u,t) \]

with $t \in [0,1]$ and a known initial condition $u(0)=u_0$. To solve this, we approximate the solution by a neural network:

\[ NN(t) \approx u(t) \]

If $NN(t)$ was the true solution, then it would hold that $NN'(t) = f(NN(t),t)$ for all $t$. Thus we turn this condition into our loss function. This motivates the loss function:

\[ L(p) = \sum_i \left(\frac{dNN(t_i)}{dt} - f(NN(t_i),t_i) \right)^2 \]

The choice of $t_i$ could be done in many ways: it can be random, it can be a grid, etc. Anyways, when this loss function is minimized (gradients computed with standard reverse-mode automatic differentiation), then we have that $\frac{dNN(t_i)}{dt} \approx f(NN(t_i),t_i)$ and thus $NN(t)$ approximately solves the differential equation.

Note that we still have to handle the initial condition. One simple way to do this is to add an initial condition term to the cost function. This would look like:

\[ L(p) = (NN(0) - u_0)^2 + \sum_i \left(\frac{dNN(t_i)}{dt} - f(NN(t_i),t_i) \right)^2 \]

While that would work, it can be more efficient to encode the initial condition into the function itself so that it's trivially satisfied for any possible set of parameters. For example, instead of directly using a neural network, we can use:

\[ g(t) = u_0 + tNN(t) \]

as our solution. Notice that $g(t)$ is thus a universal approximator for all continuous functions such that $g(0)=u_0$ (this is a property one should prove!). Since $g(t)$ will always satisfy the initial condition, we can train $g(t)$ to satisfy the derivative function then it will automatically be a solution to the derivative function. In this sense, we can use the loss function:

\[ L(p) = \sum_i \left(\frac{dg(t_i)}{dt} - f(g(t_i),t_i) \right)^2 \]

where $p$ are the parameters that define $g$, which in turn are the parameters which define the neural network $NN$ that define $g$. Thus this reduces down, once again, to simply finding weights which minimize a loss function!

Coding Up the Method

Now let's implement this method with Flux. Let's define a neural network to be the NN(t) above. To make the problem easier, let's look at the ODE:

\[ u' = \cos 2\pi t \]

and approximate it with the neural network from a scalar to a scalar:

 using Flux
 NNODE = Chain(x -> [x], # Take in a scalar and transform it into an array
@@ -223,7 +223,7 @@
            first) # Take first value, i.e. return a scalar
 NNODE(1.0)
 
-0.10085204f0
+-0.23149511f0
 

Instead of directly approximating the neural network, we will use the transformed equation that is forced to satisfy the boundary conditions. Using u0=1.0, we have the function:

 g(t) = t*NNODE(t) + 1f0
 
@@ -247,23 +247,23 @@
 display(loss())
 Flux.train!(loss, Flux.params(NNODE), data, opt; cb=cb)
 
-0.5089291687042273
-0.4660090972283474
-0.37798210626977025
-0.18461971004026578
-0.04371968207719241
-0.019536930111555596
-0.014619263249474197
-0.012650337532635803
-0.011729013939159596
-0.011283318492406378
-0.010946978192004615
+0.5708075887278983
+0.4843225444558577
+0.431599836626712
+0.26459017110776495
+0.05310078897817342
+0.011030989241838655
+0.007069401923025258
+0.00634987145212996
+0.00601922323173909
+0.0057577987424845736
+0.005500448268481607
 

How well did this do? Well if we take the integral of both sides of our differential equation, we see it's fairly trivial:

\[ \int g' = g = \int \cos 2\pi t = C + \frac{\sin 2\pi t}{2\pi} \]

where we defined $C = 1$. Let's take a bunch of (input,output) pairs from the neural network and plot it against the analytical solution to the differential equation:

 using Plots
 t = 0:0.001:1.0
 plot(t,g.(t),label="NN")
 plot!(t,1.0 .+ sin.(2Ï€.*t)/2Ï€, label = "True Solution")
-

We see that it matches very well, and we can keep improving this fit by increasing the size of the neural network, using more training points, and training for more iterations.

Example: Harmonic Oscillator Informed Training

Using this idea, differential equations encoding physical laws can be utilized inside of loss functions for terms which we have some basis to believe should approximately follow some physical system. Let's investigate this last step by looking at how to inform the training of a neural network using the harmonic oscillator.

Let's assume that we are taking measurements of (position,force) in some real one-dimensional spring pushing and pulling against a wall.

But instead of the simple spring, let's assume we had a more complex spring, for example, let's say $F(x) = -kx + 0.1sin(x)$ where this extra term is due to some deformities in the metal (assume mass=1). Then by Newton's law of motion we have a second order ordinary differential equation:

\[ x'' = -kx + 0.1 \sin(x) \]

We can use the DifferentialEquations.jl package to solve this differential equation and see what this system looks like:

+

We see that it matches very well, and we can keep improving this fit by increasing the size of the neural network, using more training points, and training for more iterations.

Example: Harmonic Oscillator Informed Training

Using this idea, differential equations encoding physical laws can be utilized inside of loss functions for terms which we have some basis to believe should approximately follow some physical system. Let's investigate this last step by looking at how to inform the training of a neural network using the harmonic oscillator.

Let's assume that we are taking measurements of (position,force) in some real one-dimensional spring pushing and pulling against a wall.

But instead of the simple spring, let's assume we had a more complex spring, for example, let's say $F(x) = -kx + 0.1sin(x)$ where this extra term is due to some deformities in the metal (assume mass=1). Then by Newton's law of motion we have a second order ordinary differential equation:

\[ x'' = -kx + 0.1 \sin(x) \]

We can use the DifferentialEquations.jl package to solve this differential equation and see what this system looks like:

 using DifferentialEquations
 k = 1.0
 force(dx,x,k,t) = -k*x + 0.1sin(x)
@@ -300,7 +300,7 @@
 loss() = sum(abs2,NNForce(position_data[i]) - force_data[i] for i in 1:length(position_data))
 loss()
 
-0.0021075816164434196
+0.001639101475417397
 

Our random parameters do not do so well, so let's train!

 opt = Flux.Descent(0.01)
 data = Iterators.repeated((), 5000)
@@ -314,24 +314,24 @@
 display(loss())
 Flux.train!(loss, Flux.params(NNForce), data, opt; cb=cb)
 
-0.0021075816164434196
-0.0016626665734058707
-0.0014129151050661442
-0.0012004879578227366
-0.001019683666213778
-0.00086574214000748
-0.0007346664655364099
-0.0006230813332454321
-0.0005281206353467606
-0.0004473482765489638
-0.00037868342158744984
+0.001639101475417397
+0.001281078565346881
+0.001078228101263855
+0.000907144491383387
+0.000762834587654969
+0.0006411230663627027
+0.0005385050286698408
+0.00045202585782455135
+0.0003791885175632164
+0.00031788120953395697
+0.0002663162119004075
 

The neural network almost exactly matched the dataset, but how well did it actually learn the real force function? Let's plot it to see:

 learned_force_plot = NNForce.(positions_plot)
 
 plot(plot_t,force_plot,xlabel="t",label="True Force")
 plot!(plot_t,learned_force_plot,label="Predicted Force")
 scatter!(t,force_data,label="Force Measurements")
-

Ouch. The problem is that a neural network can approximate any function, so it approximated a function that fits the data, but not the correct function. We somehow need to have more data... but where can we get more data?

Well, even a first year undergrad in physics will know Hooke's law, which is that the idealized spring should satisfy $F(x) = -kx$. This is a decent assumption for the evolution of the system:

+

Ouch. The problem is that a neural network can approximate any function, so it approximated a function that fits the data, but not the correct function. We somehow need to have more data... but where can we get more data?

Well, even a first year undergrad in physics will know Hooke's law, which is that the idealized spring should satisfy $F(x) = -kx$. This is a decent assumption for the evolution of the system:

 force2(dx,x,k,t) = -k*x
 prob_simplified = SecondOrderODEProblem(force2,1.0,0.0,(0.0,10.0),k)
 sol_simplified = solve(prob_simplified)
@@ -342,7 +342,7 @@
 loss_ode() = sum(abs2,NNForce(x) - (-k*x) for x in random_positions)
 loss_ode()
 
-10.993295820275433
+8.460564225345507
 

If this term is zero, then $F(x) = -kx$, which is approximately true. So now let's put these together:

 λ = 0.1
 composed_loss() = loss() + λ*loss_ode()
@@ -367,15 +367,15 @@
 plot!(plot_t,learned_force_plot,label="Predicted Force")
 scatter!(t,force_data,label="Force Measurements")
 
-1.0997082654491308
-0.0005850661060708156
-0.0005522938565740613
-0.0005232864079531569
-0.0004973263251085195
-0.00047389858231739466
-0.000452611856433718
-0.0004331665468480585
-0.00041531905615030537
-0.00039886843159506777
-0.000383654997635387
-

And there we go: we have used knowledge of physics to help inform our neural network training process!

Conclusion

In this lecture we motivated machine learning not as a process of predicting from data but as a process for learning arbitrary nonlinear functions. Neural networks were just one choice of possible function. We then demonstrated how differential equations could be solved using this function approximation technique and then put together these two domains, solving differential equations and approximating data, into a single process to allow for physical knowledge to be embedded into the training process of a neural network, thus arriving at a physics-informed neural network. This is just one method in scientific machine learning which we will be exploring in more detail, demonstrating how we can utilize scientific knowledge to improve fits and allow for data-efficient machine learning.

\ No newline at end of file +0.8463227387464513 +0.000210408027752647 +0.00020519506792570903 +0.00020026862808211442 +0.00019559928791649267 +0.00019116211020453466 +0.00018693271539265902 +0.00018289622008194647 +0.00017903277909909004 +0.00017533236251209575 +0.0001717819891828354 +

And there we go: we have used knowledge of physics to help inform our neural network training process!

Conclusion

In this lecture we motivated machine learning not as a process of predicting from data but as a process for learning arbitrary nonlinear functions. Neural networks were just one choice of possible function. We then demonstrated how differential equations could be solved using this function approximation technique and then put together these two domains, solving differential equations and approximating data, into a single process to allow for physical knowledge to be embedded into the training process of a neural network, thus arriving at a physics-informed neural network. This is just one method in scientific machine learning which we will be exploring in more detail, demonstrating how we can utilize scientific knowledge to improve fits and allow for data-efficient machine learning.

\ No newline at end of file diff --git a/notes/04-How_Loops_Work-An_Introduction_to_Discrete_Dynamics/index.html b/notes/04-How_Loops_Work-An_Introduction_to_Discrete_Dynamics/index.html index 69e459e4..66a012d1 100644 --- a/notes/04-How_Loops_Work-An_Introduction_to_Discrete_Dynamics/index.html +++ b/notes/04-How_Loops_Work-An_Introduction_to_Discrete_Dynamics/index.html @@ -113,7 +113,7 @@ end @time solve_system_save(lorenz,[1.0,0.0,0.0],p,1000)
-0.000050 seconds (1.00 k allocations: 86.062 KiB)
+0.000054 seconds (1.00 k allocations: 86.062 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -138,7 +138,7 @@
 
 @time solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-0.012133 seconds (4.50 k allocations: 336.016 KiB, 99.40% compilation tim
+0.011324 seconds (4.50 k allocations: 336.016 KiB, 99.43% compilation tim
 e)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
@@ -164,7 +164,7 @@
 

The first time Julia compiles the function, and the second is a straight call.

 @time solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-0.000057 seconds (1.01 k allocations: 99.984 KiB)
+0.000056 seconds (1.01 k allocations: 99.984 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -190,7 +190,7 @@
 using BenchmarkTools
 @btime solve_system_save(lorenz,[1.0,0.0,0.0],p,1000)
 
-38.372 μs (1001 allocations: 86.06 KiB)
+27.531 μs (1001 allocations: 86.06 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -215,7 +215,7 @@
 
 @btime solve_system_save_push(lorenz,[1.0,0.0,0.0],p,1000)
 
-44.223 μs (1006 allocations: 99.98 KiB)
+33.333 μs (1006 allocations: 99.98 KiB)
 1000-element Vector{Vector{Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -248,7 +248,7 @@
 end
 @btime solve_system_save_matrix(lorenz,[1.0,0.0,0.0],p,1000)
 
-69.190 μs (2001 allocations: 179.66 KiB)
+66.845 μs (2001 allocations: 179.66 KiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -265,7 +265,7 @@
 end
 @btime solve_system_save_matrix_view(lorenz,[1.0,0.0,0.0],p,1000)
 
-34.535 μs (1002 allocations: 101.61 KiB)
+34.956 μs (1002 allocations: 101.61 KiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -282,7 +282,7 @@
 end
 @btime solve_system_save_matrix_resize(lorenz,[1.0,0.0,0.0],p,1000)
 
-1.198 ms (2318 allocations: 11.65 MiB)
+1.043 ms (2318 allocations: 11.65 MiB)
 3×1000 Matrix{Float64}:
  1.0  0.8   0.752    0.80096   0.920338   …   1.98201    1.67886    1.4744
  0.0  0.56  0.9968   1.39785   1.81805        0.466287   0.656559   0.85300
@@ -388,7 +388,7 @@
 

which would compute f and then take the values of du and update u with them, but that's 3 extra operations than required, whereas u,du = du,u will change u to be a pointer to the updated memory and now du is an "empty" cache array that we can refill (this decreases the computational cost by ~33%). Let's see what the cost is with this newest version:

 @btime solve_system(lorenz,[1.0,0.0,0.0],p,1000)
 
-37.210 μs (1000 allocations: 78.12 KiB)
+25.919 μs (1000 allocations: 78.12 KiB)
 3-element Vector{Float64}:
   1.4744010677851374
   0.8530017039412324
@@ -396,7 +396,7 @@
 
 @btime solve_system_mutate(lorenz,[1.0,0.0,0.0],p,1000)
 
-7.301 μs (3 allocations: 240 bytes)
+7.291 μs (3 allocations: 240 bytes)
 3-element Vector{Float64}:
   1.4744010677851374
   0.8530017039412324
@@ -445,7 +445,7 @@
 
 @btime solve_system_save(lorenz,@SVector[1.0,0.0,0.0],p,1000)
 
-8.766 μs (2 allocations: 23.48 KiB)
+8.850 μs (2 allocations: 23.48 KiB)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -511,7 +511,7 @@
 
 @btime solve_system_save(lorenz,@SVector[1.0,0.0,0.0],p,1000)
 
-6.723 μs (2 allocations: 23.48 KiB)
+6.721 μs (2 allocations: 23.48 KiB)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
@@ -536,7 +536,7 @@
 

And we can get down to non-allocating for the loop:

 @btime solve_system(lorenz,@SVector([1.0,0.0,0.0]),p,1000)
 
-6.502 μs (1 allocation: 32 bytes)
+6.496 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
   1.4744010677851374
   0.8530017039412324
@@ -552,7 +552,7 @@
 u = Vector{typeof(@SVector([1.0,0.0,0.0]))}(undef,1000)
 @btime solve_system_save!(u,lorenz,@SVector([1.0,0.0,0.0]),p,1000)
 
-6.498 μs (0 allocations: 0 bytes)
+6.500 μs (0 allocations: 0 bytes)
 1000-element Vector{SVector{3, Float64}}:
  [1.0, 0.0, 0.0]
  [0.8, 0.56, 0.0]
diff --git a/notes/05-The_Basics_of_Single_Node_Parallel_Computing/index.html b/notes/05-The_Basics_of_Single_Node_Parallel_Computing/index.html
index 18131d95..159fb83d 100644
--- a/notes/05-The_Basics_of_Single_Node_Parallel_Computing/index.html
+++ b/notes/05-The_Basics_of_Single_Node_Parallel_Computing/index.html
@@ -127,7 +127,7 @@
 u = [Vector{Float64}(undef,3) for i in 1:1000]
 @btime solve_system_save_iip!(u,lorenz_mt!,[1.0,0.0,0.0],p,1000);
 
-1.202 ms (5995 allocations: 608.84 KiB)
+1.203 ms (5995 allocations: 608.84 KiB)
 

Parallelism doesn't always make things faster. There are two costs associated with this code. For one, we had to go to the slower heap+mutation version, so its implementation starting point is slower. But secondly, and more importantly, the cost of spinning a new thread is non-negligible. In fact, here we can see that it even needs to make a small allocation for the new context. The total cost is on the order of It's on the order of 50ns: not huge, but something to take note of. So what we've done is taken almost free calculations and made them ~50ns by making each in a different thread, instead of just having it be one thread with one call stack.

The moral of the story is that you need to make sure that there's enough work per thread in order to effectively accelerate a program with parallelism.

Data-Parallel Problems

So not every setup is amenable to parallelism. Dynamical systems are notorious for being quite difficult to parallelize because the dependency of the future time step on the previous time step is clear, meaning that one cannot easily "parallelize through time" (though it is possible, which we will study later).

However, one common way that these systems are generally parallelized is in their inputs. The following questions allow for independent simulations:

  • What steady state does an input u0 go to for some list/region of initial conditions?

  • How does the solution very when I use a different p?

The problem has a few descriptions. For one, it's called an embarrassingly parallel problem since the problem can remain largely intact to solve the parallelism problem. To solve this, we can use the exact same solve_system_save_iip!, and just change how we are calling it. Secondly, this is called a data parallel problem, since it parallelized by splitting up the input data (here, the possible u0 or ps) and acting on them independently.

Multithreaded Parameter Searches

Now let's multithread our parameter search. Let's say we wanted to compute the mean of the values in the trajectory. For a single input pair, we can compute that like:

 using Statistics
 function compute_trajectory_mean(u0,p)
@@ -137,7 +137,7 @@
 end
 @btime compute_trajectory_mean(@SVector([1.0,0.0,0.0]),p)
 
-7.647 μs (3 allocations: 23.52 KiB)
+7.612 μs (3 allocations: 23.52 KiB)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -151,7 +151,7 @@
 end
 @btime compute_trajectory_mean2(@SVector([1.0,0.0,0.0]),p)
 
-7.464 μs (3 allocations: 112 bytes)
+7.469 μs (3 allocations: 112 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -165,7 +165,7 @@
 end
 @btime compute_trajectory_mean3(@SVector([1.0,0.0,0.0]),p)
 
-7.424 μs (1 allocation: 32 bytes)
+7.421 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -178,7 +178,7 @@
 compute_trajectory_mean4(u0,p) = _compute_trajectory_mean4(_u_cache,u0,p)
 @btime compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p)
 
-7.429 μs (1 allocation: 32 bytes)
+7.426 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -187,50 +187,50 @@
 ps = [(0.02,10.0,28.0,8/3) .* (1.0,rand(3)...) for i in 1:1000]
 
 1000-element Vector{NTuple{4, Float64}}:
- (0.02, 9.106049390932355, 3.303812946184277, 2.0289376601917164)
- (0.02, 4.735606575537758, 24.169178529667747, 1.3480009969264561)
- (0.02, 1.8782542650718392, 22.557130006439053, 2.0243816234869705)
- (0.02, 5.302261087000523, 27.07006533567516, 2.655762043438801)
- (0.02, 5.670338250773302, 4.441306677748524, 0.9560932489779355)
- (0.02, 1.4007298019106085, 8.924613440852292, 1.7845149466502728)
- (0.02, 4.210072135010345, 12.885641724584875, 1.4106354338111666)
- (0.02, 6.178913314158388, 14.077198942044106, 2.6422469838602893)
- (0.02, 9.022764003735679, 5.959401451034244, 0.2915953150851287)
- (0.02, 7.637994682331543, 2.280140073955992, 1.3174704247968336)
+ (0.02, 2.0494785010995353, 12.87474091643628, 2.221379168745697)
+ (0.02, 3.0141933992052494, 5.011093405297812, 0.9795778033401599)
+ (0.02, 6.6087512736768526, 22.554785148961784, 0.3707891781853476)
+ (0.02, 0.5160432877281274, 2.1442560677379037, 1.7411393467364724)
+ (0.02, 8.558522987629267, 18.545203958656234, 1.9346962153448815)
+ (0.02, 9.54542624443799, 14.894432021569349, 2.2001967050663778)
+ (0.02, 7.164909712680697, 14.058087153752206, 1.9721359183413034)
+ (0.02, 4.503455496371159, 15.638037179709096, 0.5478848102231814)
+ (0.02, 5.0067292534598415, 23.498092029722358, 1.8321033765018702)
+ (0.02, 2.043018481497864, 24.8343511074286, 0.40227226208229894)
  â‹®
- (0.02, 1.4295983611840468, 22.723025540961917, 0.5603759621647108)
- (0.02, 3.5691263501247104, 11.614453902298626, 0.9865390285286525)
- (0.02, 6.347641844244725, 11.287551194973615, 2.170255707975067)
- (0.02, 4.3184493713301455, 24.303906061742534, 0.09612994921103102)
- (0.02, 6.080688519020651, 7.21028271370196, 0.8896348073525427)
- (0.02, 6.3902301771886725, 13.890419308445491, 0.5118246742927562)
- (0.02, 3.0250459886141687, 16.614712639906436, 2.5380729556522454)
- (0.02, 1.4197562136945086, 12.915115904241887, 2.1974841648464785)
- (0.02, 2.760108862922477, 8.843016809441602, 0.6191363970184215)
+ (0.02, 9.459925043278687, 14.913762836267876, 1.946411348397799)
+ (0.02, 3.7058223528064884, 9.956749354099607, 2.5430008163316744)
+ (0.02, 5.6913112833876225, 12.407156482636111, 1.0651014726178387)
+ (0.02, 0.09131385334597009, 7.6889382933701, 2.0354078697915967)
+ (0.02, 1.1885766367375572, 19.114097281325094, 1.381133123272827)
+ (0.02, 3.9804371550787767, 23.853405760253743, 1.185532990309715)
+ (0.02, 8.84078503958126, 5.847345434942631, 1.0196090739651438)
+ (0.02, 5.582363837550646, 11.995288548626041, 0.041581327093415034)
+ (0.02, 0.643229668934614, 23.346238053688726, 0.22539644325045938)
 

And let's get the mean of the trajectory for each of the parameters.

 serial_out = map(p -> compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p),ps)
 
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Now let's do this with multithreading:

 function tmap(f,ps)
   out = Vector{typeof(@SVector([1.0,0.0,0.0]))}(undef,1000)
@@ -243,26 +243,26 @@
 threaded_out = tmap(p -> compute_trajectory_mean4(@SVector([1.0,0.0,0.0]),p),ps)
 
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Let's check the output:

 serial_out - threaded_out
 
@@ -296,7 +296,7 @@
 end
 @btime compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p)
 
-7.429 μs (1 allocation: 32 bytes)
+7.424 μs (1 allocation: 32 bytes)
 3-element SVector{3, Float64} with indices SOneTo(3):
  -0.31149962346484683
  -0.3097490174897651
@@ -330,53 +330,53 @@
 
 @btime serial_out = map(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.420 ms (3 allocations: 23.50 KiB)
+7.419 ms (3 allocations: 23.50 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 
 @btime threaded_out = tmap(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.420 ms (8 allocations: 24.06 KiB)
+7.421 ms (8 allocations: 24.06 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Hierarchical Task-Based Multithreading and Dynamic Scheduling

The major change in Julia v1.3 is that Julia's Tasks, which are traditionally its green threads interface, are now the basis of its multithreading infrastructure. This means that all independent threads are parallelized, and a new interface for multithreading will exist that works by spawning threads.

This implementation follows Go's goroutines and the classic multithreading interface of Cilk. There is a Julia-level scheduler that handles the multithreading to put different tasks on different vCPU threads. A benefit from this is hierarchical multithreading. Since Julia's tasks can spawn tasks, what can happen is a task can create tasks which create tasks which etc. In Julia (/Go/Cilk), this is then seen as a single pool of tasks which it can schedule, and thus it will still make sure only N are running at a time (as opposed to the naive implementation where the total number of running threads is equal then multiplied). This is essential for numerical performance because running multiple compute threads on a single CPU thread requires constant context switching between the threads, which will slow down the computations.

To directly use the task-based interface, simply use Threads.@spawn to spawn new tasks. For example:

 function tmap2(f,ps)
   tasks = [Threads.@spawn f(ps[i]) for i in 1:1000]
@@ -385,51 +385,51 @@
 threaded_out = tmap2(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

However, if we check the timing we see:

 @btime tmap2(p -> compute_trajectory_mean5(@SVector([1.0,0.0,0.0]),p),ps)
 
-7.761 ms (6005 allocations: 562.70 KiB)
+7.741 ms (6005 allocations: 562.70 KiB)
 1000-element Vector{SVector{3, Float64}}:
- [2.0820907627308065, 2.088471204219544, 2.155235431072218]
- [1.5595505244183643, 1.5099684640627067, 22.095262274445847]
- [6.126638249655104, 6.273549674375277, 19.649196180507175]
- [0.2695734398328459, 0.36474536430110527, 22.702246606047403]
- [1.751827841218394, 1.7589379670019873, 3.2596952947386217]
- [3.6717748780283044, 3.770314179696095, 7.617740491703323]
- [-3.2187205975639945, -3.2130512092288646, 11.079628189747254]
- [-1.3436994956146562, -1.3487865435138944, 10.286513589875154]
- [-0.40026948095723325, -0.41137422951603414, 4.484011162661078]
- [1.2403493173367404, 1.2423043385425232, 1.163725766067324]
+ [4.999057172451228, 5.0999726149344795, 11.40861028611877]
+ [1.9095218957936158, 1.9255606407677823, 3.7801161867320436]
+ [0.018098089685998762, 0.006162305680758088, 22.30859678284725]
+ [1.3360619709669204, 1.3759318173846775, 1.0332497381762005]
+ [-0.39175341662834273, -0.3765521475273949, 16.121629585090233]
+ [-1.525844575711785, -1.5169080412234797, 12.195050923062764]
+ [-2.377134690797774, -2.384556348363591, 11.71714223980902]
+ [0.6927113721729414, 0.6709618126493168, 14.094390880667179]
+ [-0.7904811808266985, -0.8894107857525068, 20.100852612157897]
+ [0.33547871420519304, 0.4166653156824929, 23.17401896843689]
  â‹®
- [0.10984575880851026, 0.18303614258211953, 18.881095404828727]
- [-2.767005081412208, -2.825454726504213, 10.443233539110137]
- [-1.4824600192283712, -1.473288968668467, 8.213790730589803]
- [0.3373356921990089, 0.32575332045401934, 28.199920884030337]
- [-1.7375406489852403, -1.762221249211257, 5.848737373322062]
- [0.14296262183546848, 0.08144207197628298, 11.996268909515498]
- [-4.8803407614198, -5.012594562811099, 13.34548086739526]
- [4.994649988098529, 5.139638001158158, 11.50546102974038]
- [-1.7255017279562215, -1.7763243048645443, 7.6751484257085965]
+ [0.22875070399599703, 0.22677752744244983, 12.353008641915155]
+ [4.600804025864118, 4.651652635956364, 8.529173090072675]
+ [0.22214974573101073, 0.222892247834588, 10.251319400659881]
+ [2.8649842402742403, 4.284471959008289, 5.7544606666198845]
+ [4.867928591376662, 5.0364221417730635, 17.3381202260852]
+ [0.6735461553758312, 0.7447967652917319, 21.503598600914458]
+ [2.0801143623157454, 2.086828195134251, 4.571613185440295]
+ [0.1667586921903792, 0.15780191304537522, 15.403259875326595]
+ [-1.7202717446442446, -1.9734556539954884, 21.377374239524237]
 

Threads.@threads is built on the same multithreading infrastructure, so why is this so much slower? The reason is because Threads.@threads employs static scheduling while Threads.@spawn is using dynamic scheduling. Dynamic scheduling is the model of allowing the runtime to determine the ordering and scheduling of processes, i.e. what tasks will run run where and when. Julia's task-based multithreading system has a thread scheduler which will automatically do this for you in the background, but because this is done at runtime it will have overhead. Static scheduling is the model of pre-determining where and when tasks will run, instead of allowing this to be determined at runtime. Threads.@threads is "quasi-static" in the sense that it cuts the loop so that it spawns only as many tasks as there are threads, essentially assigning one thread for even chunks of the input data.

Does this lack of runtime overhead mean that static scheduling is "better"? No, it simply has trade-offs. Static scheduling assumes that the runtime of each block is the same. For this specific case where there are fixed number of loop iterations for the dynamical systems, we know that every compute_trajectory_mean5 costs exactly the same, and thus this will be more efficient. However, There are many cases where this might not be efficient. For example:

 function sleepmap_static()
   out = Vector{Int}(undef,24)
@@ -448,7 +448,7 @@
 @btime sleepmap_static()
 @btime sleepmap_spawn()
 
-30.057 s (104 allocations: 3.75 KiB)
+30.053 s (104 allocations: 3.75 KiB)
   2.401 s (220 allocations: 14.77 KiB)
 24-element Vector{Int64}:
   1
@@ -489,24 +489,24 @@
 A*B
 
 10000×10000 Matrix{Float64}:
- 2542.68  2521.97  2510.56  2497.9   …  2509.28  2525.0   2523.88  2520.4
- 2509.41  2523.24  2499.23  2466.59     2498.17  2490.19  2504.73  2494.0
- 2527.03  2511.33  2512.35  2475.63     2512.02  2515.73  2511.64  2515.72
- 2530.45  2522.96  2505.87  2477.47     2514.41  2511.84  2483.23  2504.55
- 2528.08  2517.44  2492.39  2481.61     2514.61  2518.98  2501.12  2506.62
- 2513.9   2530.09  2495.53  2463.74  …  2513.33  2488.51  2489.3   2499.18
- 2483.17  2492.63  2484.71  2445.09     2490.3   2491.62  2488.92  2477.49
- 2540.57  2535.27  2522.95  2491.98     2527.58  2505.16  2526.23  2522.58
- 2541.76  2536.15  2523.99  2492.26     2520.01  2514.86  2519.61  2519.08
- 2510.24  2515.49  2519.26  2479.86     2499.94  2503.5   2503.25  2500.0
+ 2525.87  2488.32  2499.41  2509.97  …  2511.13  2489.44  2531.07  2502.22
+ 2519.05  2496.88  2482.01  2506.51     2527.63  2493.75  2516.93  2503.97
+ 2534.89  2503.33  2495.34  2520.59     2529.65  2508.02  2536.28  2522.71
+ 2532.22  2520.62  2515.99  2518.95     2535.94  2524.97  2546.52  2512.56
+ 2514.36  2479.37  2504.63  2489.56     2507.64  2494.65  2523.54  2493.13
+ 2529.33  2500.05  2501.9   2505.24  …  2531.42  2486.38  2515.62  2512.86
+ 2540.09  2497.2   2504.75  2516.27     2521.17  2512.34  2529.63  2512.79
+ 2509.74  2477.49  2488.59  2479.55     2506.92  2485.0   2531.66  2480.12
+ 2481.75  2476.91  2490.11  2465.56     2465.47  2467.87  2509.78  2478.45
+ 2544.86  2496.0   2514.15  2517.92     2519.5   2511.37  2552.28  2527.42
     ⋮                                ⋱                             
- 2525.23  2512.21  2498.54  2488.94     2513.71  2501.97  2511.88  2520.2
- 2511.07  2491.4   2491.45  2465.4      2511.1   2497.52  2478.17  2490.51
- 2502.33  2508.4   2498.52  2465.6      2503.36  2502.37  2492.31  2501.05
- 2521.12  2509.38  2498.31  2474.29     2515.97  2501.83  2512.41  2488.79
- 2490.9   2496.69  2483.61  2441.27  …  2490.72  2471.2   2475.08  2476.2
- 2535.03  2541.2   2529.11  2497.66     2535.53  2517.47  2531.13  2519.93
- 2511.1   2510.73  2493.1   2492.95     2510.81  2499.15  2542.01  2518.67
- 2518.64  2526.74  2517.93  2488.57     2531.73  2504.55  2515.35  2513.5
- 2523.56  2519.73  2500.24  2485.91     2513.05  2535.31  2523.33  2518.76
+ 2527.82  2496.37  2506.14  2490.21     2519.83  2528.13  2550.02  2506.49
+ 2512.87  2490.24  2480.03  2490.32     2526.82  2474.85  2525.26  2493.48
+ 2494.52  2487.08  2483.83  2499.03     2504.63  2476.85  2533.39  2491.2
+ 2531.16  2499.71  2516.22  2520.35     2536.82  2507.74  2517.3   2530.53
+ 2521.62  2481.61  2484.72  2506.89  …  2514.74  2495.46  2525.36  2499.07
+ 2516.2   2491.17  2493.77  2509.39     2502.63  2491.1   2508.44  2497.63
+ 2499.9   2481.76  2506.27  2492.37     2494.85  2491.09  2518.55  2480.2
+ 2527.68  2503.12  2528.21  2511.25     2532.84  2503.63  2558.09  2533.89
+ 2540.76  2513.19  2517.55  2496.81     2522.99  2506.76  2533.4   2518.68
 

If you are using a computer that has N cores, then this will use N cores. Try it and look at your resource usage!

Array-Based Parallelism

The simplest form of parallelism is array-based parallelism. The idea is that you use some construction of an array whose operations are already designed to be parallel under the hood. In Julia, some examples of this are:

  • DistributedArrays (Distributed Computing)

  • Elemental

  • MPIArrays

  • CuArrays (GPUs)

This is not a Julia specific idea either.

BLAS and Standard Libraries

The basic linear algebra calls are all handled by a set of libraries which follow the same interface known as BLAS (Basic Linear Algebra Subroutines). It's divided into 3 portions:

  • BLAS1: Element-wise operations (O(n))

  • BLAS2: Matrix-vector operations (O(n^2))

  • BLAS3: Matrix-matrix operations (O(n^3))

BLAS implementations are highly optimized, like OpenBLAS and Intel MKL, so every numerical language and library essentially uses similar underlying BLAS implementations. Extensions to these, known as LAPACK, include operations like factorizations, and are included in these standard libraries. These are all multithreaded. The reason why this is a location to target is because the operation count is high enough that parallelism can be made efficient even when only targeting this level: a matrix multiplication can take on the order of seconds, minutes, hours, or even days, and these are all highly parallel operations. This means you can get away with a bunch just by parallelizing at this level, which happens to be a bottleneck for a lot scientific computing codes.

This is also commonly the level at which GPU computing occurs in machine learning libraries for reasons which we will explain later.

MPI

Well, this is a big topic and we'll address this one later!

Conclusion

The easiest forms of parallelism are:

  • Embarrassingly parallel

  • Array-level parallelism (built into linear algebra)

Exploit these when possible.

\ No newline at end of file diff --git a/notes/06-The_Different_Flavors_of_Parallelism/index.html b/notes/06-The_Different_Flavors_of_Parallelism/index.html index cd16297d..1ecaba87 100644 --- a/notes/06-The_Different_Flavors_of_Parallelism/index.html +++ b/notes/06-The_Different_Flavors_of_Parallelism/index.html @@ -9,26 +9,26 @@ arr = [MyComplex(rand(),rand()) for i in 1:100]
 100-element Vector{MyComplex}:
- MyComplex(0.14309417245278067, 0.9215224222944839)
- MyComplex(0.6563768312045752, 0.8044767963545393)
- MyComplex(0.5728049188895872, 0.33765746952116094)
- MyComplex(0.8240073299674354, 0.9470426273216329)
- MyComplex(0.7606061347124898, 0.9327907399185521)
- MyComplex(0.755341833759231, 0.5434958255089687)
- MyComplex(0.9731438085219921, 0.31820007095612113)
- MyComplex(0.6770182257794624, 0.3681202537043393)
- MyComplex(0.08480739105077983, 0.65477879435604)
- MyComplex(0.879044108699869, 0.5694763191704512)
+ MyComplex(0.5948216400563927, 0.7202742856092115)
+ MyComplex(0.8858224485878035, 0.04464426300983426)
+ MyComplex(0.4536724251568013, 0.48301713342468633)
+ MyComplex(0.17632669227127473, 0.966975004988031)
+ MyComplex(0.7423602389573798, 0.7381611563859961)
+ MyComplex(0.5049342978821135, 0.2713721944949681)
+ MyComplex(0.8956475760986714, 0.006535319763830483)
+ MyComplex(0.5275017128612832, 0.6089884504844824)
+ MyComplex(0.22199738442114458, 0.3133842057610531)
+ MyComplex(0.7056746294589479, 0.17085581295301622)
  â‹®
- MyComplex(0.6184831153175321, 0.8420717430705801)
- MyComplex(0.11097401639991189, 0.4733738650181497)
- MyComplex(0.9598982066766498, 0.5024876345986231)
- MyComplex(0.7704581759272878, 0.06431948804402998)
- MyComplex(0.5067467487289342, 0.37276230976872216)
- MyComplex(0.22392490395791997, 0.24997212682983505)
- MyComplex(0.7938100950862472, 0.499794534596235)
- MyComplex(0.5800027114018041, 0.5539306180949976)
- MyComplex(0.8613302068792389, 0.11099259999056299)
+ MyComplex(0.8949805161160401, 0.421323667111285)
+ MyComplex(0.026053733946824753, 0.9006485214268137)
+ MyComplex(0.8300583605559014, 0.8018415810144012)
+ MyComplex(0.12592125655230224, 0.27000881231142315)
+ MyComplex(0.3618587243109601, 0.4880572513222716)
+ MyComplex(0.3150275713704259, 0.13939673381769024)
+ MyComplex(0.03638847026177339, 0.8020351165630957)
+ MyComplex(0.08217419775238222, 0.7933908176556499)
+ MyComplex(0.03792572372188874, 0.1330438491287449)
 

is represented in memory as

[real1,imag1,real2,imag2,...]

while the struct of array formats are

@@ -43,18 +43,18 @@
-MyComplexes([0.2897241260009822, 0.48104950344076425, 0.1550991673426182, 0
-.4303053146297099, 0.9652545002585547, 0.129691784563555, 0.930721759368184
-5, 0.04236592750770252, 0.12121370563973322, 0.6554310506492863  …  0.73820
-40247731638, 0.057317858458742266, 0.20857864801382386, 0.7618557750226116,
- 0.30777823594243947, 0.19986995680131348, 0.7036870522240618, 0.5790694102
-590798, 0.14664887682564642, 0.44821427291761984], [0.46688590924729956, 0.
-11887230044632568, 0.2806224613334578, 0.43126362776131033, 0.9794620042427
-044, 0.42610522792220007, 0.3077309824326909, 0.6937145539324716, 0.8143594
-272939957, 0.7105773101090823  …  0.24229997305610995, 0.18587298385876627,
- 0.24053515975314443, 0.9719512037832595, 0.03802957417362052, 0.4362822838
-808521, 0.4088166896868569, 0.8960777149330896, 0.5386366912236279, 0.07960
-564919501523])
+MyComplexes([0.4463978501148237, 0.9534146019623043, 0.3272506411204227, 0.
+6610621659220888, 0.7518861360127663, 0.6604957283856057, 0.568257801511073
+4, 0.5249214846587075, 0.28031214215080524, 0.4019397592681623  …  0.230795
+16952268475, 0.4587073529434471, 0.38888294861698836, 0.8764601865880245, 0
+.7664927638985168, 0.5500101294037606, 0.8467864454608346, 0.75150871387712
+67, 0.9504725250659736, 0.7891716988140378], [0.2354669170781668, 0.3484246
+5928318156, 0.4847930258306149, 0.5050470303321312, 0.8805989081762119, 0.3
+6730961426487874, 0.7503851083942035, 0.7838065781271188, 0.833361952697676
+8, 0.4100985361969356  …  0.7241872639287391, 0.2685519569209651, 0.2209582
+6963895615, 0.2074266359413821, 0.6228410415260852, 0.17911567432938458, 0.
+5661141870291473, 0.9051093078571997, 0.37633923990420315, 0.29487469397695
+53])
 
@@ -73,7 +73,7 @@
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:5 within `average`
-define void @julia_average_10372([2 x double]* noalias nocapture noundef no
+define void @julia_average_10360([2 x double]* noalias nocapture noundef no
 nnull sret([2 x double]) align 8 dereferenceable(16) %0, {}* noundef nonnul
 l align 16 dereferenceable(40) %1) #0 {
 top:
@@ -108,21 +108,21 @@
             %.sub = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0,
  i64 0
 ; ││││││││││ @ reduce.jl:432 within `_mapreduce`
-            store {}* inttoptr (i64 139788167070960 to {}*), {}** %.sub, al
+            store {}* inttoptr (i64 139762867029232 to {}*), {}** %.sub, al
 ign 8
             %5 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6
 4 1
-            store {}* inttoptr (i64 139788214797840 to {}*), {}** %5, align
+            store {}* inttoptr (i64 139762914756112 to {}*), {}** %5, align
  8
             %6 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6
 4 2
             store {}* %1, {}** %6, align 8
             %7 = getelementptr inbounds [4 x {}*], [4 x {}*]* %2, i64 0, i6
 4 3
-            store {}* inttoptr (i64 139788196084624 to {}*), {}** %7, align
+            store {}* inttoptr (i64 139762896042896 to {}*), {}** %7, align
  8
-            %8 = call nonnull {}* @ijl_invoke({}* inttoptr (i64 13978817579
-4192 to {}*), {}** nonnull %.sub, i32 4, {}* inttoptr (i64 139788383992288 
+            %8 = call nonnull {}* @ijl_invoke({}* inttoptr (i64 13976287575
+2464 to {}*), {}** nonnull %.sub, i32 4, {}* inttoptr (i64 139761459797280 
 to {}*))
             call void @llvm.trap()
             unreachable
@@ -233,7 +233,7 @@
 ; ││││││││││└
 ; ││││││││││ @ reduce.jl:447 within `_mapreduce`
 ; ││││││││││┌ @ reduce.jl:277 within `mapreduce_impl`
-             call void @j_mapreduce_impl_10374([2 x double]* noalias nocapt
+             call void @j_mapreduce_impl_10362([2 x double]* noalias nocapt
 ure noundef nonnull sret([2 x double]) %tmpcast, {}* nonnull %1, i64 signex
 t 1, i64 signext %arraylen, i64 signext 1024)
 ; └└└└└└└└└└└
@@ -583,7 +583,7 @@ 

Next Level Up: Multithreading

-59.061 μs (6 allocations: 576 bytes)
+59.671 μs (6 allocations: 576 bytes)
 
@@ -596,7 +596,7 @@

Next Level Up: Multithreading

-66.033 μs (6 allocations: 576 bytes)
+66.023 μs (6 allocations: 576 bytes)
 
@@ -609,7 +609,7 @@

Next Level Up: Multithreading

-20.138 μs (6 allocations: 576 bytes)
+20.148 μs (6 allocations: 576 bytes)
 
@@ -637,17 +637,17 @@

Next Level Up: Multithreading

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:26 within `h`
-define void @julia_h_10465() #0 {
+define void @julia_h_10453() #0 {
 top:
-  %.promoted = load i64, i64* inttoptr (i64 139785926445312 to i64*), align
- 256
+  %.promoted = load i64, i64* inttoptr (i64 139761557327088 to i64*), align
+ 16
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:28 within `h`
   %0 = add i64 %.promoted, 10000
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:29 within `h`
 ; ┌ @ Base.jl within `setproperty!`
-   store i64 %0, i64* inttoptr (i64 139785926445312 to i64*), align 256
+   store i64 %0, i64* inttoptr (i64 139761557327088 to i64*), align 16
 ; â””
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:30 within `h`
@@ -673,7 +673,7 @@ 

Next Level Up: Multithreading

-2.785 ns (0 allocations: 0 bytes)
+3.095 ns (0 allocations: 0 bytes)
 
@@ -686,13 +686,13 @@

Next Level Up: Multithreading

 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:3 within `h2`
-define void @julia_h2_10472() #0 {
+define void @julia_h2_10460() #0 {
 top:
 ;  @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral
 lelism.jmd:6 within `h2`
 ; ┌ @ refvalue.jl:59 within `getindex`
 ; │┌ @ Base.jl:37 within `getproperty`
-    %0 = load i64, i64* inttoptr (i64 139787416178784 to i64*), align 32
+    %0 = load i64, i64* inttoptr (i64 139762055925984 to i64*), align 32
 ; └└
 ; ┌ @ range.jl:897 within `iterate`
 ; │┌ @ range.jl:672 within `isempty`
@@ -703,15 +703,15 @@ 

Next Level Up: Multithreading

br i1 %1, label %L34, label %L18.preheader L18.preheader: ; preds = %top - %.promoted = load i64, i64* inttoptr (i64 139785926445312 to i64*), align - 256 + %.promoted = load i64, i64* inttoptr (i64 139761557327088 to i64*), align + 16 ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:8 within `h2` %2 = add i64 %.promoted, %0 ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:7 within `h2` ; ┌ @ Base.jl within `setproperty!` - store i64 %2, i64* inttoptr (i64 139785926445312 to i64*), align 256 + store i64 %2, i64* inttoptr (i64 139761557327088 to i64*), align 16 ; └ ; @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture06/styles_of_paral lelism.jmd:8 within `h2` @@ -742,7 +742,7 @@

Next Level Up: Multithreading

-4.939 ns (0 allocations: 0 bytes)
+4.628 ns (0 allocations: 0 bytes)
 
diff --git a/notes/08-Forward-Mode_Automatic_Differentiation_(AD)_via_High_Dimensional_Algebras/index.html b/notes/08-Forward-Mode_Automatic_Differentiation_(AD)_via_High_Dimensional_Algebras/index.html index c845e817..144ac434 100644 --- a/notes/08-Forward-Mode_Automatic_Differentiation_(AD)_via_High_Dimensional_Algebras/index.html +++ b/notes/08-Forward-Mode_Automatic_Differentiation_(AD)_via_High_Dimensional_Algebras/index.html @@ -18,9 +18,9 @@ ϵ2 = (1+ϵ) - 1 (ϵ - ϵ2)
-ϵ = 3.873035531141372e-11
-1 + ϵ = 1.0000000000387304
-3.0527602113762817e-18
+ϵ = 6.736227852961307e-11
+1 + ϵ = 1.0000000000673623
+-5.940030844987341e-17
 

See how $\epsilon$ is only rebuilt at accuracy around $10^{-16}$ and thus we only keep around 6 digits of accuracy when it's generated at the size of around $10^{-10}$!

Finite Differencing and Numerical Stability

To start understanding how to compute derivatives on a computer, we start with finite differencing. For finite differencing, recall that the definition of the derivative is:

\[ f'(x) = \lim_{\epsilon \rightarrow 0} \frac{f(x+\epsilon)-f(x)}{\epsilon} \]

Finite differencing directly follows from this definition by choosing a small $\epsilon$. However, choosing a good $\epsilon$ is very difficult. If $\epsilon$ is too large than there is error since this definition is asymptotic. However, if $\epsilon$ is too small, you receive roundoff error. To understand why you would get roundoff error, recall that floating point error is relative, and can essentially store 16 digits of accuracy. So let's say we choose $\epsilon = 10^{-6}$. Then $f(x+\epsilon) - f(x)$ is roughly the same in the first 6 digits, meaning that after the subtraction there is only 10 digits of accuracy, and then dividing by $10^{-6}$ simply brings those 10 digits back up to the correct relative size.

This means that we want to choose $\epsilon$ small enough that the $\mathcal{O}(\epsilon^2)$ error of the truncation is balanced by the $O(1/\epsilon)$ roundoff error. Under some minor assumptions, one can argue that the average best point is $\sqrt(E)$, where E is machine epsilon

 @show eps(Float64)
 @show sqrt(eps(Float64))
@@ -95,17 +95,17 @@
 add(a, b)
 @btime add($(Ref(a))[], $(Ref(b))[])
 
-3.396 ns (0 allocations: 0 bytes)
+3.105 ns (0 allocations: 0 bytes)
 Dual{Int64}(4, 6)
 

It seems like we have lost no performance.

 @code_native add(1, 2, 3, 4)
 
 .text
 	.file	"add"
-	.globl	julia_add_13194                 # -- Begin function julia_add_13194
+	.globl	julia_add_13182                 # -- Begin function julia_add_13182
 	.p2align	4, 0x90
-	.type	julia_add_13194,@function
-julia_add_13194:                        # @julia_add_13194
+	.type	julia_add_13182,@function
+julia_add_13182:                        # @julia_add_13182
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture08/automatic_diff
 erentiation.jmd:2 within `add`
 # %bb.0:                                # %top
@@ -121,7 +121,7 @@
 	pop	rbp
 	ret
 .Lfunc_end0:
-	.size	julia_add_13194, .Lfunc_end0-julia_add_13194
+	.size	julia_add_13182, .Lfunc_end0-julia_add_13182
 ; â””
                                         # -- End function
 	.section	".note.GNU-stack","",@progbits
@@ -130,10 +130,10 @@
 
 .text
 	.file	"add"
-	.globl	julia_add_13196                 # -- Begin function julia_add_13196
+	.globl	julia_add_13184                 # -- Begin function julia_add_13184
 	.p2align	4, 0x90
-	.type	julia_add_13196,@function
-julia_add_13196:                        # @julia_add_13196
+	.type	julia_add_13184,@function
+julia_add_13184:                        # @julia_add_13184
 ; ┌ @ /home/runner/work/SciMLBook/SciMLBook/_weave/lecture08/automatic_diff
 erentiation.jmd:5 within `add`
 # %bb.0:                                # %top
@@ -152,7 +152,7 @@
 	pop	rbp
 	ret
 .Lfunc_end0:
-	.size	julia_add_13196, .Lfunc_end0-julia_add_13196
+	.size	julia_add_13184, .Lfunc_end0-julia_add_13184
 ; └└└
                                         # -- End function
 	.section	".note.GNU-stack","",@progbits
@@ -274,7 +274,7 @@
 2-element Vector{Int64}:
  6
  2
-

Directional derivative and gradient of functions $f: \mathbb{R}^n \to \mathbb{R}$

For a function $f: \mathbb{R}^n \to \mathbb{R}$ the basic operation is the directional derivative:

\[ \lim_{\epsilon \to 0} \frac{f(\mathbf{x} + \epsilon \mathbf{v}) - f(\mathbf{x})}{\epsilon} = [\nabla f(\mathbf{x})] \cdot \mathbf{v}, \]

where $\epsilon$ is still a single dimension and $\nabla f(\mathbf{x})$ is the direction in which we calculate.

We can directly do this using the same simple Dual numbers as above, using the same $\epsilon$, e.g.

\[ f(x, y) = x^2 \sin(y) \]

\[ \begin{align} f(x_0 + a\epsilon, y_0 + b\epsilon) &= (x_0 + a\epsilon)^2 \sin(y_0 + b\epsilon) \\ &= x_0^2 \sin(y_0) + \epsilon[2ax_0 \sin(y_0) + x_0^2 b \cos(y_0)] + o(\epsilon) \end{align} \]

so we have indeed calculated $\nabla f(x_0, y_0) \cdot \mathbf{v},$ where $\mathbf{v} = (a, b)$ are the components that we put into the derivative component of the Dual numbers.

If we wish to calculate the directional derivative in another direction, we could repeat the calculation with a different $\mathbf{v}$. A better solution is to use another independent epsilon $\epsilon$, expanding $x = x_0 + a_1 \epsilon_1 + a_2 \epsilon_2$ and putting $\epsilon_1 \epsilon_2 = 0$.

In particular, if we wish to calculate the gradient itself, $\nabla f(x_0, y_0)$, we need to calculate both partial derivatives, which corresponds to two directional derivatives, in the directions $(1, 0)$ and $(0, 1)$, respectively.

Forward-Mode AD as jvp

Note that another representation of the directional derivative is $f'(x)v$, where $f'(x)$ is the Jacobian or total derivative of $f$ at $x$. To see the equivalence of this to a directional derivative, write it out in the standard basis:

\[ w_i = \sum_{j}^{m} J_{ij} v_{j} \]

Now write out what $J$ means and we see that:

\[ w_i = \sum_j^{m} \frac{df_i}{dx_j} v_j = \nabla f_i(x) \cdot v \]

The primitive action of forward-mode AD is $f'(x)v$!

This is also known as a Jacobian-vector product, or jvp for short.

We can thus represent vector calculus with multidimensional dual numbers as follows. Let $d =[x,y]$, the vector of dual numbers. We can instead represent this as:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 \]

where $d_0$ is the primal vector $[x_0,y_0]$ and the $v_i$ are the vectors for the dual directions. If you work out this algebra, then note that a single application of $f$ to a multidimensional dual number calculates:

\[ f(d) = f(d_0) + f'(d_0)v_1 \epsilon_1 + f'(d_0)v_2 \epsilon_2 \]

i.e. it calculates the result of $f(x,y)$ and two separate directional derivatives. Note that because the information about $f(d_0)$ is shared between the calculations, this is more efficient than doing multiple applications of $f$. And of course, this is then generalized to $m$ many directional derivatives at once by:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 + \ldots + v_m \epsilon_m \]

Jacobian

For a function $f: \mathbb{R}^n \to \mathbb{R}^m$, we reduce (conceptually, although not necessarily in code) to its component functions $f_i: \mathbb{R}^n \to \mathbb{R}$, where $f(x) = (f_1(x), f_2(x), \ldots, f_m(x))$.

Then

\[ \begin{align} f(x + \epsilon v) &= (f_1(x + \epsilon v), \ldots, f_m(x + \epsilon v)) \\ &= (f_1(x) + \epsilon[\nabla f_1(x) \cdot v], \dots, f_m(x) + \epsilon[\nabla f_m(x) \cdot v] \\ &= f(x) + [f'(x) \cdot v] \epsilon, \end{align} \]

To calculate the complete Jacobian, we calculate these directional derivatives in the $n$ different directions of the basis vectors, i.e. if

\[ d = d_0 + e_1 \epsilon_1 + \ldots + e_n \epsilon_n \]

for $e_i$ the $i$th basis vector, then

\[ f(d) = f(d_0) + Je_1 \epsilon_1 + \ldots + Je_n \epsilon_n \]

computes all columns of the Jacobian simultaneously.

Array of Structs Representation

Instead of thinking about a vector of dual numbers, thus we can instead think of dual numbers with vectors for the components. But if there are vectors for the components, then we can think of the grouping of dual components as a matrix. Thus define our multidimensional multi-partial dual number as:

\[ D_0 = [d_1,d_2,d_3,\ldots,d_n] \]

\[ \Sigma = \begin{bmatrix} d_{11} & d_{12} & \cdots & d_{1n} \\ d_{21} & d_{22} & & \vdots \\ \vdots & & \ddots & \vdots \\ d_{m1} & \hdots & \hdots & d_{mn} \end{bmatrix} \]

\[ \epsilon=[\epsilon_1,\epsilon_2,\ldots,\epsilon_m] \]

\[ D = D_0 + \Sigma \epsilon \]

where $D_0$ is a vector in $\mathbb{R}^n$, $\epsilon$ is a vector of dimensional signifiers and $\Sigma$ is a matrix in $\mathbb{R}^{n \times m}$ where $m$ is the number of concurrent differentiation dimensions. Each row of this is a dual number, but now we can use this to easily define higher dimensional primitives.

For example, let $f(x) = Ax$, matrix multiplication. Then, we can show with our dual number arithmetic that:

\[ f(D) = A*D_0 + A*\Sigma*\epsilon \]

is how one would compute the value of $f(D_0)$ and the derivative $f'(D_0)$ in all directions signified by the columns of $\Sigma$ simultaneously. Using multidimensional Taylor series expansions and doing the manipulations like before indeed implies that the arithmetic on this object should follow:

\[ f(D) = f(D_0) + f'(D_0)\Sigma \epsilon \]

where $f'$ is the total derivative or the Jacobian of $f$. This then allows our system to be highly efficient by allowing the definition of multidimensional functions, like linear algebra, to be primitives of multi-directional derivatives.

Higher derivatives

The above techniques can be extended to higher derivatives by adding more terms to the Taylor polynomial, e.g.

\[ f(a + \epsilon) = f(a) + \epsilon f'(a) + \frac{1}{2} \epsilon^2 f''(a) + o(\epsilon^2). \]

We treat this as a degree-2 (or degree-$n$, in general) polynomial and do polynomial arithmetic to calculate the new polynomials. The coefficients of powers of $\epsilon$ then give the higher-order derivatives.

For example, for a function $f: \mathbb{R}^n \to \mathbb{R}$ we have

\[ f(x + \epsilon v) = f(x) + \epsilon \left[ \sum_i (\partial_i f)(x) v_i \right] + \frac{1}{2}\epsilon^2 \left[ \sum_i \sum_j (\partial_{i,j} f) v_i v_j \right] \]

using Dual numbers with a single $\epsilon$ component. In this way we can compute coefficients of the (symmetric) Hessian matrix.

Application: solving nonlinear equations using the Newton method

As an application, we will see how to solve nonlinear equations of the form $f(x) = 0$ for functions $f: \mathbb{R}^n \to \mathbb{R}^n$.

Since in general we cannot do anything with nonlinearity, we try to reduce it (approximate it) with something linear. Furthermore, in general we know that it is not possible to solve nonlinear equations in closed form (even for polynomials of degree $\ge 5$), so we will need some kind of iterative method.

We start from an initial guess $x_0$. The idea of the Newton method is to follow the tangent line to the function $f$ at the point $x_0$ and find where it intersects the $x$-axis; this will give the next iterate $x_1$.

Algebraically, we want to solve $f(x_1) = 0$. Suppose that $x_1 = x_0 + \delta$ for some $\delta$ that is currently unknown and which we wish to calculate.

Assuming $\delta$ is small, we can expand:

\[ f(x_1) = f(x_0 + \delta) = f(x_0) + Df(x_0) \cdot \delta + \mathcal{O}(\| \delta \|^2). \]

Since we wish to solve

\[ f(x_0 + \delta) \simeq 0, \]

we put

\[ f(x_0) + Df(x_0) \cdot \delta = 0, \]

so that mathematically we have

\[ \delta = -[Df(x_0)]^{-1} \cdot f(x_0). \]

Computationally we prefer to solve the matrix equation

\[ J \delta = -f(x_0), \]

where $J := Df(x_0)$ is the Jacobian of the function; Julia uses the syntax \ ("backslash") for solving linear systems in an efficient way:

+

Directional derivative and gradient of functions $f: \mathbb{R}^n \to \mathbb{R}$

For a function $f: \mathbb{R}^n \to \mathbb{R}$ the basic operation is the directional derivative:

\[ \lim_{\epsilon \to 0} \frac{f(\mathbf{x} + \epsilon \mathbf{v}) - f(\mathbf{x})}{\epsilon} = [\nabla f(\mathbf{x})] \cdot \mathbf{v}, \]

where $\epsilon$ is still a single dimension and $\nabla f(\mathbf{x})$ is the direction in which we calculate.

We can directly do this using the same simple Dual numbers as above, using the same $\epsilon$, e.g.

\[ f(x, y) = x^2 \sin(y) \]

\[ \begin{align} f(x_0 + a\epsilon, y_0 + b\epsilon) &= (x_0 + a\epsilon)^2 \sin(y_0 + b\epsilon) \\ &= x_0^2 \sin(y_0) + \epsilon[2ax_0 \sin(y_0) + x_0^2 b \cos(y_0)] + o(\epsilon) \end{align} \]

so we have indeed calculated $\nabla f(x_0, y_0) \cdot \mathbf{v},$ where $\mathbf{v} = (a, b)$ are the components that we put into the derivative component of the Dual numbers.

If we wish to calculate the directional derivative in another direction, we could repeat the calculation with a different $\mathbf{v}$. A better solution is to use another independent epsilon $\epsilon$, expanding $x = x_0 + a_1 \epsilon_1 + a_2 \epsilon_2$ and putting $\epsilon_1 \epsilon_2 = 0$.

In particular, if we wish to calculate the gradient itself, $\nabla f(x_0, y_0)$, we need to calculate both partial derivatives, which corresponds to two directional derivatives, in the directions $(1, 0)$ and $(0, 1)$, respectively.

Forward-Mode AD as jvp

Note that another representation of the directional derivative is $f'(x)v$, where $f'(x)$ is the Jacobian or total derivative of $f$ at $x$. To see the equivalence of this to a directional derivative, write it out in the standard basis:

\[ w_i = \sum_{j}^{m} J_{ij} v_{j} \]

Now write out what $J$ means and we see that:

\[ w_i = \sum_j^{m} \frac{df_i}{dx_j} v_j = \nabla f_i(x) \cdot v \]

The primitive action of forward-mode AD is $f'(x)v$!

This is also known as a Jacobian-vector product, or jvp for short.

We can thus represent vector calculus with multidimensional dual numbers as follows. Let $d =[x,y]$, the vector of dual numbers. We can instead represent this as:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 \]

where $d_0$ is the primal vector $[x_0,y_0]$ and the $v_i$ are the vectors for the dual directions. If you work out this algebra, then note that a single application of $f$ to a multidimensional dual number calculates:

\[ f(d) = f(d_0) + f'(d_0)v_1 \epsilon_1 + f'(d_0)v_2 \epsilon_2 \]

i.e. it calculates the result of $f(x,y)$ and two separate directional derivatives. Note that because the information about $f(d_0)$ is shared between the calculations, this is more efficient than doing multiple applications of $f$. And of course, this is then generalized to $m$ many directional derivatives at once by:

\[ d = d_0 + v_1 \epsilon_1 + v_2 \epsilon_2 + \ldots + v_m \epsilon_m \]

Jacobian

For a function $f: \mathbb{R}^n \to \mathbb{R}^m$, we reduce (conceptually, although not necessarily in code) to its component functions $f_i: \mathbb{R}^n \to \mathbb{R}$, where $f(x) = (f_1(x), f_2(x), \ldots, f_m(x))$.

Then

\[ \begin{align} f(x + \epsilon v) &= (f_1(x + \epsilon v), \ldots, f_m(x + \epsilon v)) \\ &= (f_1(x) + \epsilon[\nabla f_1(x) \cdot v], \dots, f_m(x) + \epsilon[\nabla f_m(x) \cdot v] \\ &= f(x) + [f'(x) \cdot v] \epsilon, \end{align} \]

To calculate the complete Jacobian, we calculate these directional derivatives in the $n$ different directions of the basis vectors, i.e. if

\[ d = d_0 + e_1 \epsilon_1 + \ldots + e_n \epsilon_n \]

for $e_i$ the $i$th basis vector, then

\[ f(d) = f(d_0) + Je_1 \epsilon_1 + \ldots + Je_n \epsilon_n \]

computes all columns of the Jacobian simultaneously.

Struct of Arrays Representation

Instead of thinking about a vector of dual numbers, thus we can instead think of dual numbers with vectors for the components. But if there are vectors for the components, then we can think of the grouping of dual components as a matrix. Thus define our multidimensional multi-partial dual number as:

\[ D_0 = [d_1,d_2,d_3,\ldots,d_n] \]

\[ \Sigma = \begin{bmatrix} d_{11} & d_{12} & \cdots & d_{1n} \\ d_{21} & d_{22} & & \vdots \\ \vdots & & \ddots & \vdots \\ d_{m1} & \cdots & \cdots & d_{mn} \end{bmatrix} \]

\[ \epsilon=[\epsilon_1,\epsilon_2,\ldots,\epsilon_m] \]

\[ D = D_0 + \Sigma \epsilon \]

where $D_0$ is a vector in $\mathbb{R}^n$, $\epsilon$ is a vector of dimensional signifiers and $\Sigma$ is a matrix in $\mathbb{R}^{n \times m}$ where $m$ is the number of concurrent differentiation dimensions. Each row of this is a dual number, but now we can use this to easily define higher dimensional primitives.

For example, let $f(x) = Ax$, matrix multiplication. Then, we can show with our dual number arithmetic that:

\[ f(D) = A*D_0 + A*\Sigma*\epsilon \]

is how one would compute the value of $f(D_0)$ and the derivative $f'(D_0)$ in all directions signified by the columns of $\Sigma$ simultaneously. Using multidimensional Taylor series expansions and doing the manipulations like before indeed implies that the arithmetic on this object should follow:

\[ f(D) = f(D_0) + f'(D_0)\Sigma \epsilon \]

where $f'$ is the total derivative or the Jacobian of $f$. This then allows our system to be highly efficient by allowing the definition of multidimensional functions, like linear algebra, to be primitives of multi-directional derivatives.

Higher derivatives

The above techniques can be extended to higher derivatives by adding more terms to the Taylor polynomial, e.g.

\[ f(a + \epsilon) = f(a) + \epsilon f'(a) + \frac{1}{2} \epsilon^2 f''(a) + o(\epsilon^2). \]

We treat this as a degree-2 (or degree-$n$, in general) polynomial and do polynomial arithmetic to calculate the new polynomials. The coefficients of powers of $\epsilon$ then give the higher-order derivatives.

For example, for a function $f: \mathbb{R}^n \to \mathbb{R}$ we have

\[ f(x + \epsilon v) = f(x) + \epsilon \left[ \sum_i (\partial_i f)(x) v_i \right] + \frac{1}{2}\epsilon^2 \left[ \sum_i \sum_j (\partial_{i,j} f) v_i v_j \right] \]

using Dual numbers with a single $\epsilon$ component. In this way we can compute coefficients of the (symmetric) Hessian matrix.

Application: solving nonlinear equations using the Newton method

As an application, we will see how to solve nonlinear equations of the form $f(x) = 0$ for functions $f: \mathbb{R}^n \to \mathbb{R}^n$.

Since in general we cannot do anything with nonlinearity, we try to reduce it (approximate it) with something linear. Furthermore, in general we know that it is not possible to solve nonlinear equations in closed form (even for polynomials of degree $\ge 5$), so we will need some kind of iterative method.

We start from an initial guess $x_0$. The idea of the Newton method is to follow the tangent line to the function $f$ at the point $x_0$ and find where it intersects the $x$-axis; this will give the next iterate $x_1$.

Algebraically, we want to solve $f(x_1) = 0$. Suppose that $x_1 = x_0 + \delta$ for some $\delta$ that is currently unknown and which we wish to calculate.

Assuming $\delta$ is small, we can expand:

\[ f(x_1) = f(x_0 + \delta) = f(x_0) + Df(x_0) \cdot \delta + \mathcal{O}(\| \delta \|^2). \]

Since we wish to solve

\[ f(x_0 + \delta) \simeq 0, \]

we put

\[ f(x_0) + Df(x_0) \cdot \delta = 0, \]

so that mathematically we have

\[ \delta = -[Df(x_0)]^{-1} \cdot f(x_0). \]

Computationally we prefer to solve the matrix equation

\[ J \delta = -f(x_0), \]

where $J := Df(x_0)$ is the Jacobian of the function; Julia uses the syntax \ ("backslash") for solving linear systems in an efficient way:

 using ForwardDiff, StaticArrays
 
 function newton_step(f, x0)
diff --git a/notes/16-From_Optimization_to_Probabilistic_Programming/index.html b/notes/16-From_Optimization_to_Probabilistic_Programming/index.html
index f60cebae..f059304c 100644
--- a/notes/16-From_Optimization_to_Probabilistic_Programming/index.html
+++ b/notes/16-From_Optimization_to_Probabilistic_Programming/index.html
@@ -24,7 +24,7 @@
 prob1 = ODEProblem(lotka_volterra,u0,tspan,_θ)
 sol = solve(prob1,Tsit5())
 plot(sol)
-

and from which we can get an ensemble of solutions:

+

and from which we can get an ensemble of solutions:

 prob_func = function (prob,i,repeat)
   remake(prob,p=rand.(θ))
 end
@@ -34,21 +34,21 @@
 
 using DiffEqBase.EnsembleAnalysis
 plot(EnsembleSummary(sol))
-

From just a few variables having probabilities, every variable has an induced probability: there is a probability distribution on the integrator states, the output at time t_i, etc.

Bayesian Estimation with Point Estimates: Bayes' Rule, Maximum Likelihood, and MAP

Recall from our previous studies that the difficult part of modeling is not necessarily the forward modeling approach, rather it's the incorporation of data or the estimation problem that is difficult. When your variables are now random distributions, how do you "fit" them?

The answer comes from Bayes' rule, which is the following. Assume you had a prior distribution $p(\theta)$ for the probability that $X$ is a given value $\theta$. Then the posterior probability distribution, $p(\theta|D)$, or the distribution which is updated to include data, is given by:

\[ p(\theta|D) = \frac{p(D|\theta)p(\theta)}{\int_\Omega p(D|\theta)p(\theta)d\theta} \]

The scaling factor on the denominator is simply a constant to make the distribution integrate 1 (so that the resulting function is a probability distribution!). The numerator is simply the prior distribution multiplied by the likelihood of seeing the data given the value of the random variable. The prior distribution must be given but notice that the likelihood has another name: the likelihood is the model.

The reason why it's the same thing is because the model is what tells you the expected outcomes given a value of the random variable, and your data is on an expected outcome! However, the likelihood encodes a little bit more information in that it again is a distribution and not a point estimate. We need to make a choice for our measurement distribution on our model's results.

Quick Question: Why is this referred to as measurement noise? Why is it not process noise?

A common choice for the measurement distribution is the Normal distribution. This comes from the Central Limit Theorem (CLT) which essentially states that, given enough interacting mechanisms, the average values of things "tend to become normally distributed". The true statement of the CLT is much more complex, but that is a decent working definition for practical use. The normal distribution is defined by two parameters, $\mu$ and $\sigma$, and is given by the following function:

\[ f(x;\mu,\sigma) = \frac{1}{\sigma\sqrt{2\pi}}\exp\left(\frac{-(x-\mu)^2}{2\sigma^2}\right) \]

This is a bell curve centered at $\mu$ with a variance of $\sigma$. Our best guess for the output, i.e. the model's prediction, should be the average measurement, meaning that $\mu$ is the result from the simulator. $\sigma$ is a parameter for how much measurement error we expect (some intuition on $\sigma$ will come soon).

Let's return to thinking about the ODE example. In this case, we have $\theta$ as a vector of random variables. This means that $u(t;\theta)$ is a random variable for the ODE $u'= ...$'s solution at a given point in time $t$. If we have a measurement at a time $t_i$ and assume our measurement noise is normally distributed with some constant measurement noise $\sigma$, then the likelihood of our data would be $f(x_i;u(t_i;\theta),\sigma)$ at each data point $(t_i,x_i)$. From probability we know that seeing the composition of events is given by the multiplication of probabilities, so the probability of seeing the full dataset given observations $D = (t_i,x_i)$ along the timeseries is:

\[ p(D|\theta) = \prod_i f(x_i;u(t_i;\theta),\sigma) \]

This can be read as: solve the model with the given parameters, and the probability of having seen the measurement is thus given by a product of normal distribution calculations. Note that in many cases the product is not numerically stable (and grows exponentially), and so the likelihood is transformed to the log-likelihood. To get this expression, we take the log of both sides and notice that the product becomes a summation, and thus:

\[ \begin{align} \log p(D|\theta) &= \sum_i \log f(x_i;u(t_i;\theta),\sigma)\\ &= \frac{N}{\log(\sqrt{2\pi}\sigma)} + \frac{1}{2\sigma^2} \sum_i -(x_i - u(t_i; \theta))^2 \end{align} \]

Notice that maximizing this log-likelihood is equivalent to minimizing the L2 norm of the solution against the data!. Thus we can see a few things:

  1. Previous parameter estimation by minimizing a norm against data can be seen as maximum likelihood with some measurement distribution. L2 norm corresponds to assuming measurement noise is normally distributed and all of the measurements have the same error variance.

  2. By the same derivation, having different error variances with normally distributed errors is equivalent to doing weighted L2 estimation.

This reformulation (generalization?) to likelihoods of probability distributions is known as maximum likelihood estimation (MLE), but is equivalent to our previous forms of parameter estimation using point estimates against data. However, this calculation is ignoring Bayes' rule, and is thus not finding the parameters which have the highest probability. To do that, we need to go back to Bayes' rule which states that:

\[ \log p(\theta|D) = \log p(D|\theta) + \log p(\theta) - C \]

Thus, maximizing the log-likelihood is "almost" the same as finding the most probable parameters, except that we need to add weights given $\log p(\theta)$ from our prior distribution! If we assume our prior distribution is flat, like a uniform distribution, then we have a non-informative prior and the maximum posterior point matches that of the maximum likelihood estimation. However, this formulation allows us to get point estimates in a way that takes into account prior knowledge, and is call maximum a posteriori estimation (MAP).

Bayesian Estimation of Posterior Distributions with Monte Carlo

The previous discussion still solely focused on getting point estimates for the most probable parameters. However, what if we wanted to find the distributions of the parameters, i.e. the full $p(D|\theta)$? Outside of very few small models, this cannot be done analytically and is thus the basic problem of probabilistic programming. There are two general approaches:

  1. Sampling-based approaches. Sample parameters $\theta_i$ in such a manner that the array $[\theta_i]$ converges to an array sampled from the true distribution, and thus with enough samples one can capture the distribution numerically.

  2. Variational inference. Find some way to represent the probability distribution and push forward the distributions at every step of the program.

Recovering Distributions from Sampled Points

It's clear from above that if you have a distribution, like Normal(5,1), that you can sample from the distribution to get an array of values which follow the distribution. However, in order for the following sampling approaches to make sense, we need to see how to recover a distribution from discrete samples. So let's say you had a bunch of normally distributed points:

+

From just a few variables having probabilities, every variable has an induced probability: there is a probability distribution on the integrator states, the output at time t_i, etc.

Bayesian Estimation with Point Estimates: Bayes' Rule, Maximum Likelihood, and MAP

Recall from our previous studies that the difficult part of modeling is not necessarily the forward modeling approach, rather it's the incorporation of data or the estimation problem that is difficult. When your variables are now random distributions, how do you "fit" them?

The answer comes from Bayes' rule, which is the following. Assume you had a prior distribution $p(\theta)$ for the probability that $X$ is a given value $\theta$. Then the posterior probability distribution, $p(\theta|D)$, or the distribution which is updated to include data, is given by:

\[ p(\theta|D) = \frac{p(D|\theta)p(\theta)}{\int_\Omega p(D|\theta)p(\theta)d\theta} \]

The scaling factor on the denominator is simply a constant to make the distribution integrate 1 (so that the resulting function is a probability distribution!). The numerator is simply the prior distribution multiplied by the likelihood of seeing the data given the value of the random variable. The prior distribution must be given but notice that the likelihood has another name: the likelihood is the model.

The reason why it's the same thing is because the model is what tells you the expected outcomes given a value of the random variable, and your data is on an expected outcome! However, the likelihood encodes a little bit more information in that it again is a distribution and not a point estimate. We need to make a choice for our measurement distribution on our model's results.

Quick Question: Why is this referred to as measurement noise? Why is it not process noise?

A common choice for the measurement distribution is the Normal distribution. This comes from the Central Limit Theorem (CLT) which essentially states that, given enough interacting mechanisms, the average values of things "tend to become normally distributed". The true statement of the CLT is much more complex, but that is a decent working definition for practical use. The normal distribution is defined by two parameters, $\mu$ and $\sigma$, and is given by the following function:

\[ f(x;\mu,\sigma) = \frac{1}{\sigma\sqrt{2\pi}}\exp\left(\frac{-(x-\mu)^2}{2\sigma^2}\right) \]

This is a bell curve centered at $\mu$ with a variance of $\sigma$. Our best guess for the output, i.e. the model's prediction, should be the average measurement, meaning that $\mu$ is the result from the simulator. $\sigma$ is a parameter for how much measurement error we expect (some intuition on $\sigma$ will come soon).

Let's return to thinking about the ODE example. In this case, we have $\theta$ as a vector of random variables. This means that $u(t;\theta)$ is a random variable for the ODE $u'= ...$'s solution at a given point in time $t$. If we have a measurement at a time $t_i$ and assume our measurement noise is normally distributed with some constant measurement noise $\sigma$, then the likelihood of our data would be $f(x_i;u(t_i;\theta),\sigma)$ at each data point $(t_i,x_i)$. From probability we know that seeing the composition of events is given by the multiplication of probabilities, so the probability of seeing the full dataset given observations $D = (t_i,x_i)$ along the timeseries is:

\[ p(D|\theta) = \prod_i f(x_i;u(t_i;\theta),\sigma) \]

This can be read as: solve the model with the given parameters, and the probability of having seen the measurement is thus given by a product of normal distribution calculations. Note that in many cases the product is not numerically stable (and grows exponentially), and so the likelihood is transformed to the log-likelihood. To get this expression, we take the log of both sides and notice that the product becomes a summation, and thus:

\[ \begin{align} \log p(D|\theta) &= \sum_i \log f(x_i;u(t_i;\theta),\sigma)\\ &= \frac{N}{\log(\sqrt{2\pi}\sigma)} + \frac{1}{2\sigma^2} \sum_i -(x_i - u(t_i; \theta))^2 \end{align} \]

Notice that maximizing this log-likelihood is equivalent to minimizing the L2 norm of the solution against the data!. Thus we can see a few things:

  1. Previous parameter estimation by minimizing a norm against data can be seen as maximum likelihood with some measurement distribution. L2 norm corresponds to assuming measurement noise is normally distributed and all of the measurements have the same error variance.

  2. By the same derivation, having different error variances with normally distributed errors is equivalent to doing weighted L2 estimation.

This reformulation (generalization?) to likelihoods of probability distributions is known as maximum likelihood estimation (MLE), but is equivalent to our previous forms of parameter estimation using point estimates against data. However, this calculation is ignoring Bayes' rule, and is thus not finding the parameters which have the highest probability. To do that, we need to go back to Bayes' rule which states that:

\[ \log p(\theta|D) = \log p(D|\theta) + \log p(\theta) - C \]

Thus, maximizing the log-likelihood is "almost" the same as finding the most probable parameters, except that we need to add weights given $\log p(\theta)$ from our prior distribution! If we assume our prior distribution is flat, like a uniform distribution, then we have a non-informative prior and the maximum posterior point matches that of the maximum likelihood estimation. However, this formulation allows us to get point estimates in a way that takes into account prior knowledge, and is call maximum a posteriori estimation (MAP).

Bayesian Estimation of Posterior Distributions with Monte Carlo

The previous discussion still solely focused on getting point estimates for the most probable parameters. However, what if we wanted to find the distributions of the parameters, i.e. the full $p(D|\theta)$? Outside of very few small models, this cannot be done analytically and is thus the basic problem of probabilistic programming. There are two general approaches:

  1. Sampling-based approaches. Sample parameters $\theta_i$ in such a manner that the array $[\theta_i]$ converges to an array sampled from the true distribution, and thus with enough samples one can capture the distribution numerically.

  2. Variational inference. Find some way to represent the probability distribution and push forward the distributions at every step of the program.

Recovering Distributions from Sampled Points

It's clear from above that if you have a distribution, like Normal(5,1), that you can sample from the distribution to get an array of values which follow the distribution. However, in order for the following sampling approaches to make sense, we need to see how to recover a distribution from discrete samples. So let's say you had a bunch of normally distributed points:

 X = Normal(5,1)
 x = [rand(X) for i in 1:100]
 scatter(x,[1 for i in 1:100])
-

Notice that there are more points in the areas of higher probability. Thus the density of sampled points gives us an estimate for the probability of having points in a given area. We can then count the number of points in a bin and divide by the total number of points in order to get the probability of being in a specific region. This is depicted by a histogram:

+

Notice that there are more points in the areas of higher probability. Thus the density of sampled points gives us an estimate for the probability of having points in a given area. We can then count the number of points in a bin and divide by the total number of points in order to get the probability of being in a specific region. This is depicted by a histogram:

 histogram(x)
-

and we see this converges when we get more points:

+

and we see this converges when we get more points:

 histogram([rand(X) for i in 1:10000],normed=true)
 using StatsPlots
 plot!(X,lw=5)
-

A continuous form of this is the kernel density estimate, which is essentially a smoothed binning approach.

+

A continuous form of this is the kernel density estimate, which is essentially a smoothed binning approach.

 using KernelDensity
 plot(kde([rand(X) for i in 1:10000]),lw=5)
 plot!(X,lw=5)
-

Thus, for the sampling-based approaches, we simply need to arrive at an array which is sampled according to the distribution that we want to estimate, and from that array we can recover the distribution.

Sampling Distributions with the Metropolis-Hastings Algorithm

The Metropolis-Hastings algorithm is the simplest form of Markov Chain Monte Carlo (MCMC) which gives a way of sampling the $\theta$ distribution. To see how this algorithm works, let's understand the ratio between two points in the posterior probability. If we have $x_i$ and $x_j$, the ratio of the two probabilities would be given by:

\[ \frac{p(x_i|D)}{p(x_j|D)} = \frac{p(D|x_i)p(x_i)}{p(D|x_j)p(x_j)} \]

(notice that the integration constant cancels). This motivates the idea that all we have to do is ensure we only go to a point $x_j$ from $x_i$ with probability difference that matches that ratio, and over time if we do this between "all points" we will have the right number of "each point" in the distribution (quotes because it's continuous). With a bit more rigour we arrive at the following algorithm:

  1. Starting at $x_i$, take $x_{i+1}$ from a sampling algorithm $g(x_{i+1}|x_i)$.

  2. Calculate $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})g(x_i|x_{i+1})}{p(D|x_i)p(x_i)g(x_{i+1}|x_i)}\right)$. Notice that if we require $g$ to be symmetric, then this simplifies to the probability ratio $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})}{p(D|x_i)p(x_i)}\right)$

  3. Use a random number to accept the step with a probability $A$. Go back to step 1, incrementing $i$ if accepted, otherwise just repeat.

I.e, we just walk around the space biasing the acceptance of a step by the factor $\frac{p(x_i|D)}{p(x_j|D)}$ and sooner or later we will have spent the right amount of time in each area, giving the correct distribution.

(This can be rigorously proven, and those details are left out.)

The Cost of Bayesian Estimation

Let's take a quick moment to understand the high cost of Bayesian posterior estimations. While before we were getting point estimates, now we are trying to recover a full probability distribution, and each accept/reject probability calculation requires evaluating the likelihood at some point. Remember, the likelihood is generated by our simulator, and thus every evaluation here is an ODE solver call or a neural network forward pass! This means that to get good distributions, we are solving the ODE hundreds of thousands of times, i.e. even more than when doing parameter estimation! This is something to keep in mind.

However, notice that this process is trivially parallelizable. We can just have parallel chains on going, i.e. start 16 processes all doing Metropolis-Hastings, and in the end they are all sampling from the same distribution, so the final array can simply be the pooled results of each chain.

Hamiltonian Monte Carlo

Metropolis-Hastings is easy to motivate and implement. However, it does not do well in high dimensional spaces because it searches in all directions. For example, it's common for the sampling distribution $g$ to be a multivariable distribution (i.e. normal in all directions). However, high dimensional objects commonly sit on low dimensional manifolds (known as the manifold hypothesis). If that's the case, the most probable set of parameters is something that is low dimensional. For example, parameters may compensate for one another, and so $\theta_1^2 + \theta_2^2 + \theta_3^2 = 1$ might be the manifold on which all of the most probable choices for $\theta$ lie, in which case we need sample on the sphere instead of all of $\mathbb{R}^3$.

However, it's quick to see that this will give Metropolis-Hastings some trouble, since it will use a normal distribution around the current point, and thus even if we start on the sphere, it will have a high chance of trying a point not on the sphere in the next round! This can be depicted as:

Recall that every single rejection is still evaluating the likelihood (since it's calculating an acceptance probability, finding it near zero, rejecting and starting again), and every likelihood call is calling our simulator, and so this is sllllllooooooooooooooowwwwwwwww in high dimensions!

What we need to do instead is ensure that we walk along the path of high probability. What we want to do is thus build a vector field that matches our high probability regions

and follow said vector field (following a vector field is solving what kind of equation?). The first idea one might have is to use the gradient. However, while this idea has the right intentions, the issue is that the gradient of the probability will average out all of the possible probabilities, and will thus flow towards the mode of the distribution:

To overcome this issue, we look to physical systems and see that a satellite orbiting a planet always nicely stays on some manifold instead of following the gradient:

The reason why it does is because it has momentum. Recall from basic physics that one way to describe a physical system is through Hamiltonian mechanics, where $H(x,p)$ is the energy associated with the state $(x,p)$ (normally $x$ is location and $p$ is momentum). Due to conservation of energy, the solution of the dynamical equations leads to $H(x,p)$ being constant, and thus the dynamics follow the level sets of $H$. From the Hamiltonian the dynamics of the system are:

\[ \begin{align} \frac{dx}{dt} &= \frac{dH}{dp}\\ &= -\frac{dH}{dx} \end{align} \]

Here we want our Hamiltonian to be our posterior probability, so that way we stay on the manifold of high probability. This means:

\[ H(x,p) = - \log \pi(x,p) \]

where $\pi(x,p) = \pi(p|x)\pi(x)$ (where I am now using $pi$ for probability since $p$ is momentum!). So to lift from a probability over parameters to one that includes momentum, we simply need to choose a conditional distribution $\pi(p|x)$. This would mean that

\[ \begin{align} H(x,p) &= -log \pi(p|x) - \log \pi(x)\\ &= K(p,x) + V(x) \end{align} \]

where $K$ is the kinetic energy and $V$ is the potential. Thus the potential energy is directly given by the posterior calculation, and the kinetic energy is thus a choice that is used to build the correct Hamiltonian. Hamiltonian Monte Carlo methods then dig into good ways to choose the kinetic energy function. This is done at the start (along with the choice of ODE solver time step) in such a way that it maximizes acceptance probabilities.

Connections to Differentiable Programming

\[ -\frac{dH}{dx} \]

requires calculating the gradient of the likelihood function with respect to the parameters, so we are once again using the gradient of our simulator! This means that all of our previous discussion on automatic differentiation and differentiable programming applies to the Hamiltonian Monte Carlo context.

There's another thread to follow that transformations of probability distributions are pushforwards of the Jacobian transformations (given the transformation of an integral formula), and this is used when doing variational inference.

Symplectic and Geometric Integration

One way to integrate the system of ODEs which result from the Hamiltonian system is to convert it to a system of first order ODEs and solve it directly. However, this loses information and can result in drift. This is demonstrated by looking at the long time solution of the pendulum:

+

Thus, for the sampling-based approaches, we simply need to arrive at an array which is sampled according to the distribution that we want to estimate, and from that array we can recover the distribution.

Sampling Distributions with the Metropolis-Hastings Algorithm

The Metropolis-Hastings algorithm is the simplest form of Markov Chain Monte Carlo (MCMC) which gives a way of sampling the $\theta$ distribution. To see how this algorithm works, let's understand the ratio between two points in the posterior probability. If we have $x_i$ and $x_j$, the ratio of the two probabilities would be given by:

\[ \frac{p(x_i|D)}{p(x_j|D)} = \frac{p(D|x_i)p(x_i)}{p(D|x_j)p(x_j)} \]

(notice that the integration constant cancels). This motivates the idea that all we have to do is ensure we only go to a point $x_j$ from $x_i$ with probability difference that matches that ratio, and over time if we do this between "all points" we will have the right number of "each point" in the distribution (quotes because it's continuous). With a bit more rigour we arrive at the following algorithm:

  1. Starting at $x_i$, take $x_{i+1}$ from a sampling algorithm $g(x_{i+1}|x_i)$.

  2. Calculate $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})g(x_i|x_{i+1})}{p(D|x_i)p(x_i)g(x_{i+1}|x_i)}\right)$. Notice that if we require $g$ to be symmetric, then this simplifies to the probability ratio $A = \min\left(1,\frac{p(D|x_{i+1})p(x_{i+1})}{p(D|x_i)p(x_i)}\right)$

  3. Use a random number to accept the step with a probability $A$. Go back to step 1, incrementing $i$ if accepted, otherwise just repeat.

I.e, we just walk around the space biasing the acceptance of a step by the factor $\frac{p(x_i|D)}{p(x_j|D)}$ and sooner or later we will have spent the right amount of time in each area, giving the correct distribution.

(This can be rigorously proven, and those details are left out.)

The Cost of Bayesian Estimation

Let's take a quick moment to understand the high cost of Bayesian posterior estimations. While before we were getting point estimates, now we are trying to recover a full probability distribution, and each accept/reject probability calculation requires evaluating the likelihood at some point. Remember, the likelihood is generated by our simulator, and thus every evaluation here is an ODE solver call or a neural network forward pass! This means that to get good distributions, we are solving the ODE hundreds of thousands of times, i.e. even more than when doing parameter estimation! This is something to keep in mind.

However, notice that this process is trivially parallelizable. We can just have parallel chains on going, i.e. start 16 processes all doing Metropolis-Hastings, and in the end they are all sampling from the same distribution, so the final array can simply be the pooled results of each chain.

Hamiltonian Monte Carlo

Metropolis-Hastings is easy to motivate and implement. However, it does not do well in high dimensional spaces because it searches in all directions. For example, it's common for the sampling distribution $g$ to be a multivariable distribution (i.e. normal in all directions). However, high dimensional objects commonly sit on low dimensional manifolds (known as the manifold hypothesis). If that's the case, the most probable set of parameters is something that is low dimensional. For example, parameters may compensate for one another, and so $\theta_1^2 + \theta_2^2 + \theta_3^2 = 1$ might be the manifold on which all of the most probable choices for $\theta$ lie, in which case we need sample on the sphere instead of all of $\mathbb{R}^3$.

However, it's quick to see that this will give Metropolis-Hastings some trouble, since it will use a normal distribution around the current point, and thus even if we start on the sphere, it will have a high chance of trying a point not on the sphere in the next round! This can be depicted as:

Recall that every single rejection is still evaluating the likelihood (since it's calculating an acceptance probability, finding it near zero, rejecting and starting again), and every likelihood call is calling our simulator, and so this is sllllllooooooooooooooowwwwwwwww in high dimensions!

What we need to do instead is ensure that we walk along the path of high probability. What we want to do is thus build a vector field that matches our high probability regions

and follow said vector field (following a vector field is solving what kind of equation?). The first idea one might have is to use the gradient. However, while this idea has the right intentions, the issue is that the gradient of the probability will average out all of the possible probabilities, and will thus flow towards the mode of the distribution:

To overcome this issue, we look to physical systems and see that a satellite orbiting a planet always nicely stays on some manifold instead of following the gradient:

The reason why it does is because it has momentum. Recall from basic physics that one way to describe a physical system is through Hamiltonian mechanics, where $H(x,p)$ is the energy associated with the state $(x,p)$ (normally $x$ is location and $p$ is momentum). Due to conservation of energy, the solution of the dynamical equations leads to $H(x,p)$ being constant, and thus the dynamics follow the level sets of $H$. From the Hamiltonian the dynamics of the system are:

\[ \begin{align} \frac{dx}{dt} &= \frac{dH}{dp}\\ &= -\frac{dH}{dx} \end{align} \]

Here we want our Hamiltonian to be our posterior probability, so that way we stay on the manifold of high probability. This means:

\[ H(x,p) = - \log \pi(x,p) \]

where $\pi(x,p) = \pi(p|x)\pi(x)$ (where I am now using $pi$ for probability since $p$ is momentum!). So to lift from a probability over parameters to one that includes momentum, we simply need to choose a conditional distribution $\pi(p|x)$. This would mean that

\[ \begin{align} H(x,p) &= -log \pi(p|x) - \log \pi(x)\\ &= K(p,x) + V(x) \end{align} \]

where $K$ is the kinetic energy and $V$ is the potential. Thus the potential energy is directly given by the posterior calculation, and the kinetic energy is thus a choice that is used to build the correct Hamiltonian. Hamiltonian Monte Carlo methods then dig into good ways to choose the kinetic energy function. This is done at the start (along with the choice of ODE solver time step) in such a way that it maximizes acceptance probabilities.

Connections to Differentiable Programming

\[ -\frac{dH}{dx} \]

requires calculating the gradient of the likelihood function with respect to the parameters, so we are once again using the gradient of our simulator! This means that all of our previous discussion on automatic differentiation and differentiable programming applies to the Hamiltonian Monte Carlo context.

There's another thread to follow that transformations of probability distributions are pushforwards of the Jacobian transformations (given the transformation of an integral formula), and this is used when doing variational inference.

Symplectic and Geometric Integration

One way to integrate the system of ODEs which result from the Hamiltonian system is to convert it to a system of first order ODEs and solve it directly. However, this loses information and can result in drift. This is demonstrated by looking at the long time solution of the pendulum:

 using ParameterizedFunctions
 u0 = [1.,0.]
 harmonic! = @ode_def HarmonicOscillator begin
diff --git a/notes/17-Global_Sensitivity_Analysis/index.html b/notes/17-Global_Sensitivity_Analysis/index.html
index 4bbd6875..c5f5d4e9 100644
--- a/notes/17-Global_Sensitivity_Analysis/index.html
+++ b/notes/17-Global_Sensitivity_Analysis/index.html
@@ -9,4 +9,4 @@
 using LatinHypercubeSampling
 p = LHCoptim(120,2,1000)
 scatter(p[1][:,1],p[1][:,2])
-

For a reference library with many different quasi-Monte Carlo samplers, check out QuasiMonteCarlo.jl.

Fourier Amplitude Sensitivity Sampling (FAST) and eFAST

The FAST method is a change to the Sobol method to allow for faster convergence. First transform the variables $x_i$ onto the space $[0,1]$. Then, instead of the linear decomposition, one decomposes into a Fourier basis:

\[ f(x_i,x_2,\ldots,x_n) = \sum_{m_1 = -\infty}^{\infty} \ldots \sum_{m_n = -\infty}^{\infty} C_{m_1m_2\ldots m_n}\exp\left(2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

where

\[ C_{m_1m_2\ldots m_n} = \int_0^1 \ldots \int_0^1 f(x_i,x_2,\ldots,x_n) \exp\left(-2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

The ANOVA like decomposition is thus

\[ f_0 = C_{0\ldots 0} \]

\[ f_j = \sum_{m_j \neq 0} C_{0\ldots 0 m_j 0 \ldots 0} \exp (2\pi i m_j x_j) \]

\[ f_{jk} = \sum_{m_j \neq 0} \sum_{m_k \neq 0} C_{0\ldots 0 m_j 0 \ldots m_k 0 \ldots 0} \exp \left(2\pi i (m_j x_j + m_k x_k)\right) \]

The first order conditional variance is thus:

\[ V_j = \int_0^1 f_j^2 (x_j) dx_j = \sum_{m_j \neq 0} |C_{0\ldots 0 m_j 0 \ldots 0}|^2 \]

or

\[ V_j = 2\sum_{m_j = 1}^\infty \left(A_{m_j}^2 + B_{m_j}^2 \right) \]

where $C_{0\ldots 0 m_j 0 \ldots 0} = A_{m_j} + i B_{m_j}$. By Fourier series we know this to be:

\[ A_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\cos(2\pi m_j x_j)dx \]

\[ B_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\sin(2\pi m_j x_j)dx \]

Implementation via the Ergodic Theorem

Define

\[ X_j(s) = \frac{1}{2\pi} (\omega_j s \mod 2\pi) \]

By the ergodic theorem, if $\omega_j$ are irrational numbers, then the dynamical system will never repeat values and thus it will create a solution that is dense in the plane (Let's prove a bit later). As an animation:

(here, $\omega_1 = \pi$ and $\omega_2 = 7$)

This means that:

\[ A_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\sin(m_j \omega_j s)ds \]

i.e. the multidimensional integral can be approximated by the integral over a single line.

One can satisfy this approximately to get a simpler form for the integral. Using $\omega_i$ as integers, the integral is periodic and so only integrating over $2\pi$ is required. This would mean that:

\[ A_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\sin(m_j \omega_j s)ds \]

It's only approximate since the sequence cannot be dense. For example, with $\omega_1 = 11$ and $\omega_2 = 7$:

A higher period thus gives a better fill of the space and thus a better approximation, but may require a more points. However, this transformation makes the true integrals simple one dimensional quadratures which can be efficiently computed.

To get the total index from this method, one can calculate the total contribution of the complementary set, i.e. $V_{c_i} = \sum_{j \neq i} V_j$ and then

\[ S_{T_i} = 1 - S_{c_i} \]

Note that this then is a fast measure for the total contribution of variable $i$, including all higher-order nonlinear interactions, all from one-dimensional integrals! (This extension is called extended FAST or eFAST)

Proof of the Ergodic Theorem

Look at the map $x_{n+1} = x_n + \alpha (\text{mod} 1)$, where $\alpha$ is irrational. This is the irrational rotation map that corresponds to our problem. We wish to prove that in any interval $I$, there is a point of our orbit in this interval.

First let's prove a useful result: our points get arbitrarily close. Assume that for some finite $\epsilon$ that no two points are $\epsilon$ apart. This means that we at most have spacings of $\epsilon$ between the points, and thus we have at most $\frac{2\pi}{\epsilon}$ points (rounded up). This means our orbit is periodic. This means that there is a $p$ such that

\[ x_{n+p} = x_n \]

which means that $p \alpha = 1$ or $p = \frac{1}{\alpha}$ which is a contradiction since $\alpha$ is irrational.

Thus for every $\epsilon$ there are two points which are $\epsilon$ apart. Now take any arbitrary $I$. Let $\epsilon < d/2$ where $d$ is the length of the interval. We have just shown that there are two points $\epsilon$ apart, so there is a point that is $x_{n+m}$ and $x_{n+k}$ which are $<\epsilon$ apart. Assuming WLOG $m>k$, this means that $m-k$ rotations takes one from $x_{n+k}$ to $x_{n+m}$, and so $m-k$ rotations is a rotation by $\epsilon$. If we do $\frac{1}{\epsilon}$ rounded up rotations, we will then cover the space with intervals of length epsilon, each with one point of the orbit in it. Since $\epsilon < d/2$, one of those intervals is completely encapsulated in $I$, which means there is at least one point in our orbit that is in $I$.

Thus for every interval we have at least one point in our orbit that lies in it, proving that the rotation map with irrational $\alpha$ is dense. Note that during the proof we essentially showed as well that if $\alpha$ is rational, then the map is periodic based on the denominator of the map in its reduced form.

A Quick Note on Parallelism

Very quick note: all of these are hyper parallel since it does the same calculation per parameter or trajectory, and each calculation is long. For quasi-Monte Carlo, after generating "good enough" trajectories, one can evaluate the model at all points in parallel, and then simply do the GSA index measurement. For FAST, one can do each quadrature in parallel.

\ No newline at end of file +

For a reference library with many different quasi-Monte Carlo samplers, check out QuasiMonteCarlo.jl.

Fourier Amplitude Sensitivity Sampling (FAST) and eFAST

The FAST method is a change to the Sobol method to allow for faster convergence. First transform the variables $x_i$ onto the space $[0,1]$. Then, instead of the linear decomposition, one decomposes into a Fourier basis:

\[ f(x_i,x_2,\ldots,x_n) = \sum_{m_1 = -\infty}^{\infty} \ldots \sum_{m_n = -\infty}^{\infty} C_{m_1m_2\ldots m_n}\exp\left(2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

where

\[ C_{m_1m_2\ldots m_n} = \int_0^1 \ldots \int_0^1 f(x_i,x_2,\ldots,x_n) \exp\left(-2\pi i (m_1 x_1 + \ldots + m_n x_n)\right) \]

The ANOVA like decomposition is thus

\[ f_0 = C_{0\ldots 0} \]

\[ f_j = \sum_{m_j \neq 0} C_{0\ldots 0 m_j 0 \ldots 0} \exp (2\pi i m_j x_j) \]

\[ f_{jk} = \sum_{m_j \neq 0} \sum_{m_k \neq 0} C_{0\ldots 0 m_j 0 \ldots m_k 0 \ldots 0} \exp \left(2\pi i (m_j x_j + m_k x_k)\right) \]

The first order conditional variance is thus:

\[ V_j = \int_0^1 f_j^2 (x_j) dx_j = \sum_{m_j \neq 0} |C_{0\ldots 0 m_j 0 \ldots 0}|^2 \]

or

\[ V_j = 2\sum_{m_j = 1}^\infty \left(A_{m_j}^2 + B_{m_j}^2 \right) \]

where $C_{0\ldots 0 m_j 0 \ldots 0} = A_{m_j} + i B_{m_j}$. By Fourier series we know this to be:

\[ A_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\cos(2\pi m_j x_j)dx \]

\[ B_{m_j} = \int_0^1 \ldots \int_0^1 f(x)\sin(2\pi m_j x_j)dx \]

Implementation via the Ergodic Theorem

Define

\[ X_j(s) = \frac{1}{2\pi} (\omega_j s \mod 2\pi) \]

By the ergodic theorem, if $\omega_j$ are irrational numbers, then the dynamical system will never repeat values and thus it will create a solution that is dense in the plane (Let's prove a bit later). As an animation:

(here, $\omega_1 = \pi$ and $\omega_2 = 7$)

This means that:

\[ A_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} = \lim_{T\rightarrow \infty} \frac{1}{2T} \int_{-T}^T f(x)\sin(m_j \omega_j s)ds \]

i.e. the multidimensional integral can be approximated by the integral over a single line.

One can satisfy this approximately to get a simpler form for the integral. Using $\omega_i$ as integers, the integral is periodic and so only integrating over $2\pi$ is required. This would mean that:

\[ A_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\cos(m_j \omega_j s)ds \]

\[ B_{m_j} \approx \frac{1}{2\pi} \int_{-\pi}^\pi f(x)\sin(m_j \omega_j s)ds \]

It's only approximate since the sequence cannot be dense. For example, with $\omega_1 = 11$ and $\omega_2 = 7$:

A higher period thus gives a better fill of the space and thus a better approximation, but may require a more points. However, this transformation makes the true integrals simple one dimensional quadratures which can be efficiently computed.

To get the total index from this method, one can calculate the total contribution of the complementary set, i.e. $V_{c_i} = \sum_{j \neq i} V_j$ and then

\[ S_{T_i} = 1 - S_{c_i} \]

Note that this then is a fast measure for the total contribution of variable $i$, including all higher-order nonlinear interactions, all from one-dimensional integrals! (This extension is called extended FAST or eFAST)

Proof of the Ergodic Theorem

Look at the map $x_{n+1} = x_n + \alpha (\text{mod} 1)$, where $\alpha$ is irrational. This is the irrational rotation map that corresponds to our problem. We wish to prove that in any interval $I$, there is a point of our orbit in this interval.

First let's prove a useful result: our points get arbitrarily close. Assume that for some finite $\epsilon$ that no two points are $\epsilon$ apart. This means that we at most have spacings of $\epsilon$ between the points, and thus we have at most $\frac{2\pi}{\epsilon}$ points (rounded up). This means our orbit is periodic. This means that there is a $p$ such that

\[ x_{n+p} = x_n \]

which means that $p \alpha = 1$ or $p = \frac{1}{\alpha}$ which is a contradiction since $\alpha$ is irrational.

Thus for every $\epsilon$ there are two points which are $\epsilon$ apart. Now take any arbitrary $I$. Let $\epsilon < d/2$ where $d$ is the length of the interval. We have just shown that there are two points $\epsilon$ apart, so there is a point that is $x_{n+m}$ and $x_{n+k}$ which are $<\epsilon$ apart. Assuming WLOG $m>k$, this means that $m-k$ rotations takes one from $x_{n+k}$ to $x_{n+m}$, and so $m-k$ rotations is a rotation by $\epsilon$. If we do $\frac{1}{\epsilon}$ rounded up rotations, we will then cover the space with intervals of length epsilon, each with one point of the orbit in it. Since $\epsilon < d/2$, one of those intervals is completely encapsulated in $I$, which means there is at least one point in our orbit that is in $I$.

Thus for every interval we have at least one point in our orbit that lies in it, proving that the rotation map with irrational $\alpha$ is dense. Note that during the proof we essentially showed as well that if $\alpha$ is rational, then the map is periodic based on the denominator of the map in its reduced form.

A Quick Note on Parallelism

Very quick note: all of these are hyper parallel since it does the same calculation per parameter or trajectory, and each calculation is long. For quasi-Monte Carlo, after generating "good enough" trajectories, one can evaluate the model at all points in parallel, and then simply do the GSA index measurement. For FAST, one can do each quadrature in parallel.

\ No newline at end of file