Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

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.

courses/b0m36spj/tutorials/hw01.txt · Last modified: 2021/10/12 15:34 by francja5