josesa@ucr.edu) or Jericho Lawson
(jlaws011@ucr.edu) if you have any questions, using “[STAT
107]” in the subject line.The usual (not element-wise) matrix multiplication \(C = A \cdot B\) is only defined when the number of rows from \(B\) match the number of columns from \(A\). For our purposes, let us say \(A\) has \(m\) rows and \(n\) columns and \(B\) has \(n\) rows and \(p\) columns. We can denote this in a simplified manner by writing \(A\in\mathbb{R}^{m\times n}\) and \(B\in\mathbb{R}^{n\times p}\). The outcome of matrix multiplication is another matrix \(C \in\mathbb{R}^{m\times p}\) which combines the entries of \(A\) and \(B\).
Matrix multiplication can be viewed in several ways:
The entry \((i,k)\) of \(C\), denoted by \(C_{ik}\) (\(i\)-th row, \(k\)-th column) is given by the scalar sum of products: \[C_{ik} = \sum_{j = 1}^{n} A_{ij} \cdot B_{jk}\]
The \(k\)-th column of the
product matrix \(C\) can be thought of
as the sum of \(A\)’s columns weighted
by the columns of \(B\) \[C_{,k} = \sum_{j=1}^{n} B_{jk} \cdot
A_{,j},\] where the notation \(C_{,k}\) is used to indicate column \(k\) to match R’s index notation
C[,k]. That is, \(C_{,k}\)
is a linear combination of \(A\)’s columns and the weights \(B_{jk}\) tell us how much to “mix” for each
column. Note that here we have a scalar-vector
product.
1(A) Write a function that implements Formula 1 as
given above. Call it matmul1(A, B). Your function should
take two arguments, A and B, and return their
matrix product C. You may assume that A and
B are compatible. Use only arithmetic operators and loops,
and avoid functions like sum() or apply().
This version requires the most for
loops.
matmul1= function(A,B) {
C= matrix(0, nrow= nrow(A), ncol= ncol(B))
for(i in 1: (nrow(A))){
for(k in 1:ncol(B)){
count=0
for(j in 1:ncol(A)){
count= count + A[i,j]*B[j,k]
}
C[i,k]= count
}
}
return(C)
}
A= matrix(c(1,2,3,4,5,6,7,8), nrow = 2, ncol = 4)
B= matrix(c(3,6,9,12,15,18,21,24), nrow = 4, ncol = 2)
matmul1(A,B)
## [,1] [,2]
## [1,] 150 342
## [2,] 180 420
A%*%B
## [,1] [,2]
## [1,] 150 342
## [2,] 180 420
1(B) Write a function that implements Formula 2 as
given above. Call it matmul2(A, B). Your function should
take two arguments, A and B, and return their
matrix product C. You may assume that A and
B are compatible. Use only arithmetic operators and loops,
and avoid functions like sum() or apply().
This version requires fewer for loops.
matmul2= function(A,B) {
C= matrix(0, nrow(A), ncol(B))
for(k in 1: ncol(B)){
count=0
for(j in 1: ncol(A)) {
count= count+ B[j,k]*A[,j]
}
C[,k]= count
}
return(C)
}
A= matrix(c(1,2,3,4,5,6,7,8), nrow = 2, ncol = 4)
B= matrix(c(3,6,9,12,15,18,21,24), nrow = 4, ncol = 2)
matmul2(A,B)
## [,1] [,2]
## [1,] 150 342
## [2,] 180 420
A%*%B
## [,1] [,2]
## [1,] 150 342
## [2,] 180 420
1(C) Now we are going to compare the computation
time of our functions matmul1 and matmul2 to
R’s implementation of matrix multiplication A %*% B. To
compare times, we measure time with the system.time()
function. To use system.time(), simply pass an R expression
as an argument to the function; for example
system.time(A %*% B) will report timing results for running
A %*% B.
Use the system.time() function to compare the
computation time for multiplying matrices with matmul1(),
matmul2(), and A %*% B and save the results in
a named list with names matmul1,
matmul2, and baseR, respectively. Display the
results as a data.frame object.
Look at the elapsed measurements. Which method was the
fastest for you? Which one was the slowest?
# DO NOT MODIFY
set.seed(1000)
A <- matrix(rnorm(1000 * 500), nrow = 1000, ncol = 500)
B <- matrix(rnorm(500 * 800), nrow = 500, ncol = 800)
# END DO NOT MODIFY
## Your code here:
##
##
as.data.frame(list(matmul1=system.time(matmul1(A,B)),matmul2= system.time(matmul2(A,B)), baseR= system.time(A%*%B)))
## matmul1 matmul2 baseR
## user.self 20.99 1.43 0.17
## sys.self 0.05 0.28 0.00
## elapsed 21.21 1.75 0.17
## user.child NA NA NA
## sys.child NA NA NA
Answer here: baseR is fastest, while matmul1 is the slowest computation speed.
while loops in iterative
algorithmsThe ancient Babylonians knew how to approximate the hypotenuse of a triangle, which involves computing the square root of a non-negative number. The Greek mathematician Heron would later define an explicit algorithm for computing the square root of an arbitrary non-nengative number \(c\), which is often attributed to the Babylonians for their geometric skills. The method turns out to be a special case of Newton’s method, a standard algorithm for minimizing/maximizing functions (e.g. \(\min f(x)\)). The so-called Babylonian method estimates square roots using the iterative recipe:
\[ x_{n+1} = \frac{1}{2} \left[x_{n} + \frac{c}{x_{n}}\right].\] This is an iterative algorithm in which the next iterate (left-hand side), \(x_{n+1}\), is defined by some function of the previous iterates (just \(x_{n}\) in this case) plus some additional problem data (such as the number, \(c\)).
Source: Babylonian Method (Wikipedia)
Credit: Algorithms from THE BOOK (Chapter 1, Section 3)
2(A) Write a function named my.sqrt()
that accepts four arguments, given in the following order:
my.sqrt= function(c,x0, maxiter=10, epsilon= .001){
count=0
xn= x0
eps= epsilon-.5
while(count<= maxiter & eps<epsilon) {
xn1= .5*(xn+(c/xn))
eps= xn1^(1/2)-c
xn= xn1
count=count+ 1
}
return(xn)
}
my.sqrt(c = 30,maxiter=500, x0= 3)
## [1] 5.477226
sqrt(30)
## [1] 5.477226
c, whose square root we wish to
compute,x0, which must be a positive
number,maxiter with default value 10, andepsilon with default value
0.001.This function should implement the Babylonian method, using a single
while loop. Use the following outline to guide you.
Before the while loop
Use x0 to initialize your current estimate of the
square root.
Decide how you’re going to exit the while loop. What
variables might you need to define?
Provide brief comments for any variables that you define here, as needed.
In the while loop
Implement the algorithm map using the formula above.
Check the convergence condition \(|x_{n+1}^2 - c| < \epsilon\). Here \(\epsilon\) is the Greek letter
corresponding to epsilon. There are many ways to do this
check, but make sure that you do not create an infinite loop by
accident, or exit early. If the convergence condition is
TRUE, you should exit the while
loop.
FALSE, you should continue the while
loop.
Check that you have not taken more than the maximum number of
iterations maxiter:
if TRUE, you should exit the while
loop.
if FALSE, you should continue the while
loop.
Update any additional variables that may need it.
After the while loop
Before finishing the function, print the number of iterations
performed by the algorithm. For example, if the algorithm iterated 5
times, print Iteration Total = 5.
The function must return our estimate of \(\sqrt{c}\).
2(B) [Convergence Check] Call your
function with c = pi^2, x0 = 1 and
maxiter = 100. The algorithm should finish in 5 steps.
my.sqrt= function(c,x0, maxiter=100, epsilon= .001){
count=0
xn= x0
eps= epsilon-.5
while(count<= maxiter & eps<epsilon) {
xn1= .5*(xn+(c/xn))
eps= xn1^(1/2)-c
xn= xn1
count=count+ 1
}
return(xn)
}
my.sqrt(c = pi^2,maxiter=100, x0= 1)
## [1] 3.141593
2(C) [Iterations Check] Call your
function with c = pi^2, x0 = 1, and
maxiter = 3. The algorithm should only take 3 steps.
my.sqrt(c = pi^2,maxiter=3, x0= 1)
## [1] 3.141757
2(D) Create a new function called
my.sqrt.iterates(), which has the same inputs as your
function my.sqrt(), but, instead of returning only the
estimator of \(\sqrt{c}\), it returns a
vector with all of the iterated values. For example, if for a given set
of inputs, the function performs 5 iterations, we must return a vector
x = c(x1, x2, x3, x4, x5).
2(E) Run
iterates <- my.sqrt.iterates(c = 400, x = 1000, maxit = 1000, epsilon = 0.001).
Then, create a convergence plot. The plot must include
points corresponding to each iteration step of our estimates of \(\sqrt{c}\), , a curve connecting the
points, and a horizontal red line at the true square root. Give
appropriate title, and labels to the plot:
"Iteration Index"."Iteration Value""Babylonian Method: Convergence"Add a legend on the top-right of the plot describing the lines and points of the iteration, and the horizontal red line.
The Fibonacci sequence is a classic example of a sequence of numbers \(\{F_0,F_1,F_2,F_3,...\}\) defined through a recursive formula. The sequence of numbers starts with \(F_0 = 0\), \(F_1 = 1\). The following numbers in the sequence are defined as: \[F_{t+2} = F_{t+1} + F_{t}.\] For example, \(F_2 = 2\), \(F_3 = 3\), \(F_4 = 5\), \(F_5 = 8\) and \(F_6 = 13\). In this exercise, you will create two functions that calculate the Fibonacci sequence.
3(A) Create a function my_fibonacci1(t)
that, for any positive type integer value t, it calculates
the Fibonacci value \(F_t\). To prevent
potential errors, use conditionals to verify that the input value
t is both an integer type, and positive. If the value is
not integer or not positive, print
"Error: input does not conform to required format" and
return -1.
If the input t is appropriate, move forward with the
calculation of the Fibonacci \(F_t\).
For this, start with \(F_1 = 1\), \(F_2 = 1\) and use a for() loop
to calculate the values \(F_3,F_4,...,F_{t-1},
a_t\) in sequence. Then, return the value \(F_t\).
To verify your function, save in a vector myfibo1 of
length 25 the Fibonacci numbers calculated with the function
my_fibonacci1() for the values
t = 1L,2L,3L,..., 25L. Print the vector
myfibo1.
3(B) Notice that, in the previous function, it was necessary to calculate all Fibonacci numbers from 1 to \(t\) to recover the value \(F_t\). This means that the time it requires to calculate \(t\) is increasing with respect to \(t\). Mathematicians in the XIX century realized that it was possible to provide a non-recursive formula for the Fibonacci sequence, that allowed them to recover the value of \(F_t\) without the need to calculate all of \(F_1,F_2,...,F_{t-1}\). The formula is given as follows: \[ F_t = \frac{1}{2} \left( \varphi^t - \big(-\frac{1}{\varphi}\big)^t \right),\] where the value \(\varphi\) is the golden ratio \(\varphi := \frac{1 + \sqrt{5}}{2}\).
Create a function named my_fibonacci2(t) that uses this
formula to calculate the Fibonacci numbers. First, just as in part 3(a),
do a check of whether the input is of type integer and positive. If the
conditions are met, apply the formula above to calculate the \(t\)-the Fibonacci number, and return
it.
To verify your function, save in a vector myfibo2 of
length 25 the Fibonacci numbers calculated with the function
my_fibonacci2() for the values
t = 1L,2L,3L,..., 25L. Print the vector
myfibo2.