2

I can't find documentation as to the exact operation of this function. I have a QR factorization of a matrix X:

X = matrix(c(1,1,1,1,-1.5,-0.5,0.5,1.5,2.25,0.25,0.25, 2.25,-3.275,-0.125,0.125,3.375), 
nrow=4, byrow=F)

     [,1] [,2] [,3]   [,4]
[1,]    1 -1.5 2.25 -3.375
[2,]    1 -0.5 0.25 -0.125
[3,]    1  0.5 0.25  0.125
[4,]    1  1.5 2.25  3.375

The function qr(X) yields a list:

$qr (rounding output)
     [,1]       [,2]          [,3]          [,4]
[1,] -2.0          0          -2.5             0
[2,]  0.5     -2.236             0        -4.583
[3,]  0.5      0.447             2             0 
[4,]  0.5      0.894        -0.929        -1.341

$rank
[1] 4

$qraux
[1] 1.500000 1.000000 1.368524 1.341641

$pivot
[1] 1 2 3 4

attr(,"class")
[1] "qr"

I select the diagonal elements of qr(X)$qr, which I name z:

z = qr(X)$qr
z = Q * (row(Q) == col(Q))

     [,1]      [,2] [,3]      [,4]
[1,]   -2  0.000000    0  0.000000
[2,]    0 -2.236068    0  0.000000
[3,]    0  0.000000    2  0.000000
[4,]    0  0.000000    0 -1.341641

So far, so good. Now the next call I don't understand:

(raw = qr.qy(qr(X), z))

     [,1] [,2] [,3] [,4]
[1,]    1 -1.5    1 -0.3
[2,]    1 -0.5   -1  0.9
[3,]    1  0.5   -1 -0.9
[4,]    1  1.5    1  0.3

MAKING SOME PROGRESS:

So, thanks to the answer and some reading, I think that the object qr(X)$qr contains R completely in the upper triangle:

     [,1]       [,2]          [,3]          [,4]
[1,] -2.0          0          -2.5             0
[2,]          -2.236             0        -4.583
[3,]                             2             0 
[4,]                                      -1.341

The lower triangle of qr(X)$qr contains information about Q:

     [,1]       [,2]          [,3]          [,4]
[1,]                                            
[2,]  0.5                                       
[3,]  0.5      0.447                             
[4,]  0.5      0.894        -0.929      

Somehow calling qr.Q(qr(X)) returns Q using internally the function qr.qy() with qr() and a diagonal matrix of 1's as inputs.

But how is this operation carried out? How is the rest of the right upper corner of Q get filled? I think it makes use of $qraux, but how does it get to:

     [,1]       [,2] [,3]       [,4]
[1,] -0.5  0.6708204  0.5  0.2236068
[2,] -0.5  0.2236068 -0.5 -0.6708204
[3,] -0.5 -0.2236068 -0.5  0.6708204
[4,] -0.5 -0.6708204  0.5 -0.2236068

In short, How does qr.qy() work specifically?

I just found this: "qy.qr(): return the results of the matrix multiplications: Q %*% y, where Q is the order-nrow(x) orthogonal (or unitary) transformation represented by qr."

Antoni Parellada
  • 4,253
  • 6
  • 49
  • 114
  • You seem to think that the matrix `qr(X)$qr` is the `Q` matrix. It is not. It is a compact representation of the Q matrix. If you want the actual Q matrix use the `qr.Q` function. See the documentation; there is a link to `qr.Q` on the help page for the `qr` function. Just do `Q <- qr.Q(qr(X))` instead of what you are doing and then do `Q %*% z`. – Bhas Apr 09 '16 at 09:59
  • No it doesn't generate the `Q`. It uses special lapack routines to do the multiplication without generating an explicit `Q`. – Bhas Apr 09 '16 at 18:09
  • Thank you. So when you say that it doesn't generate Q, you mean that `qr.qy()` does not generate Q, correct? So it sort of bypasses Q altogether, but the result after it multiplies Q and z is identical as if it had used Q? Is there any way I could convince you to write a formal answer? – Antoni Parellada Apr 09 '16 at 19:13
  • You seem to have edited the original question in such a way that `Q` is no longer generated. Shouldn't `z = qr(X)$qr` actually be `Q = qr(X)$qr`? I'll see what I can do about composing a formal answer without making it too long. – Bhas Apr 10 '16 at 06:40

1 Answers1

6

