===== Homework 01 ===== As always there are multiple options for the ''circlemat'' function definition. Here we show the two, that we have foreshadowed in the homework hints: * longer version using incremental definition in for loop function circlemat(n) A = zeros(Int, n, n) # creates nxn matrix of zeros for i in 1:n for j in 1:n if (i == j-1 && j > 1) || (i == n && j == 1) || (i == j+1 && j < n) || (i == 1 && j == n) A[i,j] = 1 end end end return A end circlemat(10) * short version with comprehension and ternary operator circlemat(n) = [(i == j-1 && j > 1) || (i == n && j == 1) || (i == j+1 && j < n) || (i == 1 && j == n) ? 1 : 0 for i in 1:n, j in 1:n] circlemat(10) Both version give the same answer. Extending the original ''polynomial'' to matrix valued point ''x = A'', requires only small changes to the initialization of ''accumulator'' variable. Running directly the original code fails on ''MethodError'', because julia cannot add a matrix to an integer. function polynomial(a, x::AbstractMatrix) # we are limiting this function to everything that is subtype of AbstractMatrix accumulator = zeros(eltype(x), size(x)) # zeros of the same type and size as `x` for i in length(a):-1:1 accumulator += x^(i-1) * a[i] # ! 1-based indexing for arrays end return accumulator end A = circlemat(10) # matrix of size 10x10 coeffs = ones(4) # coefficients of polynomial polynomial(coeffs, A) The other option is to use the more abstract version that we have defined to work with generators/iterators. For example polynomial(a, x) = sum(ia -> x^(ia[1]-1) * ia[2], enumerate(a)) works out of the box polynomial(coeffs, A) ==== General notes ==== * We did not care about the type just numeric values, no conversions were necessary. * ''ones(4)'' creates by default array of ''Float64'' to force it to ''Int'' you can specify the type in the first argument i.e. ''ones(Int, 4)'' * Next time please send us the hw in a zip file without any internal directories and with the properly named script * ''Project.toml'' and ''Manifest.toml'' should define the environment that needs to be active before running the ''hw.jl'' script * In the instructions to submission we have said to isolate the compulsory task, however we got many solutions that contained the plotting task as well, even though the solution has already been provided for that and there was no need to check it. * To shed some light on the evaluation process, we include your file into Julia with ''include("hw.jl")'' and run our set of test, which may be slightly different from what has been given in the assignment to asses the correctness of the individual functions. Therefore no interactivity is required and more importantly the functions have to be exported or otherwise made accessible in the Main module. * We appreciate the inclusion of docstrings in some of the solutions, even though no bonus points were awarded for it. It builds good habits. * As argument typing has not been explained that well so let's just summarize it quickly: # no argument types specified # will work as a fallback when no more concrete example is found function polynomial(a, x) accumulator = 0 for i in length(a):-1:1 accumulator += x^(i-1) * a[i] end return accumulator end # with this we are defining a special method for situations, where x is a matrix # multiple dispatch always tries to find most specified/concrete implementation for a given argument type function polynomial(a, x::AbstractMatrix) # equivalent to function polynomial(a, x::T) where T<:AbstractMatrix accumulator = zeros(size(x)) for i in length(a):-1:1 accumulator += x^(i-1) * a[i] end return accumulator end As you can see there is quite a bit of code duplication, thus in this case it is more suitable to design this differently, by taking the generic untyped function and introduce a initialization function in the place of ''accumulator''. function polynomial(a, x) accumulator = _init_zero(x) for i in length(a):-1:1 accumulator += x^(i-1) * a[i] end return accumulator end _init_zero(x) = 0; _init_zero(x::AbstractMatrix) = zeros(size(x)) This is still not an ideal solution, because it does not treat the ''eltype'' of the matrix or the ''typeof'' the scalar input ''x''. We will show in later lectures, what problems this "better" design brings. If you are interested this functionality is already built in Julia with ''zero'' and ''one'' functions. In one of the solutions, we have seen a type of dynamic dispatch based on an if condition function polynomial(a, x) if typeof(x) <: AbstractMatrix accumulator = zeros(size(x)) else accumulator = 0 end for i in length(a):-1:1 accumulator += x^(i-1) * a[i] end return accumulator end which essentially mimics the design with the ''_init_zero'' function above. In this simple case Julia will compile away the if condition, because for each of the argument type we get a different method and different if branch as well being compiled, however this is not a good design pattern, because new type of initialization requires changes in the original method. Note that this type of dynamic dispatch is often encountered in Python, where due to the interpretation nature of the language, the condition is always evaluated.