From 86ce76ef3b5eefe752ed4dbb12047361adf433be Mon Sep 17 00:00:00 2001 From: masenka31 Date: Tue, 16 Sep 2025 18:30:00 +0200 Subject: [PATCH 01/11] a bunch of corrected typos and clarifications --- docs/src/lecture_01/arrays.md | 2 +- docs/src/lecture_02/conditions.md | 8 ++++---- docs/src/lecture_02/exercises.md | 8 ++++---- docs/src/lecture_02/functions.md | 6 +++--- docs/src/lecture_02/loops.md | 4 ++-- docs/src/lecture_02/scope.md | 4 ++-- docs/src/lecture_03/DataFrames.md | 10 +++++----- docs/src/lecture_04/exceptions.md | 8 ++++---- docs/src/lecture_04/exercises.md | 4 ++-- docs/src/lecture_04/functions.md | 4 ++-- docs/src/lecture_04/methods.md | 8 ++++---- docs/src/lecture_04/scope.md | 4 ++-- docs/src/lecture_05/compositetypes.md | 12 ++++++------ docs/src/lecture_05/currencies.md | 10 +++++----- docs/src/lecture_06/compatibility.md | 20 ++++++++++---------- docs/src/lecture_06/modules.md | 4 ++-- docs/src/lecture_06/structure.md | 12 ++++++------ docs/src/lecture_06/workflow.md | 16 ++++++++-------- docs/src/lecture_07/documentation.md | 12 ++++++------ docs/src/lecture_07/extensions.md | 12 ++++++------ docs/src/lecture_07/tests.md | 16 ++++++++-------- docs/src/lecture_08/constrained.md | 4 ++-- docs/src/lecture_08/exercises.md | 6 +++--- docs/src/lecture_08/gradients.md | 8 ++++---- docs/src/lecture_08/theory.md | 4 ++-- docs/src/lecture_08/unconstrained.md | 10 +++++----- docs/src/lecture_09/exercises.md | 6 +++--- docs/src/lecture_09/linear.md | 8 ++++---- docs/src/lecture_09/logistic.md | 6 +++--- docs/src/lecture_09/theory.md | 14 +++++++------- docs/src/lecture_10/exercises.md | 2 +- docs/src/lecture_10/nn.md | 6 +++--- docs/src/lecture_10/theory.md | 8 ++++---- docs/src/lecture_11/nn.md | 6 +++--- docs/src/lecture_11/theory.md | 6 +++--- 35 files changed, 139 insertions(+), 139 deletions(-) diff --git a/docs/src/lecture_01/arrays.md b/docs/src/lecture_01/arrays.md index 3f8b838b7..5d5c0ebf1 100644 --- a/docs/src/lecture_01/arrays.md +++ b/docs/src/lecture_01/arrays.md @@ -322,7 +322,7 @@ Accessing matrix elements can be also done in the same way as for vectors. julia> M[1] # the first element, equivalent to m[begin] 1 -julia> M[2] # the the second element element +julia> M[2] # the second element 5 julia> M[end-1] # the second to last element diff --git a/docs/src/lecture_02/conditions.md b/docs/src/lecture_02/conditions.md index 666406ee7..762e4550e 100644 --- a/docs/src/lecture_02/conditions.md +++ b/docs/src/lecture_02/conditions.md @@ -1,4 +1,4 @@ -# Conditional evalutions +# Conditional evaluations This lecture handles control flow. The first part focuses on `if` conditions and the second one of loops. @@ -22,7 +22,7 @@ end compare (generic function with 1 method) ``` -If the expression `x < y` is true, the functions prints *"x is less than y"*, otherwise, the expression `x > y` is evaluated, and if it is true, the functions prints *"x is greater than y"*. If neither expression is true, the function prints the remaining option *"x is equal to y"*. +If the expression `x < y` is true, the function prints *"x is less than y"*, otherwise, the expression `x > y` is evaluated, and if it is true, the function prints *"x is greater than y"*. If neither expression is true, the function prints the remaining option *"x is equal to y"*. ```jldoctest conditions julia> compare(1, 2.3) @@ -150,7 +150,7 @@ julia> compare(2.3, 2.3) Make sure that the input argument is a non-negative integer. For negative input arguments and for arguments that can not be represented as an integer, the function should throw an error. - **Hint:** use recursion, the `isinteger` function and the `error` function. The or operator is written by `|`. + **Hint:** use recursion, the `isinteger` function and the `error` function. The or operator is written as `|`. !!! details "Solution:" We split the solution into three cases: @@ -215,7 +215,7 @@ Since we wrapped the whole expression into the `println` function, the ternary o ## Short-circuit evaluation -Julia provides the so-called short-circuit evaluation which is similar to the conditional evaluation. The behaviour exists in most imperative programming languages having the `&&` and `||` boolean operators. In a series of boolean expressions connected by these operators, only the minimal number of expressions is evaluated to determine the final boolean value of the entire chain: +Julia provides the so-called short-circuit evaluation which is similar to the conditional evaluation. The behaviour exists in most imperative programming languages, which have the `&&` and `||` boolean operators. In a series of boolean expressions connected by these operators, only the minimal number of expressions is evaluated to determine the final boolean value of the entire chain: - In the expression `a && b`, the subexpression `b` is only evaluated if `a` evaluates true. - In the expression `a || b`, the subexpression `b` is only evaluated if `a` evaluates to false. diff --git a/docs/src/lecture_02/exercises.md b/docs/src/lecture_02/exercises.md index 6cfa2a958..55ba22c95 100644 --- a/docs/src/lecture_02/exercises.md +++ b/docs/src/lecture_02/exercises.md @@ -12,8 +12,8 @@ We need to use the `using` keyword to load the package. For example, we can use using Plots x = 0:0.01π:2π -plot(x, sin.(x); label = "sinus", linewidth = 2) -plot!(x, cos.(x); label = "cosinus", linewidth = 2) +plot(x, sin.(x); label = "sine", linewidth = 2) +plot!(x, cos.(x); label = "cosine", linewidth = 2) savefig("sin.svg") # hide ``` @@ -140,7 +140,7 @@ There will be a whole [section](@ref Plots.jl) dedicated to the Plots package. H end ``` - We use the ternary operator to decide which value is returned. Now we need to define all input parameters as in the previous exercise. + We use the ternary operator `? :` to decide which value is returned. Now we need to define all input parameters as in the previous exercise. ```julia c = - 0.4 + 0.61im @@ -213,7 +213,7 @@ Firstly, we create the vector of all values `c` by combining the `range` functio cs = 0.7885 .* exp.(range(π/2, 3π/2; length = 500) .* im) ``` -Note that we use the `length` keyword to specify the length of `cs`. To create an animation, it suffices to use the `for` loop in combination with the `@animate` macro. +Note that we use the `length` keyword to specify the length of `cs`. To create an animation, it is sufficient to use the `for` loop in combination with the `@animate` macro. ```julia anim = @animate for c in cs diff --git a/docs/src/lecture_02/functions.md b/docs/src/lecture_02/functions.md index 7888bc7c4..3c9a93ce6 100644 --- a/docs/src/lecture_02/functions.md +++ b/docs/src/lecture_02/functions.md @@ -1,6 +1,6 @@ ## Function basics -So far, we did not show how to define functions. In Julia, a function is an object that maps a tuple of argument values to a return value. There are multiple ways to create a function. Each of them is useful in different situations. The following example shows the basic way how to define a function using `function ... end` syntax +So far, we did not show how to define functions. In Julia, a function is an object that maps a tuple of argument values to a return value. There are multiple ways to create a function. Each of them is useful in different situations. The following example shows the basic way to define a function using `function ... end` syntax ```jldoctest functions; output = false function quadratic(x::Real; a::Real = 1, b::Real = 1, c::Real = 1) @@ -21,7 +21,7 @@ The function definition consists of multiple parts: - positional argument `x` with type annotation - separator `;` of positional and keyword arguments - keyword arguments `a`, `b` and `c` with type annotations and default values -- actual code that computed the output values +- actual code that computes the output values - `return` keyword that specifies the function output followed by comma separated list of output values Note that not all parts of the function definition above are mandatory. The following parts are optional: @@ -62,7 +62,7 @@ However, we highly recommend to use these optional features when writing your ow @ none:1 ``` - This is different to a a case when we would define the function without the type annotation: + This is different to a case when we would define the function without the type annotation: ```jldoctest functions; output = false function quadratic2(x; a = 1, b = 1, c = 1) diff --git a/docs/src/lecture_02/loops.md b/docs/src/lecture_02/loops.md index 745d0fa59..0e1f808fd 100644 --- a/docs/src/lecture_02/loops.md +++ b/docs/src/lecture_02/loops.md @@ -1,6 +1,6 @@ # Loops -While `if` conditions are evaluated only once, loops are assessed multiple times. +While `if` conditions are evaluated only once, loops are executed multiple times. ## `for` and `while` loops @@ -145,7 +145,7 @@ Hi, my name is Bob and I am 23 old. i = 84 ``` - The `for` loop should be used here because the range is known before-hand and unlike the `while` loop, it does not require to initialize `i`. + The `for` loop should be used here because the range is known beforehand and unlike the `while` loop, it does not require to initialize `i`. ### `break` and `continue` diff --git a/docs/src/lecture_02/scope.md b/docs/src/lecture_02/scope.md index a9d46d63e..f495b47f0 100644 --- a/docs/src/lecture_02/scope.md +++ b/docs/src/lecture_02/scope.md @@ -1,6 +1,6 @@ # Soft local scope -The scope of a variable is the region of a code where the variable is visible. There are two main types of scopes in Julia: **global** and **local**, and we will discuss it [later](@ref Scope-of-variables). In this section, we will only focus on loops. +The scope of a variable is the region of a code where the variable is visible. There are two main types of scopes in Julia: **global** and **local**, and we will discuss them [later](@ref Scope-of-variables). In this section, we will only focus on loops. Every variable created inside a loop is local, i.e., it is possible to use it only inside the loop. @@ -33,7 +33,7 @@ ERROR: UndefVarError: `i` not defined Variable `j` is a local variable defined in the outer loop. This means that it is visible inside the inner loop and can be used there. On the other hand, variable `i` is a local variable from the inner loop and cannot be accessed in the outer loop. -What happens if use variables from the global scope inside loops? In this case, it depends whether the loop is created in *interactive* (REPL, Jupyter notebook) or *non-interactive* context (file, eval). In the interactive case (in the REPL in our case), global variables can be accessed and modified in local scopes without any restrictions. +What happens if we use variables from the global scope inside loops? In this case, it depends whether the loop is created in *interactive* (REPL, Jupyter notebook) or *non-interactive* context (file, eval). In the interactive case (in the REPL in our case), global variables can be accessed and modified in local scopes without any restrictions. ```jldoctest julia> s = 0 diff --git a/docs/src/lecture_03/DataFrames.md b/docs/src/lecture_03/DataFrames.md index de35c1065..96ec6b645 100644 --- a/docs/src/lecture_03/DataFrames.md +++ b/docs/src/lecture_03/DataFrames.md @@ -14,7 +14,7 @@ using DataFrames df = DataFrame(A = 1:4, B = ["M", "F", "F", "M"], C = rand(4)) ``` -Since each column is stored in a `DataFrame` as a separate vector, it is possible to combine columns of different element types. Columns can be accessed directly without copying. +Since each column is stored in a `DataFrame` as a separate vector, it is possible to combine columns of different element types. Columns can be accessed directly, without copying. ```@repl dfbasics df.A @@ -33,7 +33,7 @@ df.A[1] = 5 df ``` -On the other hand, the `:` creates a copy, which will not change the original `DataFrame`. +On the other hand, the `:` creates a copy, therefore changes will not affect the original `DataFrame`. ```@example dfbasics col = df[:, :A] @@ -56,7 +56,7 @@ See the package [documentation](https://csv.juliadata.org/stable/) for more info ## Adding columns and rows -It is common for tables to be created column by column or row by row. `DataFrame`s provides an easy way to extend existing tables. To can add new columns to a `DataFrame` in a direct way. +It is common for tables to be created column by column or row by row. `DataFrame`s provides an easy way to extend existing tables. You can add new columns to a `DataFrame` in a direct way. ```@example dfbasics df.D = [:a, :b, :c, :d] @@ -100,7 +100,7 @@ df_empty ## Renaming -Two functions can be used to rename columns. The `names` function returns column names as a vector of strings, while the `propertynames` function returns a vector of symbols. +There are two functions that can be used to rename columns. The `names` function returns column names as a vector of strings, while the `propertynames` function returns a vector of symbols. ```@repl dfbasics names(df) @@ -114,7 +114,7 @@ rename!(df, [:a, :b, :c, :d, :e, :f]) df ``` -Another option is to rename only some of the columns specified by their names. +Another option is to rename only some columns, specified by their names. ```@example dfbasics rename!(df, :a => :A, :f => :F) diff --git a/docs/src/lecture_04/exceptions.md b/docs/src/lecture_04/exceptions.md index ec2d326e8..7aae4be19 100644 --- a/docs/src/lecture_04/exceptions.md +++ b/docs/src/lecture_04/exceptions.md @@ -1,6 +1,6 @@ # Exception handling -Unexpected behaviour may often occur during running code, which may lead to the situation that some function cannot return a reasonable value. Such behaviour should be handled by either terminating the program with a proper diagnostic error message or allowing that code to take appropriate action. +Unexpected behaviour may often occur during running code, which may lead to a situation where some function cannot return a reasonable value. Such behaviour should be handled by either terminating the program with a proper diagnostic error message or allowing that code to take appropriate action. In the following example, we define a factorial function in the same way as we did in the [Short-circuit evaluation](@ref Short-circuit-evaluation) section. @@ -26,7 +26,7 @@ ERROR: argument must be non-negative integer [...] ``` -However, it is better to use error messages as descriptive as possible. In the case above, the error message can also include the argument value. Julia provides several predefined types of exceptions that can be used to create more descriptive error messages. In our example, we want to check whether the argument is a non-negative integer. The more specific `DomainError` can do this. +However, it is better to use error messages that are as descriptive as possible. In the case above, the error message can also include the argument value. Julia provides several predefined types of exceptions that can be used to create more descriptive error messages. In our example, we want to check whether the argument is a non-negative integer. The more specific `DomainError` can do this. ```jldoctest expections; output = false function fact(n) @@ -69,7 +69,7 @@ Stacktrace: In this case, the `MethodError` is raised for the `isinteger` function. Since the `DomainError` function is not even called, the error says nothing about the `fact` function. We can track that the error occurs when calling the `fact` function using the `Stacktrace` section located under the error message. The `Stacktrace` provides us with an ordered list of function calls (starting from the last one) that preceded the error. In this case, the last function call before the error is `fact(::String)`. It tells us that the error occurs in the function `fact` with a string as the input argument. In this particular case, it makes sense to define factorial function only for real numbers. This can be done by entering the input type in the function declaration. -```jldoctest expections; output = false +```jldoctest exceptions; output = false function fact_new(n::Real) isinteger(n) && n >= 0 || throw(DomainError(n, "argument must be non-negative integer")) return n == 0 ? 1 : n * fact(n - 1) @@ -79,7 +79,7 @@ end fact_new (generic function with 1 method) ``` -This function declaration will only work for subtypes of `Real`. Otherwise, `the MethodError` will occur. +This function declaration will only work for subtypes of `Real`. Otherwise, a `MethodError` will occur. ```jldoctest expections julia> fact_new("aaa") diff --git a/docs/src/lecture_04/exercises.md b/docs/src/lecture_04/exercises.md index b12392469..78848781d 100644 --- a/docs/src/lecture_04/exercises.md +++ b/docs/src/lecture_04/exercises.md @@ -44,7 +44,7 @@ The following few exercises will implement the Game of Life. We will consider fi end ``` - The approach above can not define a general version of the `neighbours` function. In this case, we can use nested loops. First, we compute proper row indices by `range` combined with the `mod1` function. + The approach above cannot define a general version of the `neighbours` function. In this case, we can use nested loops. First, we compute proper row indices by `range` combined with the `mod1` function. ```julia rows = mod1.(row .+ (-r:r), size(world, 1)) @@ -62,7 +62,7 @@ The following few exercises will implement the Game of Life. We will consider fi ``` !!! warning "Exercise:" - Add a new method to the `neighbours` function that for the `world` matrix returns a matrix containing numbers of living neighbours. + Add a new method to the `neighbours` function that, for the `world` matrix, returns a matrix containing numbers of living neighbours. !!! details "Solution:" We created a function that computes the number of living neighbours in the exercise above. One way how to create a matrix with numbers of living neighbours is: diff --git a/docs/src/lecture_04/functions.md b/docs/src/lecture_04/functions.md index 9b5fffd40..feb4900cb 100644 --- a/docs/src/lecture_04/functions.md +++ b/docs/src/lecture_04/functions.md @@ -543,7 +543,7 @@ julia> roundmod(12.529, 5; sigdigits = 2) 2.5 ``` -This construction is beneficial whenever there are multiple chained functions, and only the deepest ones need keyword arguments. +This construction is usefull whenever there are multiple chained functions, and only the innermost ones need keyword arguments. !!! warning "Exercise:" Write a function `wrapper`, that accepts a number and applies one of `round`, `ceil` or `floor` functions based on the keyword argument `type`. Use the function to solve the following tasks: @@ -738,7 +738,7 @@ julia> A 0.7071067811865476 ``` -Or by a list compherension. +Or by a list comprehension. ```jldoctest dot julia> A = [sin(xi) for xi in x] diff --git a/docs/src/lecture_04/methods.md b/docs/src/lecture_04/methods.md index 1582f8c13..d70d8ecc6 100644 --- a/docs/src/lecture_04/methods.md +++ b/docs/src/lecture_04/methods.md @@ -53,7 +53,7 @@ product(x, y) = throw(ArgumentError("product is defined for numbers only.")) product (generic function with 2 methods) ``` -The second line redefined the original definition of the `product` function. It now throws an error if `product` is called with non-numeric inputs. +The second line redefines the original definition of the `product` function. It now throws an error if `product` is called with non-numeric inputs. ```jldoctest methods julia> methods(product) @@ -64,7 +64,7 @@ julia> methods(product) @ none:1 ``` -Now, we have a function with two methods, that returns a product if the input arguments are numbers, and throws an error otherwise. +Now, we have a function with two methods that returns a product if the input arguments are numbers, and throws an error otherwise. ```jldoctest methods julia> product(1, 4.5) @@ -346,7 +346,7 @@ Closest candidates are: Here we get a different error. However, the error returned by the `product_new` function is more useful because it tells us what the real problem is. We can see that it is impossible to use the `*` operator to multiply a `String` and a `Symbol`. We can decide if this is the desired behaviour, and if not, we can define a method for the `*` operator that will fix it. -We show a simple example when the multiple dispatch is useful. +We show a simple example where multiple dispatch is useful. !!! warning "Exercise:" We define the abstract type `Student` and specific types `Master` and `Doctoral`. The latter two are defined as structures containing one and three fields, respectively. @@ -436,7 +436,7 @@ julia> f(2, 3.0) 5.0 ``` -Both methods can be used if both arguments are of type `Float64`. The problem is that neither method is more specific than the other. This results in `MethodError`. +Both methods could match if both arguments are of type `Float64`. The problem is that neither method is more specific than the other. This results in `MethodError`. ```jldoctest methods_amb julia> f(2.0, 3.0) diff --git a/docs/src/lecture_04/scope.md b/docs/src/lecture_04/scope.md index efbd12a1e..5b264babc 100644 --- a/docs/src/lecture_04/scope.md +++ b/docs/src/lecture_04/scope.md @@ -66,7 +66,7 @@ In the example above, the `z` variable in the function is local, and the `z` var ## Global scope -Each module introduces a new global scope, separate from the global scope of all other modules. The interactive prompt (aka REPL) is in the global scope of the module `Main`. +Each module introduces a new global scope, separate from the global scope of all other modules. The interactive prompt (also know as REPL) is in the global scope of the module `Main`. ```jldoctest global julia> module A @@ -112,7 +112,7 @@ julia> foo(1) 11 ``` -However, it is not recommended to use global variables in this way. The reason is that global variables can change their type and value at any time, and therefore they cannot be properly optimized by the compiler. We can see the performance drop in a simple test. +However, using global variables in this way is not recommended. The reason is that global variables can change their type and value at any time, and therefore they cannot be properly optimized by the compiler. We can see the performance drop in a simple test. ```@repl global_test x = rand(10); diff --git a/docs/src/lecture_05/compositetypes.md b/docs/src/lecture_05/compositetypes.md index 1976ce9bf..95c85dbf3 100644 --- a/docs/src/lecture_05/compositetypes.md +++ b/docs/src/lecture_05/compositetypes.md @@ -4,7 +4,7 @@ Julia does not allow abstract types to be instantiated. They can only be used to ![](types.svg) -All types depicted in blue are abstract types, and all green types are concrete types. For example, `Int8`, `Int16`, `Int32`, `Int64` and `Int128` are signed integer types, `UInt8`, `UInt16`, `UInt32`, `UInt64` and `UInt128` are unsigned integer types, while `Float16`, `Float32` and `Float64` are floating-point types. In many cases, the inputs must be of a specific type. An algorithm to find the greatest common denominator should work any integer types, but it should not work for any floating-point inputs. Abstract types specify these cases and provide a context into which concrete types can fit. +All types depicted in blue are abstract types, and all green types are concrete types. For example, `Int8`, `Int16`, `Int32`, `Int64` and `Int128` are signed integer types, `UInt8`, `UInt16`, `UInt32`, `UInt64` and `UInt128` are unsigned integer types, while `Float16`, `Float32` and `Float64` are floating-point types. In many cases, the inputs must be of a specific type. An algorithm to find the greatest common denominator should work for any integer types, but it should not work for any floating-point inputs. Abstract types specify these cases and provide a context into which concrete types can fit. Abstract types are defined by `abstract type` followed by the type name. It is possible to specify a type to be a subtype of another abstract type. The definition of abstract numeric types would be: @@ -176,12 +176,12 @@ julia> fieldnames(typeof(r)) Out[4]: [[1.0, 2.0], [4.0, 2.0], [4.0, 6.0], [1.0, 6.0]] ``` - The declaration of the `Rectangle` class is very similar to the one in Julia. The main difference is, that in Python methods are bounded to the class, while Julia defines functions outside of the composite types. This is very important since Julia uses multiple-dispatch. It means, that functions consist of methods, and Julia decides which method to use based on the number of input arguments and its types. Since all arguments are used for method selection, it would be inappropriate for functions to "belong" to some composite type. As a consequence, we can modify existing methods or add new ones without the necessity to change the composite type definition. This property significantly improves code extensibility and reusability. + The declaration of the `Rectangle` class is very similar to the one in Julia. The main difference is, that in Python methods are bound to the class, while Julia defines functions outside of the composite types. This is very important since Julia uses multiple-dispatch. It means that functions consist of methods, and Julia decides which method to use based on the number of input arguments and its types. Since all arguments are used for method selection, it would be inappropriate for functions to "belong" to some composite type. As a consequence, we can modify existing methods or add new ones without the necessity to change the composite type definition. This property significantly improves code extensibility and reusability. ## Mutable composite types -Composite types declared with `struct` keyword are immutable and cannot be modified after being constructed. +Composite types declared with the `struct` keyword are immutable and cannot be modified after being constructed. ```jldoctest structs julia> r.bottomleft = [2;2] @@ -234,7 +234,7 @@ julia> isa(mr, MutableRectangle) true ``` -Similarly to accessing field values, we can change them by the dot notation or the `setproperty!` function. +Similarly to accessing field values, we can change them using the dot notation or the `setproperty!` function. ```jldoctest structs julia> mr.width = 1.5 @@ -366,7 +366,7 @@ julia> Point(0.2, 1.3) (0.2, 1.3) ``` -There are two ways how to instantiate the `Point` type. The first one does not specify the `T` parameter and lets Julia automatically decide the appropriate type. The second one specifies the `T` parameter manually. +There are two ways to instantiate the `Point` type. The first one does not specify the `T` parameter and lets Julia automatically decide the appropriate type. The second one specifies the `T` parameter manually. ```jldoctest structs julia> Point(1, 2) @@ -614,7 +614,7 @@ MyType(5, 4.5, "hello") Verify that the probability density function is defined correctly, i.e., its integral equals 1. !!! details "Solution:" - One possible way to define this structure is the `@kwdef` macro, where we specify the default parameters. We also define an inner constructor that promotes the inputs to a same type, and checks if the variance is positive. + One possible way to define this structure is the `@kwdef` macro, where we specify the default parameters. We also define an inner constructor that promotes the inputs to the same type, and checks if the variance is positive. ```jldoctest structs_gauss; output = false Base.@kwdef struct Gauss{T<:Real} diff --git a/docs/src/lecture_05/currencies.md b/docs/src/lecture_05/currencies.md index 3828c7f2c..4d3c85735 100644 --- a/docs/src/lecture_05/currencies.md +++ b/docs/src/lecture_05/currencies.md @@ -84,7 +84,7 @@ ERROR: MethodError: Cannot `convert` an object of type Dollar to an object of ty [...] ``` -We used only the abstract type `Currency` to define the `BankAccount` type. This allows us to write a generic code that not constrained to one concrete type. We created an instance of `BankAccount` and added a new transaction. However, we cannot calculate an account balance (the sum of all transactions), and we cannot convert money from one currency to another. In the rest of the lecture, we will fix this, and we will also define basic arithmetic operations such as `+` or `-`. +We used only the abstract type `Currency` to define the `BankAccount` type. This allows us to write generic code that is not constrained to one concrete type. We created an instance of `BankAccount` and added a new transaction. However, we cannot calculate an account balance (the sum of all transactions), and we cannot convert money from one currency to another. In the rest of the lecture, we will fix this, and we will also define basic arithmetic operations such as `+` or `-`. !!! info "Avoid containers with abstract type parameters:" It is generally not good to use [containers with abstract element type](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-abstract-container) as we did for storing transactions. We used it in the example above because we do not want to convert all transactions to a common currency. When we create an array from different types, the promotion system converts these types to their smallest supertype for efficient memory storage. @@ -149,7 +149,7 @@ julia> Euro(1.5) 1.5 € ``` -There is one big difference with Python, where we can create a class and define methods inside the class. If we wanted to add a new method, we have to would modify the class. In Julia, we can add or alter methods any time without the necessity to change the class. +There is one big difference compared to Python, where we can create a class and define methods inside the class. If we wanted to add a new method, we would have to modify the class. In Julia, we can add or alter methods any time without the necessity to change the class. !!! warning "Exercise:" Define a new method for the `symbol` function for `Dollar`. @@ -392,7 +392,7 @@ julia> dlr = convert(Dollar, pnd) 1.3 $ ``` - We realize that the rounding is done only for printing, while the original value remains unchanged. + Note that the rounding is done only for printing, while the original value remains unchanged. ## Promotion @@ -548,7 +548,7 @@ ERROR: MethodError: no method matching length(::Main.Dollar) [...] ``` -The reason is that Julia assumes that custom structures are iterable. But in our case, all subtypes of the `Currency` type represent scalar values. This situation can be easily fixed by defining a new method to the `broadcastable` function from `Base`. +The reason is that Julia assumes custom structures are iterable., but in our case, all subtypes of the `Currency` type represent scalar values. This situation can be easily fixed by defining a new method to the `broadcastable` function from `Base`. ```jldoctest currency; output=false Base.broadcastable(c::Currency) = Ref(c) @@ -802,7 +802,7 @@ Bank Account: - Number of transactions: 1 ``` -The last function that we define is the function that adds a new transaction into the given bank account. Even though it can be defined like any other function, we decided to use a special syntax. Since methods are associated with types, making any arbitrary Julia object "callable" is possible by adding methods to its type. Such "callable" objects are sometimes called "functors". +The last function we define is the function that adds a new transaction to the given bank account. Even though it can be defined like any other function, we decided to use a special syntax. Since methods are associated with types, making any arbitrary Julia object "callable" is possible by adding methods to its type. Such "callable" objects are sometimes called "functors". ```jldoctest currency; output=false function (b::BankAccount{T})(c::Currency) where {T} diff --git a/docs/src/lecture_06/compatibility.md b/docs/src/lecture_06/compatibility.md index d1ca37059..f3563233d 100644 --- a/docs/src/lecture_06/compatibility.md +++ b/docs/src/lecture_06/compatibility.md @@ -1,6 +1,6 @@ # Package dependencies -In this section, we focus on package dependencies. So far, we showed how to add a specific package into enviroment. However, we have never take into account the compatibility of different versions of the same package. As stated in the official [Julia package manager documentation](https://pkgdocs.julialang.org/v1/compatibility/): Compatibility refers to the ability to restrict the versions of the dependencies that your project is compatible with. If the compatibility for a dependency is not given, the project is assumed to be compatible with all versions of that dependency. +In this section, we focus on package dependencies. So far, we showed how to add a specific package into environment. However, we have never taken into account the compatibility of different versions of the same package. As stated in the official [Julia package manager documentation](https://pkgdocs.julialang.org/v1/compatibility/): Compatibility refers to the ability to restrict the versions of the dependencies that your project is compatible with. If the compatibility for a dependency is not given, the project is assumed to be compatible with all versions of that dependency. ## Compatibility @@ -22,7 +22,7 @@ In the example above, we are using semantic versioning. In this case, we set, th !!! warning "Exercise:" This exercise defines the `image` function that converts a matrix of real numbers to a matrix of Gray points. Real numbers can be converted to Gray points by the `Gray` constructor from the Colors package. - Each Julia package contains its environment for tracking package dependencies. Use proper commands in the Pkg REPL to add `Colors` as a dependency of the ImageInspector package. Do not forgot to add which versions of `Colors` package are supported. Fo the sake of the following excersises, allow versions `0.12.*` and `0.13.*` + Each Julia package contains its environment for tracking package dependencies. Use proper commands in the Pkg REPL to add `Colors` as a dependency of the ImageInspector package. Do not forget to add which versions of `Colors` package are supported. For the sake of the following exercises, allow versions `0.12.*` and `0.13.*` !!! details "Solution:" @@ -51,7 +51,7 @@ In the example above, we are using semantic versioning. In this case, we set, th Info Packages marked with ⌃ have new versions available and may be upgradable. ``` - In this particular case, we have Colors package in versions `0.12.11`. To add compatibility for a dependency, we can use `compat` command in the Pkg REPL. To allow versions `0.12.*` and `0.13.*`, we can use the following command + In this particular case, we have Colors package in version `0.12.11`. To add compatibility for a dependency, we can use `compat` command in the Pkg REPL. To allow versions `0.12.*` and `0.13.*`, we can use the following command ```julia (ImageInspector) pkg> compat Colors "0.12, 0.13" @@ -83,7 +83,7 @@ In the example above, we are using semantic versioning. In this case, we set, th end ``` -In the previous excersise, we added the first function into our package. In the following excersise, we will test the function in our `example` enviroment. +In the previous exercise, we added the first function into our package. In the following exercise, we will test the function in our `example` environment. !!! warning "Exercise:" Use the following code to test the function. @@ -101,7 +101,7 @@ In the previous excersise, we added the first function into our package. In the **Hint:** Do not forget to add `MLDatasets` and `Plots` to the `examples` environment. !!! details "Solution:" - First, we need to install all necessary packages. Since we want to add the packages to the `examples` environment, we have to change the enviroment again + First, we need to install all necessary packages. Since we want to add the packages to the `examples` environment, we have to change the environment again ```julia (ImageInspector) pkg> activate ./examples @@ -115,7 +115,7 @@ In the previous excersise, we added the first function into our package. In the (examples) pkg> add MLDatasets Plots ``` - Now with all packages installed, we can test the `image` function + Now, with all packages installed, we can test the `image` function ```julia # /examples/example.jl @@ -131,7 +131,7 @@ In the previous excersise, we added the first function into our package. In the ## Adding content -The previous exercise used the MLDatasets package that provides many well-known datasets used in machine learning. One of them is the `FashionMNIST` dataset of gray images of clothes. However, the resulting image is rotated 90 degrees. The reason is that images in the FashionMNIST dataset are stored in the **width x height** format, but the Plots package assumes the **height x width** format. We solve this issue by redefining the `image` function. +The previous exercise used the MLDatasets package that provides many well-known datasets used in machine learning. One of them is the `FashionMNIST` dataset of gray images of clothes. However, the resulting image is rotated 90 degrees. The reason is that images in the FashionMNIST dataset are stored in the **width x height** format, but the Plots package assumes the **height x width** format. We solve this issue by redefining the `image` function as follows ```julia function image(x::AbstractMatrix{T}; flip = true) where {T <: Real} @@ -187,7 +187,7 @@ We will now extend the `image` function to three-dimensional inputs. The third d ) ``` - **Hint:** use the `eachslice` function to split the array along the third dimension and the `dropdims` function to drop a dimension slice. + **Hint:** use the `eachslice` function to split the array along the third dimension and the `dropdims` function to drop a dimension. !!! details "Solution:" The functionality depends on the size of the third dimension. @@ -217,13 +217,13 @@ We will now extend the `image` function to three-dimensional inputs. The third d Multiple images are usually stored in multi-dimensional arrays. For example, grayscale images are stored as 3D or 4D arrays, where the last dimension represents individual images. Similarly, colour images are stored as a 4D array. !!! warning "Exercise:" - Add new methods for the `image` function with the following properties: + Add new methods to the `image` function with the following properties: - New methods should accept two arguments: - `x`: 3D or 4D array of real numbers that represents images, - `inds`: one or more image indices to extract and convert to Gray/RGB representation. - If only one index is provided, the method should return a single image in its representation. - - If more indices are provided, the method should return an array of images. + - If multiple indices are provided, the method should return an array of images. Use the following code to test the `image` function. diff --git a/docs/src/lecture_06/modules.md b/docs/src/lecture_06/modules.md index 57b79f1f1..e823af7c2 100644 --- a/docs/src/lecture_06/modules.md +++ b/docs/src/lecture_06/modules.md @@ -15,7 +15,7 @@ include("/absolute/path/to/the/file/filename.jl") include("../relative/path/to/the/file/filename.jl") ``` -The `include` function evaluates the source file content in the global scope of the module, where the `include` call occurs. If a file is included multiple times, it is also evaluated multiple times. +The `include` function evaluates the source file content in the global scope of the module, where the `include` call occurs. If a file is included multiple times, it is also evaluated multiple times. Even though using separate files to organize code can be very useful, this approach also has several disadvantages. For example, since all files are evaluated in the same global scope, we have to avoid clashes of variable/function names from different files. This problem can be solved by using modules as described in the following section. @@ -84,7 +84,7 @@ Points.coordinates(p) Points.coordinates(q) ``` -When writing a module, we have to decide which functions and types we want to export. The rule of thumb is that we export only the data end-users should use. +When writing a module, we have to decide which functions and types we want to export. The rule of thumb is to export only the data end-users should use. To redefine or extend an imported function, we need to specify the module. We can use the following way to redefine the `distance` function: diff --git a/docs/src/lecture_06/structure.md b/docs/src/lecture_06/structure.md index 7e84bf47d..74748b94f 100644 --- a/docs/src/lecture_06/structure.md +++ b/docs/src/lecture_06/structure.md @@ -13,7 +13,7 @@ We first generate an empty package `PackageName` by the built-in function `gener PackageName/src/PackageName.jl ``` -This way generates the new package in the working directory. However, we may also specify an absolute or relative path to generate it elsewhere. The `generate` function creates a new folder (with the name matching the package name) with the following content. +This command generates the new package in the working directory. However, we may also specify an absolute or relative path to generate it elsewhere. The `generate` function creates a new folder (with the name matching the package name) with the following content. ```julia ├── Project.toml @@ -41,11 +41,11 @@ authors = ["Author Name"] version = "0.1.0" ``` -Since the `Project.toml` file `src/*.jl` files are sufficient for determining a package, packages are modules with their own environment. +Since the `Project.toml` file `src/*.jl` files are sufficient for defining a package, packages are modules with their own environment. ## PkgTemplates -The built-in `generate` function provides only basic functionality for generating packages. Even though it is sufficient in many cases, the [PkgTemplates](https://github.com/invenia/PkgTemplates.jl) package offers a straightforward and customizable way for creating packages. +The built-in `generate` function provides only basic functionality for generating packages. Even though it is sufficient in many cases, the [PkgTemplates](https://github.com/invenia/PkgTemplates.jl) package offers a straightforward and customizable way to create packages. !!! warning "Exercise:" The goal of this exercise is to create a new package by the PkgTemplates package. Install PkgTemplates and then use the following code to generate a new package template. @@ -62,7 +62,7 @@ The built-in `generate` function provides only basic functionality for generatin plugins=[ ProjectFile(; version=v"0.1.0"), # Add version Readme(; inline_badges=true), # add readme file with badges - Tests(; project=false, aqua=true), # add unit test deps and Aquq + Tests(; project=false, aqua=true), # add unit test deps and Aqua Git(; manifest=false), # add manifest.toml to .gitignore License(; name="MIT"), # add MIT licence # disable other plugins @@ -76,7 +76,7 @@ The built-in `generate` function provides only basic functionality for generatin Do not forget to change `user`, `authors` and `dir`. - In the rest of the lecture, we will write code to visualize grayscale and colour images. Come up with a proper package name and use the following code to generate a new package. + In the rest of the lecture, we will write code to visualize grayscale and colour images. Choose an appropriate package name and use the following code to generate a new package. ```julia template("PackageName") @@ -97,7 +97,7 @@ The built-in `generate` function provides only basic functionality for generatin mkdir(joinpath("/Path/To/Dir/", "ImageInspector", "examples")) ``` - The generated folder contains more files than the folder generated by the built-in `generate` function. + The generated folder contains more files than the folder created by the built-in `generate` function. ```julia ├── .git diff --git a/docs/src/lecture_06/workflow.md b/docs/src/lecture_06/workflow.md index 04dcc6057..c14df753c 100644 --- a/docs/src/lecture_06/workflow.md +++ b/docs/src/lecture_06/workflow.md @@ -4,12 +4,12 @@ In the previous section, we created a new empty package. In this section, we wil ## Development mode -The content of the `ImageInspector` folder can be divided into four parts: +The contents of the `ImageInspector` folder can be divided into four parts: - *Root folder* contains information about the package and git. - *Folder src* contains the package source code. - *Folder tests* contains the testing scripts for verifying the code correctness. - *Folder examples* is used to run examples. -The first three are standard, while we added the last folder manually. We can add more folders, such as `data`. +The first three are standard, while we the last folder was added manually. We can add more folders, such as `data`. We first activate a new environment in the `examples` folder. @@ -34,8 +34,8 @@ Status `.../ImageInspector/examples/Project.toml` Like the `add` command, the `dev` command allows us to load the package by `using` or `import`. The difference between `add` and `dev` is that the `dev` command tracks the package current state and not a concrete git commit in some branch. -!!! warning "Default Julia enviroment in VS Code:" - The VS Code allows setting a default Julia environment that is activated when Julia REPL is opened. We can do this by pressing `Julia env: ` located at the bottom info bar and selecting the desired environment. +!!! warning "Default Julia environment in VS Code:" + VS Code allows setting a default Julia environment that is activated when Julia REPL is opened. We can do this by pressing `Julia env: ` located at the bottom info bar and selecting the desired environment. ## Revise.jl @@ -66,7 +66,7 @@ julia> greet() ERROR: UndefVarError: greet not defined ``` -In this case, we have to restart Julia. There are two ways how to exit Julia interactive session: using keyword shortcut `ctrl + D` or using the `exit()` function. Even though we can use the `greet()` function after the restart, we will not do it yet. The reason is that we would have to restart Julia again after making any changes to the package. Since this is not a convenient way to code, we will use the [Revise](https://github.com/timholy/Revise.jl) package. Even though it provides lots of convenient features, we will present only its basic use. First, we install it. +In this case, we have to restart Julia. There are two ways to exit Julia interactive session: using keyword shortcut `ctrl + D` or using the `exit()` function. Even though we can use the `greet()` function after the restart, we will not do it yet. The reason is that we would have to restart Julia again after making any changes to the package. Since this is not a convenient way to code, we will use the [Revise](https://github.com/timholy/Revise.jl) package. Even though it provides many convenient features, we will present only its basic use. First, we install it. ```julia (examples) pkg> add Revise @@ -108,13 +108,13 @@ Hello World!!!! ``` !!! info "Automatic Revise loading" - `Revise` package can be loaded automaticaly at the start of every Julia session. The easiest way how to achieve such behavior is to use `StartupCustomizer` package. Let's start with installing the package into the default Julia enviroment + `Revise` package can be loaded automatically at the start of every Julia session. The easiest way how to achieve such behavior is to use `StartupCustomizer` package. Let's start with installing the package into the default Julia environment ```julia (@v1.10) pkg> add StartupCustomizer ``` - When the package is installed, we can run the following commands, that will install `Revise` into the default Julia enviroment and modify the Julia startup file, to load `Revise` at the beggining of every Julia session. + When the package is installed, we can run the following commands, that will install `Revise` into the default Julia environment and modify the Julia startup file, to load `Revise` at the beginning of every Julia session. ```julia julia> import StartupCustomizer @@ -135,7 +135,7 @@ Hello World!!!! # end StartupCustomizer.Revise() ``` - `StartupCustomizer` also supports other plugins, such as `OhMyREPL`, that will enable code highlightning in your REPL. We can add this pluggin in the similar way as we added the Revise plugin. + `StartupCustomizer` also supports other plugins, such as `OhMyREPL`, that will enable code highlighting in your REPL. We can add this plugin in the similar way as we added the Revise plugin. ```julia julia> StartupCustomizer.add(StartupCustomizer.OhMyREPL()) diff --git a/docs/src/lecture_07/documentation.md b/docs/src/lecture_07/documentation.md index 540c27432..c93ba86a5 100644 --- a/docs/src/lecture_07/documentation.md +++ b/docs/src/lecture_07/documentation.md @@ -38,7 +38,7 @@ function image(x::AbstractMatrix{T}; flip = true) where {T <: Real} end ```` -We first wrote a function header, and then we used one tab as an indentation. Then we wrote a short description of the function. Finally, we wrote usage examples. To get a well-looking format of the docstring, we use [markdown](https://en.wikipedia.org/wiki/Markdown) `# Example` to represents a title. We use the `julia-repl` block to write code. Now we type the function name into the Julia help. +We first wrote a function header, and then we used one tab as an indentation. Then we wrote a short description of the function. Finally, we wrote usage examples. To get a well-looking format of the docstring, we use [markdown](https://en.wikipedia.org/wiki/Markdown) `# Example` to represent a title. We use the `julia-repl` block to write code. Now we type the function name into the Julia help. ```julia @@ -96,14 +96,14 @@ img1 = [x^2 + y^2 > 1 for x in xs, y in xs]; plot(image(img1); axis=nothing, border=:none) ``` -The Markdown syntax starts with `#`. Among others, it allows to use: +The Markdown syntax starts with `#`. Among others, it allows you to use: - Links such as `[Literate](https://fredrikekre.github.io/Literate.jl/v2)`. - Variables or latex syntax such as `$[-1,1]$`. Exporting the script into a notebook is simple. ```julia -julia> Import Literate +julia> import Literate julia> Literate.notebook("report.jl"; execute=true) ``` @@ -145,8 +145,8 @@ function imagegrid(x, inds; flip = true, sep = 1, kwargs...) h, w = size(imgs[1]) A = fill( eltype(imgs[1])(1), # white color in proper color type - nrows*h + (nrows + 1)*sep, # height of the reculting image - ncols*w + (ncols + 1)*sep, # width of the reculting image + nrows*h + (nrows + 1)*sep, # height of the resulting image + ncols*w + (ncols + 1)*sep, # width of the resulting image ) for i in 1:nrows, j in 1:ncols @@ -173,4 +173,4 @@ plot(imagegrid(X, 1:10; nrows = 2, sep = 2); axis = nothing, border = :none) ![](image_5.svg) !!! warning "Exercise:" - Add doc strings for all functions in the ImageInspector package. \ No newline at end of file + Add docstrings for all functions in the ImageInspector package. \ No newline at end of file diff --git a/docs/src/lecture_07/extensions.md b/docs/src/lecture_07/extensions.md index f24f55572..01d0d5986 100644 --- a/docs/src/lecture_07/extensions.md +++ b/docs/src/lecture_07/extensions.md @@ -29,16 +29,16 @@ Test = "1.9" julia = "1.9" ``` -Now, we define an empty function `imageplot` inside of the ImageInstructor, i. e., we add the foollowing code to the `src/ImageInstructor.jl` +Now, we define an empty function `imageplot` inside of the ImageInstructor, i. e., we add the following code to the `src/ImageInstructor.jl` ```julia # src/ImageInstructor.jl function imageplot end ``` -This step is needed, since we will add methods to this function inside our extention. +This step is needed, since we will add methods to this function inside our extension. -The last step is to create the extension itself. The code for extension must be stored in `ext` folder in the root dir of the package. The code for the extension is them must be defined in the file with the same name, i. e., we have to create a new file `ext/PlotsExt.jl` and add the code into it +The last step is to create the extension itself. The code for extension must be stored in `ext` folder in the root dir of the package. The code for the extension must be defined in the file with the same name, i. e., we have to create a new file `ext/PlotsExt.jl` and add the code into it ```julia # ext/PlotsExt.jl @@ -71,7 +71,7 @@ end end ``` -Note, that we defined a new module, that has the same name as our extension. And that's all. Now we can test, whether the extension works. We have to start a new Julia session and activate `examples` enviroment. Now, if we do not load `Plots`, the `imageplot` function will have no methods, as can be seen below +Note, that we defined a new module, that has the same name as our extension. And that's all. Now we can test, whether the extension works. We have to start a new Julia session and activate `examples` environment. Now, if we do not load `Plots`, the `imageplot` function will have no methods, as can be seen below ```julia julia> using ImageInspector, MLDatasets @@ -148,7 +148,7 @@ end end ``` -Now it's time to test the extension. To do so, we first have to install `CairoMakie` into `examples` enviroment +Now it's time to test the extension. To do so, we first have to install `CairoMakie` into `examples` environment ```julia (ImageInspector) pkg> activate ./examples @@ -156,7 +156,7 @@ Now it's time to test the extension. To do so, we first have to install `CairoMa (examples) pkg> add CairoMakie ``` -We have to start a new Julia session and activate `examples` enviroment. Now, if we do not load `CairoMakie`, the `imageplot` function will have no methods, as can be seen below +We have to start a new Julia session and activate `examples` environment. Now, if we do not load `CairoMakie`, the `imageplot` function will have no methods, as can be seen below ```julia julia> using ImageInspector, MLDatasets diff --git a/docs/src/lecture_07/tests.md b/docs/src/lecture_07/tests.md index 6aac8b43d..76b988797 100644 --- a/docs/src/lecture_07/tests.md +++ b/docs/src/lecture_07/tests.md @@ -5,7 +5,7 @@ In this section, we discuss how to test functions defined in the package as well ## Test dependencies -When testing the package, it is often usefull to have some additional dependencies that we do not directly use in the package, but are useful for testing. The prototypical example is the `Test` standard library, that contains utilities for testing. Since we use `PkgTemplates` to generate the package structure, we already have some test specific dependencies defined. We can check it in the `Project.toml`, where we have two section we didn';t talk about yet. The first one is `extras` section that cane be used to optional dependencies. In our case, the `extras` section contains two packages +When testing the package, it is often useful to have some additional dependencies that we do not directly use in the package, but are useful for testing. The prototypical example is the `Test` standard library, that contains utilities for testing. Since we use `PkgTemplates` to generate the package structure, we already have some test specific dependencies defined. We can check it in the `Project.toml`, where we have two section we didn't talk about yet. The first one is `extras` section that can be used for optional dependencies. In our case, the `extras` section contains two packages ```toml [extras] @@ -13,14 +13,14 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" ``` -The second section we didnt talked about yet, is the `targets` section. This section allows us to define which dependencies are used for testing. In our case, the section has the following content +The second section we haven't talked about yet, is the `targets` section. This section allows us to define which dependencies are used for testing. In our case, the section has the following content ```toml [targets] test = ["Aqua", "Test"] ``` -It is a good practice to specify compatibility even for th packages that are used for testing. Unfortunatelly, the package manager curently do not support adding compatibility for extras. However, we can add the compatibility manually by modifying `compat` section in the `Project.toml` as follows +It is a good practice to specify compatibility even for the packages that are used for testing. Unfortunately, the package manager currently do not support adding compatibility for extras. However, we can add the compatibility manually by modifying `compat` section in the `Project.toml` as follows ```julia [compat] @@ -50,7 +50,7 @@ using Aqua end ``` -Now we can easily run the tests using the `test` command in the Pkg REPL. Note, that we mst have activated the correct envroment +Now we can easily run the tests using the `test` command in the Pkg REPL. Note, that we must have activated the correct envroment ```julia (ImageInspector) pkg> test @@ -63,7 +63,7 @@ ImageInspector.jl | 11 11 7.6s ## Unit tests -The previous lecture added the `image` function with multiple methods. We also manually tested if these methods work correctly. Even though this practice works for small projects, it is not optimal for code testing and should be automized by [unit testing](https://en.wikipedia.org/wiki/Unit_testing). The `Test` package from the standard library provides utility functions to simplify writing unit tests. Its core is the `@test` macro that tests if an expression evaluates as `true`. +The previous lecture added the `image` function with multiple methods. We also manually tested if these methods work correctly. Even though this practice works for small projects, it is not optimal for code testing and should be automated by [unit testing](https://en.wikipedia.org/wiki/Unit_testing). The `Test` package from the standard library provides utility functions to simplify writing unit tests. Its core is the `@test` macro that tests if an expression evaluates as `true`. ```@repl tests using Test @@ -78,7 +78,7 @@ It is possible to pass additional arguments to the `@test` macro. @test π ≈ 3.14 atol=0.01 ``` -If we go back to our package, we can start writing tests for the methods of the `image` function. All tests should be located in the ` /test` foldert. First, we have to import all necessary packages: `Test`, `ImageInspector` and `Colors`. +If we go back to our package, we can start writing tests for the methods of the `image` function. All tests should be located in the ` /test` folder. First, we have to import all necessary packages: `Test`, `ImageInspector` and `Colors`. ```julia julia> using ImageInspector, ImageInspector.Colors @@ -215,7 +215,7 @@ Test Passed end ``` - We can again run all tests directly from the ImageInspector enviroment in the Pkg REPL using the `test` command. + We can again run all tests directly from the ImageInspector environment in the Pkg REPL using the `test` command. ```julia (ImageInspector) pkg> test @@ -226,7 +226,7 @@ Test Passed Testing ImageInspector tests passed ``` - We can also run tests for some specific package by specifying the name of the package after the `test` command. For example, we can run all testst for the ImageInspector from the `example` enviroment in the following way + We can also run tests for some specific package by specifying the name of the package after the `test` command. For example, we can run all tests for the ImageInspector from the `example` environment in the following way ```julia (ImageInspector) pkg> activate ./examples diff --git a/docs/src/lecture_08/constrained.md b/docs/src/lecture_08/constrained.md index 4a4a1fc20..160211c8b 100644 --- a/docs/src/lecture_08/constrained.md +++ b/docs/src/lecture_08/constrained.md @@ -122,7 +122,7 @@ When there are no constraints, the Lagrangian ``L`` reduces to the objective ``f ## Numerical method -We present only the simplest method for constraint optimization. Projected gradients +We present only the simplest method for constrained optimization. Projected gradients ```math \begin{aligned} @@ -203,7 +203,7 @@ xys = hcat(reshape([xs[:,1:end-1]; ys][:], 2, :), xs[:,end]) nothing # hide ``` -It is probably not the nicest thing to do, but it is Saturday evening, I am tired, and it works. Sorry :) The animation can now be created in the same way as before. +The animation can now be created in the same way as before. ```@example optim create_anim(f, xys, xlims, ylims, "anim7.gif"; diff --git a/docs/src/lecture_08/exercises.md b/docs/src/lecture_08/exercises.md index ec9d311d6..2039716c8 100644 --- a/docs/src/lecture_08/exercises.md +++ b/docs/src/lecture_08/exercises.md @@ -76,9 +76,9 @@ nothing # hide ``` - This implementation is efficient in the way that only one function evaluation is needed per iteration. The price to pay are additional variables ```fa```, ```fb``` and ```fc```. + This implementation is efficient in the way that only one function evaluation is needed per iteration. The price to pay is additional variables ```fa```, ```fb``` and ```fc```. - To use the bisection method to minimize a function ``f(x)``, we use it find the solution of the optimality condition ``f'(x)=0``. + To use the bisection method to minimize a function ``f(x)``, we use it to find the solution of the optimality condition ``f'(x)=0``. ```@example bisec f(x) = x^2 - x @@ -253,7 +253,7 @@ println(round.(x, digits=4)) # hide If ``h_j(x)\neq 0``, then it is simple to choose ``\mu_j``so that the inner maximization problem has the optimal value ``+\infty``. However, since the outer problem minimizes the objective, the value of ``+\infty`` is irrelevant. Therefore, we can ignore all points with ``h_j(x)\neq 0`` and prescribe ``h_j(x)=0`` as a hard constraint. That is precisely the primal formulation. !!! warning "Exercise 6 (theory)" - Derive the dual formulation for the linear programming. + Derive the dual formulation for linear programming. !!! details "Solution:" The linear program diff --git a/docs/src/lecture_08/gradients.md b/docs/src/lecture_08/gradients.md index 68fceedd6..f3dd9439a 100644 --- a/docs/src/lecture_08/gradients.md +++ b/docs/src/lecture_08/gradients.md @@ -25,7 +25,7 @@ Functions are usually complicated, and this definition cannot be used to compute \nabla h(x) = \nabla f(g(x))\nabla g(x). ``` -The derivative is the direction of the steepest ascent. The following figure shows the vector field of derivatives, where each arrow shows the direction and size of derivatives at the points in the domain. Since a function has the same function values along its contour lines and since derivate is the direction of the steepest ascent, derivatives are perpendicular to contour lines. The figure also shows that local minima and local maxima have zero derivatives. +The derivative is the direction of the steepest ascent. The following figure shows the vector field of derivatives, where each arrow shows the direction and size of derivatives at the points in the domain. Since a function has the same function values along its contour lines and since derivative is the direction of the steepest ascent, derivatives are perpendicular to contour lines. The figure also shows that local minima and local maxima have zero derivatives. ![](grad3.svg) @@ -75,7 +75,7 @@ on domain ``[-3,1]\times [-2,1]``. println(f(0, 0)) # hide ``` - We use the ```Plots``` package for plotting. We create the discretization ```xs``` and ```ys``` of both axis and then call the ```contourf``` function. + We use the ```Plots``` package for plotting. We create the discretization ```xs``` and ```ys``` of both axes and then call the ```contourf``` function. ```@example optim using Plots @@ -167,7 +167,7 @@ This way of computing the gradient has two disadvantages: ![](grad2.svg) -The approximation is good if ``h`` is not too small or too large. It cannot be too large because the definition of the gradient considers the limit to zero. It cannot be too small because the numerical errors kick in. This is connected with machine precision, which is most vulnerable to subtracting two numbers of almost the same value. A simple example shows +The approximation is good if ``h`` is not too small or too large. It cannot be a large number because the definition of the gradient considers the limit to zero. It cannot be too small of a number, because the numerical errors kick in. This is connected with machine precision, which is most vulnerable to subtracting two numbers of almost the same value. A simple example shows ```math (x + h)^2 - x^2 = 2xh + h^2 @@ -182,7 +182,7 @@ h = 1e-13; 2*x*h + h^2 ``` -gives an error already at the fourth valid digit. It is important to realize how numbers are stored. Julia uses the [IEEE 754 standard](https://en.wikipedia.org/wiki/IEEE_754). For example, `Float64` uses 64 bits to store the number, from which 1 bit represents the sign, 11 bits the exponent and 52 bits the significand precision. As ``2^{52}\approx 10^{16}``, numbers are stored with a 16-digit precision. Since the exponent is stored separately, it is possible to represent numbers smaller than the machine precision, such as ``10^{-25}``. To prevent numerical errors, all computations are done in higher precision, and the resulting variable is rounded to the type precision. +returns an incorrect number at the fourth valid digit already. It is important to realize how numbers are stored. Julia uses the [IEEE 754 standard](https://en.wikipedia.org/wiki/IEEE_754). For example, `Float64` uses 64 bits to store the number, from which 1 bit represents the sign, 11 bits the exponent and 52 bits the significand precision. As ``2^{52}\approx 10^{16}``, numbers are stored with a 16-digit precision. Since the exponent is stored separately, it is possible to represent numbers smaller than the machine precision, such as ``10^{-25}``. To prevent numerical errors, all computations are done in higher precision, and the resulting variable is rounded to the type precision. Finally, we show how the gradients look like. diff --git a/docs/src/lecture_08/theory.md b/docs/src/lecture_08/theory.md index ffcebb91d..a57d8aa0b 100644 --- a/docs/src/lecture_08/theory.md +++ b/docs/src/lecture_08/theory.md @@ -3,10 +3,10 @@ Optimization problems optimize (minimize or maximize) a given function on a given set. There are many applications: - Maximize profit under market forecasts. - Given a set of points, find a visiting order which minimizes the distance. This application includes various tasks ranging from delivery services to snow ploughing. -- Make a prediction based on known data. Specific examples are whether a client gets a loan or whether an autonomous vehicle sees a pedestrian. Almost all tasks in machine learning minimize the difference between a prediction and a label. +- Make a prediction based on known data. Specific examples are whether a client gets a loan or whether an autonomous vehicle sees a pedestrian. Almost all tasks in machine learning minimize the difference between prediction and label. - Find the optimal shape of a machine so that a criterion is maximized. Specific examples are designing planes with minimal drag or optimizing engines to maximize power under a reasonable oil consumption. -These applications are very different from each other. They differ in their assumptions about the world, in their formulation and solution way. +These applications are very different from each other. They differ in their assumptions about the world, in their formulation and solutions. - Profit maximization needs to model future uncertainty. The formulation will probably contain expectations and chance constraints, while the variables will be continuous. - Finding the minimal way is often reformulated as finding the shortest path in a graph. Problems like this operate typically with binary variables and no uncertainty. - Machine learning requires loads of data and usually ignores any physical models. Due to the abundance of data, the objective function evaluations are lengthy, and specially designed optimization algorithms are used. diff --git a/docs/src/lecture_08/unconstrained.md b/docs/src/lecture_08/unconstrained.md index 07a08578e..e3aed583a 100644 --- a/docs/src/lecture_08/unconstrained.md +++ b/docs/src/lecture_08/unconstrained.md @@ -21,7 +21,7 @@ What do we look for when we minimize a function ``f`` over some ``X``? The optim f(x) \le f(y) \text{ for all }y\in X. ``` -This point is often challenging to find. Sometimes we can find a local minimum, which is a global minimum on some small neighbourhood of ``x``. However, as the following theorem suggests, we often need to lower our requirements even lower. +This point is often challenging to find. Sometimes we can find a local minimum, which is a global minimum on some small neighbourhood of ``x``. However, as the following theorem suggests, we often need to lower our requirements even more. !!! todo "Theorem: Connection between optimization problems and gradients" Consider a differentiable function ``f`` over ``X=\mathbb{R}^n``. If ``x`` is its local minimum, then ``\nabla f(x)=0``. Conversely, if ``f`` is convex, then every point ``x`` with ``\nabla f(x)=0`` is a global minimum of ``f``. @@ -31,7 +31,7 @@ Points with ``\nabla f(x)=0`` are known as stationary points. Optimization algor ![](minmax.svg) !!! info "Take care:" - This theorem does not hold if ``X`` is not the whole space. A simple counterexmple is minimization of ``f(x)=x`` on ``X=[0,1]``. + This theorem does not hold if ``X`` is not the whole space. A simple counter example is minimization of ``f(x)=x`` on ``X=[0,1]``. Since the gradient is the direction of the steepest ascent, the straightforward idea is to move in the opposite direction. This gives rise to the gradient (or steepest) descent algorithm ```math @@ -53,8 +53,8 @@ Here ``c\in(0,1)`` is a small constant, usually ``c=10^{-4}``. Since the left-h - Parameter is an external (fixed) parameter such as a material parameter. The example would be ``\alpha``. In machine learning, the usual terminology is: - Parameter is to be optimized. - - Hyperparameter is an external model parameter that is not optimized and needs to be tuned. The example is the steplength because the gradient descent finds a different solution for different steplength, but it is not changed during the optimization. - The different terminology (and possibly the fact that there are adaptive schemes to select the steplength, which should make it a parameter instead of a hyperparameter) makes the notation confusing. + - Hyperparameter is an external model parameter that is not optimized and needs to be tuned. The example is the step length because the gradient descent finds a different solution for different step length, but it is not changed during the optimization. + The different terminology (and possibly the fact that there are adaptive schemes to select the step length, which should make it a parameter instead of a hyperparameter) makes the notation confusing. ## Gradient descent @@ -237,7 +237,7 @@ end nothing # hide ``` -The specification of the input ```s::Step``` allows for any subtype of the abstract type ```Step```. Using this implentation results in +The specification of the input ```s::Step``` allows for any subtype of the abstract type ```Step```. Using this implementation results in ```@example optim gd = GD(0.1) diff --git a/docs/src/lecture_09/exercises.md b/docs/src/lecture_09/exercises.md index 83d435dac..c8e605593 100644 --- a/docs/src/lecture_09/exercises.md +++ b/docs/src/lecture_09/exercises.md @@ -105,13 +105,13 @@ w = log_reg(X, y, zeros(size(X,2))) \hat y_i = \sigma(w^\top x_i), ``` - where ``\sigma`` is the sigmoid function. Since the mimimum from ``w^\top x_i`` + where ``\sigma`` is the sigmoid function. Since the minimum from ``w^\top x_i`` ```@example ex_log minimum(X*[1;2;3]) ``` - is large, all ``w^\top x_i`` are large. But plotting the sigmoid funtion + is large, all ``w^\top x_i`` are large. But plotting the sigmoid function ```@example ex_log xs = -10:0.01:10 @@ -164,4 +164,4 @@ w = log_reg(X, y, zeros(size(X,2))) a^\top x_i x_i^\top a = (x_i^\top a)^\top (x_i^\top a) = \|x_i^\top a\|^2 \ge 0, ``` - which implies that ``x_i x_i^\top`` is a positive semidefinite matrix (it is known as rank-1 matrix as its rank is always 1 if ``x_i`` is a non-zero vector). Since ``y_i(1-\hat y_i)\ge 0``, it follows that ``\nabla^2 L(w)`` is a positive semidefinite matrix. If a Hessian of a function is positive semidefinite everywhere, the function is immediately convex. Since Newton's method found a stationary point, this points is a global minimum. \ No newline at end of file + which implies that ``x_i x_i^\top`` is a positive semidefinite matrix (it is known as rank-1 matrix as its rank is always 1 if ``x_i`` is a non-zero vector). Since ``y_i(1-\hat y_i)\ge 0``, it follows that ``\nabla^2 L(w)`` is a positive semidefinite matrix. If a Hessian of a function is positive semidefinite everywhere, the function is immediately convex. Since Newton's method found a stationary point, this point is a global minimum. \ No newline at end of file diff --git a/docs/src/lecture_09/linear.md b/docs/src/lecture_09/linear.md index 4a57148e8..cdf6235f1 100644 --- a/docs/src/lecture_09/linear.md +++ b/docs/src/lecture_09/linear.md @@ -21,13 +21,13 @@ We start with linear regression, where the labels are continuous variables. ## Theory of linear regression -Linear regression uses the linear prediction function ``\operatorname{predict}(w;x) = w^\top x`` and the mean square error ``\operatorname{loss}(y, \hat y) = (y - \hat y)^2`` as the loss function. When we have a dataset with ``n`` data points (samples) ``x_i`` and labels ``y_i``, linear regression may be written as the following optimization problem. +Linear regression uses the linear prediction function ``\operatorname{predict}(w;x) = w^\top x`` and the d error ``\operatorname{loss}(y, \hat y) = (y - \hat y)^2`` as the loss function. When we have a dataset with ``n`` data points (samples) ``x_i`` and labels ``y_i``, linear regression may be written as the following optimization problem. ```math \operatorname{minimize}_w\qquad \frac 1n\sum_{i=1}^n (w^\top x_i - y_i)^2. ``` -The objective function is minimal if the predictions ``w^\top x_i`` equal to the labels ``y_i`` for all samples ``i=1,\dots,n``. +The objective function is minimal if the predictions ``w^\top x_i`` are equal to the labels ``y_i`` for all samples ``i=1,\dots,n``. Some algorithms use the sum instead of the mean in the objective function. These approaches are equivalent. For the former case, it is simpler to work in the matrix notation, where we form a matrix ``X`` whose rows are the samples ``x_i``. It is not difficult to show that the previous problem is equivalent to @@ -126,7 +126,7 @@ The figure shows a positive correlation between length and width. This is natura nothing # hide ``` - For the gradient descent, we first realize that the formula for the derivate is ``X^\top (Xw-y)``. Defining the derivative function in ```g```, we call the ```optim``` method in the same way as in the last lecture. Since we use the sum and not mean in the objective, we need to use a much smaller stepsize. + For the gradient descent, we first realize that the formula for the derivative is ``X^\top (Xw-y)``. Defining the derivative function in ```g```, we call the ```optim``` method in the same way as in the last lecture. Since we use the sum and not mean in the objective, we need to use a much smaller step size. ```@example linear g(w) = X'*(X*w-y) @@ -154,7 +154,7 @@ w # hide Now we can estimate the petal width if only petal length is known. !!! warning "Exercise:" - Write the dependence on the petal width on the petal length. Plot it in the previous graph. + Write the dependence of the petal width on the petal length. Plot it in the previous graph. !!! details "Solution:" The desired dependence is diff --git a/docs/src/lecture_09/logistic.md b/docs/src/lecture_09/logistic.md index d06ce3614..fadf83a0d 100644 --- a/docs/src/lecture_09/logistic.md +++ b/docs/src/lecture_09/logistic.md @@ -19,7 +19,7 @@ The name logistic regression is misleading because it is actually a classificati \end{aligned} ``` -Denoting ``\hat y = \mathbb{P}(y=1\mid x)`` the probabily of predicting ``1``, the loss function is the cross-entropy loss +Denoting ``\hat y = \mathbb{P}(y=1\mid x)`` the probability of predicting ``1``, the loss function is the cross-entropy loss ```math \operatorname{loss}(y,\hat y) = - y\log \hat y - (1-y)\log(1-\hat y). @@ -34,7 +34,7 @@ Denoting ``\hat y = \mathbb{P}(y=1\mid x)`` the probabily of predicting ``1``, t Since ``\hat y`` lies in the interval ``(0,1)`` due to the sigmoid function, the cross-entropy is minimized when ``\hat y = 1``. Since we get similar results for ``y=0``, the cross-entropy is minimal whenever the labels ``y`` equal to the predictions ``\hat y``. -Then is not difficult to show that then the logistic regression problems reads +Then it is not difficult to show that then the logistic regression problems reads ```math \operatorname{minimize}_w\qquad \frac1n\sum_{i=1}^n\left(\log(1+e^{-w^\top x_i}) + (1-y_i)w^\top x_i \right). @@ -302,7 +302,7 @@ The picture shows that there are misclassified samples. The next exercise analys nothing # hide ``` - There is an alternative (but equivalent way). Since the separating hyperplane has form ``w^\top x``, we predict that a sample is positive whenever ``w^\top x\ge 0``. Write arguments on why these two approaches are equivalent. + There is an alternative (but equivalent way). Since the separating hyperplane the form ``w^\top x``, we predict that a sample is positive whenever ``w^\top x\ge 0``. Write arguments on why these two approaches are equivalent. The correct answer is diff --git a/docs/src/lecture_09/theory.md b/docs/src/lecture_09/theory.md index a7c6ed48f..1f075aea4 100644 --- a/docs/src/lecture_09/theory.md +++ b/docs/src/lecture_09/theory.md @@ -1,6 +1,6 @@ # Introduction to regression and classification -Regression and classification are a part of machine learning which predicts certain variables based on labelled data. Both regression and classification operate on several premises: +Regression and classification are a part of machine learning which predicts certain variables based on labeled data. Both regression and classification operate on several premises: - We differentiate between datapoints ``x`` and labels ``y``. While data points are relatively simple to obtain, labels ``y`` are relatively hard to obtain. - We consider some parameterized function ``\operatorname{predict}(w;x)`` and try to find an unknown variable ``w`` to correctly predict the labels from samples (data points) @@ -8,17 +8,17 @@ Regression and classification are a part of machine learning which predicts cert \operatorname{predict}(w;x) \approx y. ``` -- We have a labelled datasets with ``n`` samples ``x_1,\dots,x_n`` and labels ``y_1,\dots,y_n``. -- We use the labelled dataset to train the weights ``w``. -- When an unlabelled sample arrives, we use the prediction function to predict its label. +- We have a labeled dataset with ``n`` samples ``x_1,\dots,x_n`` and labels ``y_1,\dots,y_n``. +- We use the labeled dataset to train the weights ``w``. +- When an unlabeled sample arrives, we use the prediction function to predict its label. -The [MNIST](https://en.wikipedia.org/wiki/MNIST_database) dataset contains ``n=50000`` images of grayscale digits. Each image ``x_i`` from the dataset has the size ``28\times 28`` and was manually labelled by ``y_i\in\{0,\dots,9\}``. When the weights ``w`` of a prediction function ``\operatorname{predict}(w;x)`` are trained on this dataset, the prediction function can predict which digit appears on images it has never seen before. This is an example where the images ``x`` are relatively simple to obtain, but the labels ``y`` are hard to obtain due to the need to do it manually. +The [MNIST](https://en.wikipedia.org/wiki/MNIST_database) dataset contains ``n=50000`` images of grayscale digits. Each image ``x_i`` from the dataset has the size ``28\times 28`` and was manually labeled by ``y_i\in\{0,\dots,9\}``. When the weights ``w`` of a prediction function ``\operatorname{predict}(w;x)`` are trained on this dataset, the prediction function can predict which digit appears on images it has never seen before. This is an example where the images ``x`` are relatively simple to obtain, but the labels ``y`` are hard to obtain due to the need to do it manually. ## Regression and classification The difference between regression and classification is simple: - Regression predicts a continuous variable ``y`` (such as height based on weight). -- Classification predict a variable ``y`` with a finite number of states (such as cat/dog/none from images). +- Classification predicts a variable ``y`` with a finite number of states (such as cat/dog/none from images). The body-mass index is used to measure fitness. It has a simple formula @@ -70,7 +70,7 @@ while non-linear predictions are considered in the following lecture. That means that if we add ``1`` to each sample ``x_i``, it is sufficient to consider the classifier in the form ``w^\top x`` without the bias (shift, intercept) ``b``. This allows for simpler implementation. !!! compat "BONUS: Data transformation" - Linear models have many advantages, such as simplicity or guaranteed convergence for optimization methods. Sometimes it is possible to transform non-linear dependences into linear ones. For example, the body-mass index + Linear models have many advantages, such as simplicity or guaranteed convergence for optimization methods. Sometimes it is possible to transform non-linear dependencies into linear ones. For example, the body-mass index ```math \operatorname{BMI} = \frac{w}{h^2} diff --git a/docs/src/lecture_10/exercises.md b/docs/src/lecture_10/exercises.md index 43f2d6339..18e4251a5 100644 --- a/docs/src/lecture_10/exercises.md +++ b/docs/src/lecture_10/exercises.md @@ -341,7 +341,7 @@ nothing # hide If a classifier does not have any data in some region, it may predict anything there, including predictions with no sense. !!! warning "Exercise 5: Universal approximation of neural networks" - Proof the theorem about universal approximation of neural networks. + Prove the theorem about universal approximation of neural networks. !!! details "Solution:" Since piecewise linear functions are dense in the set of continuous functions, there is a piecewise linear function ``h`` such that ``\|h-g\|_{\infty}\le \varepsilon``. Assume that ``h`` has kinks at ``x_1<\dots