The matrix Q from a QR decomposition is only implicit in the return value of the function qr. The list element qr is a compact representation of the Q matrix; it is contained in the lower triangular part of list element qr and in the vector qraux. The upper triangular matrix R of a QR decomposition is the upper triangular part of the list element qr in the return value.

The R function qr.qy after some intermediate steps eventually calls the Lapack subroutine dormqr, which does NOT generate the Q matrix explicitly. It uses the information contained in list elements qr and qraux. See http://www.netlib.org/lapack/explore-html/da/d82/dormqr_8f.html .

So qr.qy does NOT transform the compact form of Q into the actual Q. It uses the compact form to compute Q %*% z.

The R function qr.Q (which you have done) uses qr.qy with a diagonal matrix with 1's on the diagonals to generate Q.

Why is it done like this? For efficiency reasons.

With the following code you can check this:

library(rbenchmark)

benchmark(qr.qy(XQR,z), {Q <- qr.Q(qr(X)); Q %*% z},  { Q %*% z},
          replications=10000, 
          columns=c("test","replications","elapsed","relative")  )

with output

                                     test replications elapsed relative
3                           {    Q %*% z}        10000   0.022    1.000
2       {    Q <- qr.Q(qr(X));   Q %*% z}        10000   0.486   22.091
1                           qr.qy(XQR, z)        10000   0.152    6.909

Lesson: only generate Q if you really need it in explicit form and if you need to generate it many times with different input matrices. If Q is fixed and doesn't change then you can use Q %*% z.

Bhas
  • 1,844
  • 1
  • 11
  • 9
  • +1 Thank you very much. I didn't realize you had gone ahead and answered my question. Do you happen to have any references or possibly some additional words... I'm trying really hard to find out how to generate this "compact Q" matrix. – Antoni Parellada Apr 11 '16 at 00:27
  • For instance, I wonder if [this book](https://books.google.com/books?id=iIOhAwAAQBAJ&pg=PA241&lpg=PA241&dq=linear+algebra+and+matrix+analysis+for+statistics+reflectors&source=bl&ots=eZDhKU8BJO&sig=MPCJuWXpWSBJC9_ohHBI6UX-xJ0&hl=en&sa=X&ved=0ahUKEwjshNX9p4XMAhUEVT4KHRzkDPQQ6AEIMTAD#v=onepage&q=reflectors&f=false) contains a good explanation of these "reflectors"... – Antoni Parellada Apr 11 '16 at 00:30
  • For instance, and just to focus my study, are any of the matrices (T or V,...) mentioned [here](http://www.netlib.org/lapack/lug/node69.html) the "compact form" that you make reference to. The link you include is so code heavy that I can't follow. – Antoni Parellada Apr 11 '16 at 00:40
  • More or less. See [Wikipedia QR](https://en.wikipedia.org/wiki/QR_decomposition) for explanation and references. Any good book on linear algebra should give an explanation. E.g. Golub and van Loan: Matrix Computations. The link I included is a link to the Lapack code which is what R is using. – Bhas Apr 11 '16 at 05:47
  • I posted a bounty in the hope that I would entice you to add additonal computational/mathematical detail. I have articles, looked at your links, etc. but I'm still working towards it... – Antoni Parellada Apr 12 '16 at 16:50
  • The QR-factorization used in lm() (linear regression estimate) is a FORTRAN subroutine. So it is clear why the binding of FORTRAN-libraries is essential for the reliability of R: R can use the long tested subroutines of FORTRAN (as also other number crunchers). http://stackoverflow.com/questions/19226816/how-can-i-view-the-source-code-for-a-function – jogo Apr 15 '16 at 19:16
  • @jogo Can you give an example where qr.qy() would be used? – Antoni Parellada Apr 17 '16 at 23:51
  • @AntoniParellada To understand how qr.qy() intern works, you have to look at the QR-decomposition itself https://svn.r-project.org/R/trunk/src/appl/dqrdc2.f In 1978 memory was expensive, so the QR-decomposition tries to work inplace. Not the matrix Q is stored but vectors, which allow to reconstruct Q. These vectors have some values 0. This property is used to store the triangle R and the vectors in one rectangular matrix. – jogo Apr 19 '16 at 07:22
  • @jogo The historical reference is very enlightening. Please note that I have made inroads into understanding the general concept, as in [this post](http://stats.stackexchange.com/a/207128/67822). I was looking for a similar dissection exercise on specifically qr.qy(). – Antoni Parellada Apr 19 '16 at 13:38