'Why am I getting different answer for same algorithm on R studio desktop vs R studio cloud?

I run this simple code from class on my R studio desktop(M1 pro MacBook Pro) and the answer is different to the answer I get on R studio cloud. On the MacBook, I get 239.3093 vs 237.7821 on cloud. Am I missing something?

A = matrix(NA,nrow = 50,ncol = 50)
for (i in 1:50) {
  for (j in 1:50) {
    A[i,j] = sin(i) + cos(j)
  }
}

pivot.above = function(A, r, c) {
  A[r, ] = A[r, ]/A[r,c]
  for (i in (r-1):1) {
    A[i, ] = A[i, ]-A[i,c]*A[r, ]
  }
   A
}

for (i in 0:48) {
  j = 50-i
  A = pivot.above(A,j,j)
}

A[1, ] = A[1, ]/A[1,1]

print(sum(A))


Solution 1:[1]

This matrix has a very large condition number. From Wikipedia:

If the condition number is very large, then the matrix is said to be ill-conditioned. Practically, such a matrix is almost singular, and the computation of its inverse, or solution of a linear system of equations is prone to large numerical errors.

kappa(A)
[1] 1.352794e+19

This means that tiny numerical differences, such as the difference between compilers, system linear algebra libraries, operating systems, chips, etc., will lead to non-negligible differences in the results — as you found out.

As @DaveArmstrong commented, what the OP is doing here is not exactly either of the operations mentioned in the Wikipedia article (inversion or solution of a linear system); we're reducing the matrix to lower triangular form by Gaussian elimination. However, they're closely related (once we've reduced the matrix to triangular form, we've done most of the work of solving a linear system, because we can now use simple back-substitution). I don't know offhand why the sum of the first column of the reduced matrix should be a quantity that is especially sensitive, but it's not surprising to me that the condition number is related to this sensitivity. In these notes, Luke Tierney (an R-core member ...) describes why simple Gaussian elimination is unstable, and why a more sophisticated partial pivoting approach is more stable.

This is a perfectly reasonable question to ask, but is also something you're going to have to learn about/get used to as you move ahead with numerical computation. It would actually be a great thing to ask your instructor about.

Solution 2:[2]

Apologies that this isn't really an answer, but perhaps it's somewhere to start. In response to Ben's response to my question, I understand that the algorithm isn't just adding numbers, but so far as I can see, it is just vectorized scalar arithmetic (addition, subtraction, multiplication, division) - there are no matrix operations going on. The fact that the inputs and outputs are organized in a matrix is incidental. Consider what I think is an equivalent set of operations for data organized in a rectangular data frame rather than a square matrix.

B <- expand.grid(
  row = 1:50, 
  col = 1:50)

B <- B %>% 
  mutate(A = sin(row) + cos(col))

I rewrote the pivot.above function to work for this long-form data:

pivot.above.long <- function(A, rowind, colind){
  A$A[which(A$row == rowind)]  <- A$A[which(A$row == rowind)]/A$A[which(A$row == rowind & A$col == colind)]
  for(i in (rowind - 1):1){
    A$A[which(A$row == i)] = A$A[which(A$row == i)]-A$A[which(A$row == i & A$col == colind)]*A$A[which(A$row == rowind)]
  }
  A
}

Then, I applied that to the long-form data:

for (i in 0:48) {
  j = 50-i
  B = pivot.above.long(B,j,j)
}

B$A[which(B$row == 1)] <- B$A[which(B$row == 1)]/B$A[which(B$row == 1 & B$col == 1)]

And, I get exactly the same result as I got from the matrix operations - one of the two values returned in the original question.

print(sum(B$A))
# [1] 239.3093

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 DaveArmstrong