wrathematics / float

Single precision (float) matrices for R.
Other
46 stars 12 forks source link

qr.Q(float32) returns numeric #27

Closed dselivanov closed 6 years ago

dselivanov commented 6 years ago

Hi here. Seems qr.Q on float32 matrix returns double matrix. Not sure if it is hard to calculate and integrate properly, but would be nice at least to convert it from double to float before returning (in order to have type-safe calculations).

wrathematics commented 6 years ago

Can you give an example code that's breaking? It should return a float:

library(float)
set.seed(1234)

x = flrnorm(10, 3)
QR = qr(x)
qr.Q(QR)
## # A float32 matrix: 10x3
##              [,1]        [,2]         [,3]
##  [1,] -0.14802432  0.39461002 -0.075990766
##  [2,] -0.30969775 -0.04993335 -0.263598472
##  [3,] -0.24079260 -0.31019425 -0.156482309
##  [4,]  0.01999501  0.73036253  0.008359464
##  [5,]  0.29763341 -0.16934457 -0.210273698
##  [6,] -0.03421037 -0.15402134 -0.614522934
##  [7,] -0.15851428  0.19833231  0.174357086
##  [8,] -0.28265125  0.20427391 -0.568394244
##  [9,] -0.25968921  0.20711732 -0.111747116
## [10,]  0.74938786  0.18915167 -0.335218400
dselivanov commented 6 years ago

Sorry for not providing it - it would help me to understand that there was a mistake on my side!

I think this happened because I've not loaded float and R dispatched qr.Q from base:

U = float::flrnorm(10, 3)
U = qr.Q(float::qr(U))
# num [1:10, 1:3] -0.0473 0.0692 0.5123 -0.0916 0.015 ...

However I'm curious now why the remaining S4 methods worked properly.

wrathematics commented 6 years ago

This is a very interesting example. Basically it's defaulting to base::qr.Q(), which constructs a numeric Dvec (when the Dvec argument is missing). In float, I always cast things to the highest precision, so once it hits qr.qy() it goes to float::qr.qy() which eventually promotes to double. Kind of amusing that it even works.

A quick fix is to call float::qr.Q(). This will work even if the input is numeric.

set.seed(1234)
s = float::flrnorm(3, 3)
x = as.numeric(s)
float::qr.Q(float::qr(x))
##            [,1]        [,2]        [,3]
## [1,] -0.7332419 -0.65954086 -0.16541508
## [2,]  0.1685267  0.05940792 -0.98390521
## [3,]  0.6587527 -0.74931739  0.06758985
float::qr.Q(float::qr(s))
## # A float32 matrix: 3x3
##            [,1]       [,2]        [,3]
## [1,] -0.9622459 -0.2717418  0.01546986
## [2,]  0.1760344 -0.6646785 -0.72609526
## [3,]  0.2075929 -0.6959589  0.68742007
dselivanov commented 6 years ago

Thank you for explanation. But why did remaining S4 method work? (for example %*%, diag, etc) Can't find any good reference on this...

wrathematics commented 6 years ago

I wouldn't think that diag() would work. %*% should because it's generic. qr() is generic, but qr.Q() isn't, so my guess is it searches the methods table for the former, but not the latter. If you install float from source, you'll see a message like

Creating a generic function for ‘qr.Q’ from package ‘base’ in package ‘float’

So that method will only be available if the full float namespace is attached, I guess.

wrathematics commented 6 years ago

I'm not 100% sure I know what's happening, but I think that explanation is in the ballpark of being correct.

dselivanov commented 6 years ago

Thanks a lot! I think in my case it will be better to load float - otherwise will be many non-obvious edge cases.