Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent results with complex matrices #55

Open
eliocamp opened this issue Nov 2, 2020 · 2 comments
Open

Inconsistent results with complex matrices #55

eliocamp opened this issue Nov 2, 2020 · 2 comments

Comments

@eliocamp
Copy link

eliocamp commented Nov 2, 2020

I'm using {irlba} to perform svd on complex matrices and found that the results where inconsistent. In my real code, I had an issue that the resulting principal values depended on the order of the rows. Trying to create a reproducible example, I realised that the solution was inconsistent between different calls even if the matrix remained the same

set.seed(42)
N <- 10
M <- 40

m <- matrix(complex(real = rnorm(M*N), imaginary = rnorm(M*N)),
             ncol = N, nrow = M)

first_pc <- function(matrix, package) {
  if (package == "svd") {
    s <- svd(matrix, nu = 2)  
  } else {
    s <- suppressWarnings(irlba::irlba(matrix, nu = 2, rng = runif) )
  }

  s$v[, 1]
}

Plotting the first of several calls to irlba::irlba, the results change considerably!

plot(first_pc(m, "irlba"), type = "l")
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))

Using base::svd, the results are always the same:

plot(-first_pc(m, "svd"), type = "l")
lines(-first_pc(m, "svd"))
lines(-first_pc(m, "svd"))
lines(-first_pc(m, "svd"))

With real values, this doesn't happen:

m <- matrix(rnorm(M*N),
            ncol = N, nrow = M)

plot(first_pc(m, "irlba"), type = "l")
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))

Created on 2020-11-02 by the reprex package (v0.3.0)

@bwlewis
Copy link
Owner

bwlewis commented Nov 4, 2020 via email

@eliocamp
Copy link
Author

eliocamp commented Dec 7, 2021

Hi, I've been having issues with this for a while and I've just now realised that this is not necessarily a bug. It's just that complex SVDs are undefined up to a rotation angle!

set.seed(42)
N <- 10
M <- 40

m <- matrix(complex(real = rnorm(M*N), imaginary = rnorm(M*N)),
           ncol = N, nrow = M)

# Baseline svd
base <- irlba::irlba(m, 2)


rotate <- function(z, angle = 0) {
  complex(real = cos(angle), imaginary = sin(angle)) * z
}

first_pc <- function(matrix, package) {
  if (package == "svd") {
     s <- svd(matrix, nu = 2)  
  } else {
     s <- suppressWarnings(irlba::irlba(matrix, nu = 2))
  }
  
  # Rotate v so that is has the maximum corraltion with 
  # the baseline value. 
  angles <- seq(0, 2*pi, length.out = 80)
  
  cors <- vapply(angles, function(angle) {
     v <- rotate(s$v[, 1], angle)
     cor(Re(v), Re(base$v[, 1]))
  }, FUN.VALUE = 1)
  
  rotate(s$v[, 1], angles[which.max(cors)])
}

plot(first_pc(m, "irlba"), type = "l")
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))
lines(first_pc(m, "irlba"))

Created on 2021-12-07 by the reprex package (v2.0.1)

I don't know if there's anything that can make the algorithm more deterministic, in that it returns always the same svd decomposition for the same input (like base::svd() does), but in any case, this is just a property of complex SVD; it's just not unique. ¯_(ツ)_/¯

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants