JuliaLang / julia

The Julia Programming Language
https://julialang.org/
MIT License
45.59k stars 5.48k forks source link

Rank() returns different values on Windows and Linux for a (230,3) float array #48058

Closed JustinSGray closed 1 year ago

JustinSGray commented 1 year ago

test script:

using LinearAlgebra

arc_points = [1.899999976158142 0.4975919766115289 1.5893374977125878; 1.899999976158142 0.49722218278655056 1.5897410451080904; 1.899999976158142 0.4975919766115289 1.5893374977125878; 1.899999976158142 0.4979579315923174 1.5889381395584135; 1.899999976158142 0.49684837728690256 1.5901489703508018; 1.899999976158142 0.49722218278655056 1.5897410451080904; 1.899999976158142 0.4979579315923174 1.5889381395584135; 1.899999976158142 0.49832012931058217 1.5885428816173934; 1.899999976158142 0.496770705830528 1.590233731403604; 1.899999976158142 0.49684837728690256 1.5901489703508018; 1.899999976158142 0.4986787401376044 1.5881515379673141; 1.899999976158142 0.49832012931058217 1.5885428816173934; 1.899999976158142 0.4986787401376044 1.5881515379673141; 1.899999976158142 0.4990337942088668 1.587764075722024; 1.899999976158142 0.4990337942088668 1.587764075722024; 1.899999976158142 0.49938539270214155 1.5873803844685748; 1.899999976158142 0.49973371354645546 1.5870002700371604; 1.899999976158142 0.49938539270214155 1.5873803844685748; 1.899999976158142 0.49973371354645546 1.5870002700371604; 1.899999976158142 0.5000788016261712 1.5866236834465204; 1.899999976158142 0.5000788016261712 1.5866236834465204; 1.899999976158142 0.5004207741619771 1.5862504967764457; 1.899999976158142 0.5007597451172834 1.5858805856613183; 1.899999976158142 0.5004207741619771 1.5862504967764457; 1.899999976158142 0.5007597451172834 1.5858805856613183; 1.899999976158142 0.501095773380899 1.585513885837152; 1.899999976158142 0.501095773380899 1.585513885837152; 1.899999976158142 0.501428929555806 1.5851503202565551; 1.899999976158142 0.5017593342303803 1.5847897573242395; 1.899999976158142 0.501428929555806 1.5851503202565551; 1.899999976158142 0.5017593342303803 1.5847897573242395; 1.899999976158142 0.5020870181523167 1.5844321634859615; 1.899999976158142 0.5020870181523167 1.5844321634859615; 1.899999976158142 0.5024121444044118 1.5840773607732601; 1.899999976158142 0.5027347539210644 1.5837253045153787; 1.899999976158142 0.5024121444044118 1.5840773607732601; 1.899999976158142 0.5027347539210644 1.5837253045153787; 1.899999976158142 0.5030549196506816 1.5833759151054183; 1.899999976158142 0.5030549196506816 1.5833759151054183; 1.899999976158142 0.5033727315155595 1.5830290944132694; 1.899999976158142 0.5036882631379038 1.5826847620967328; 1.899999976158142 0.5033727315155595 1.5830290944132694; 1.899999976158142 0.5036882631379038 1.5826847620967328; 1.899999976158142 0.5040015705456986 1.5823428570137728; 1.899999976158142 0.5040015705456986 1.5823428570137728; 1.899999976158142 0.5043126976789617 1.582003331213671; 1.899999976158142 0.504621777086214 1.5816660400493106; 1.899999976158142 0.5043126976789617 1.582003331213671; 1.899999976158142 0.504621777086214 1.5816660400493106; 1.899999976158142 0.5049288520292333 1.5813309363101207; 1.899999976158142 0.5049288520292333 1.5813309363101207; 1.899999976158142 0.505233980591305 1.5809979566111636; 1.899999976158142 0.5055372209533721 1.58066703746093; 1.899999976158142 0.505233980591305 1.5809979566111636; 1.899999976158142 0.5055372209533721 1.58066703746093; 1.899999976158142 0.5058386309694426 1.5803381157246874; 1.899999976158142 0.5058386309694426 1.5803381157246874; 1.899999976158142 0.5061383044705505 1.580011089006812; 1.899999976158142 0.5064362693818132 1.579685926833273; 1.899999976158142 0.5061383044705505 1.580011089006812; 1.899999976158142 0.5064362693818132 1.579685926833273; 1.899999976158142 0.5067326155631819 1.5793625311419968; 1.899999976158142 0.5067326155631819 1.5793625311419968; 1.899999976158142 0.5070273800331048 1.5790408615356126; 1.899999976158142 0.5073206211287764 1.5787208543520974; 1.899999976158142 0.5070273800331048 1.5790408615356126; 1.899999976158142 0.5073206211287764 1.5787208543520974; 1.899999976158142 0.5076123858777252 1.5784024582714038; 1.899999976158142 0.5076123858777252 1.5784024582714038; 1.899999976158142 0.5079027559000793 1.5780855842233856; 1.899999976158142 0.508191778335036 1.5777701807661335; 1.899999976158142 0.5079027559000793 1.5780855842233856; 1.899999976158142 0.508191778335036 1.5777701807661335; 1.899999976158142 0.5084794872675191 1.5774562107035637; 1.899999976158142 0.5084794872675191 1.5774562107035637; 1.899999976158142 0.5087659565033167 1.5771435934931368; 1.899999976158142 0.5090512211365765 1.5768322908374266; 1.899999976158142 0.5087659565033167 1.5771435934931368; 1.899999976158142 0.5090512211365765 1.5768322908374266; 1.899999976158142 0.5093353319855672 1.5765222472796387; 1.899999976158142 0.5093353319855672 1.5765222472796387; 1.899999976158142 0.509618351194475 1.5762133950032693; 1.899999976158142 0.5099003081610008 1.575905701927291; 1.899999976158142 0.509618351194475 1.5762133950032693; 1.899999976158142 0.5099003081610008 1.575905701927291; 1.899999976158142 0.5101812860958431 1.5755990772458055; 1.899999976158142 0.5101812860958431 1.5755990772458055; 1.899999976158142 0.5104613116974854 1.5752934918233787; 1.899999976158142 0.510740432188988 1.574988894126584; 1.899999976158142 0.5104613116974854 1.5752934918233787; 1.899999976158142 0.510740432188988 1.574988894126584; 1.899999976158142 0.5110186770758169 1.5746852519567929; 1.899999976158142 0.5110186770758169 1.5746852519567929; 1.899999976158142 0.511296130066264 1.574382473965095; 1.899999976158142 0.5115728016679068 1.5740805486848144; 1.899999976158142 0.511296130066264 1.574382473965095; 1.899999976158142 0.5115728016679068 1.5740805486848144; 1.899999976158142 0.5118487618803075 1.573779399727059; 1.899999976158142 0.5118487618803075 1.573779399727059; 1.899999976158142 0.5121240416173582 1.573478993356217; 1.899999976158142 0.5123987034121146 1.5731792613314204; 1.899999976158142 0.5121240416173582 1.573478993356217; 1.899999976158142 0.5123987034121146 1.5731792613314204; 1.899999976158142 0.5126727613099775 1.5728801883252501; 1.899999976158142 0.5126727613099775 1.5728801883252501; 1.899999976158142 0.5129462869463884 1.5725816961636156; 1.899999976158142 0.5132192929527638 1.5722837710621465; 1.899999976158142 0.5129462869463884 1.5725816961636156; 1.899999976158142 0.5132192929527638 1.5722837710621465; 1.899999976158142 0.5134918634763089 1.5719863211929554; 1.899999976158142 0.5134918634763089 1.5719863211929554; 1.899999976158142 0.5137639957583512 1.5716893495665178; 1.899999976158142 0.5140357602064581 1.571392779348694; 1.899999976158142 0.5137639957583512 1.5716893495665178; 1.899999976158142 0.5140357602064581 1.571392779348694; 1.899999976158142 0.5143072086673981 1.571096553960312; 1.899999976158142 0.5143072086673981 1.571096553960312; 1.899999976158142 0.5145783517336386 1.5708006618420582; 1.899999976158142 0.5148492480792657 1.5705050389642685; 1.899999976158142 0.5145783517336386 1.5708006618420582; 1.899999976158142 0.5148492480792657 1.5705050389642685; 1.899999976158142 0.5151199419021083 1.5702096370948802; 1.899999976158142 0.5151199419021083 1.5702096370948802; 1.899999976158142 0.5153904734551678 1.5699144123067301; 1.899999976158142 0.5156608757479939 1.5696193285772646; 1.899999976158142 0.5153904734551678 1.5699144123067301; 1.899999976158142 0.5156608757479939 1.5696193285772646; 1.899999976158142 0.5159312004890759 1.5693243294782127; 1.899999976158142 0.5159312004890759 1.5693243294782127; 1.899999976158142 0.5162014838277715 1.5690293755606215; 1.899999976158142 0.5164717576649855 1.5687344320117762; 1.899999976158142 0.5162014838277715 1.5690293755606215; 1.899999976158142 0.5164717576649855 1.5687344320117762; 1.899999976158142 0.5167420940232382 1.5684394202351761; 1.899999976158142 0.5167420940232382 1.5684394202351761; 1.899999976158142 0.517012495531948 1.5681443373613986; 1.899999976158142 0.5172830152617092 1.5678491254757387; 1.899999976158142 0.517012495531948 1.5681443373613986; 1.899999976158142 0.5172830152617092 1.5678491254757387; 1.899999976158142 0.5175537072456601 1.5675537256130903; 1.899999976158142 0.5175537072456601 1.5675537256130903; 1.899999976158142 1.1203459582111193 0.9097405700782359; 1.899999976158142 1.1203459582111193 0.9097405700782359; 1.899999976158142 1.5217249952094916 0.47172496773670924; 1.899999976158142 1.5217249952094916 0.47172496773670924; 1.899999976158142 1.5223785233907974 0.47101178764110074; 1.899999976158142 1.5230326315916636 0.4702979745836438; 1.899999976158142 1.5223785233907974 0.47101178764110074; 1.899999976158142 1.5230326315916636 0.4702979745836438; 1.899999976158142 1.5236874511356935 0.46958338525394616; 1.899999976158142 1.5236874511356935 0.46958338525394616; 1.899999976158142 1.5243431118088497 0.4688678780196077; 1.899999976158142 1.5249996215674866 0.4681514441980438; 1.899999976158142 1.5243431118088497 0.4688678780196077; 1.899999976158142 1.5249996215674866 0.4681514441980438; 1.899999976158142 1.5256571573568842 0.4674338906929879; 1.899999976158142 1.5256571573568842 0.4674338906929879; 1.899999976158142 1.526315799368484 0.4667151299933865; 1.899999976158142 1.5269756386526734 0.4659950627380703; 1.899999976158142 1.526315799368484 0.4667151299933865; 1.899999976158142 1.5269756386526734 0.4659950627380703; 1.899999976158142 1.5276368048652744 0.46527354743665855; 1.899999976158142 1.5276368048652744 0.46527354743665855; 1.899999976158142 1.5282993826454043 0.4645504917244517; 1.899999976158142 1.5289634749146277 0.4638257832855419; 1.899999976158142 1.5282993826454043 0.4645504917244517; 1.899999976158142 1.5289634749146277 0.4638257832855419; 1.899999976158142 1.5296291949124312 0.4630992985443125; 1.899999976158142 1.5296291949124312 0.4630992985443125; 1.899999976158142 1.5302966921928287 0.4623708742959483; 1.899999976158142 1.5309659987176603 0.46164047566123734; 1.899999976158142 1.5302966921928287 0.4623708742959483; 1.899999976158142 1.5309659987176603 0.46164047566123734; 1.899999976158142 1.5316372484758363 0.4609079564211997; 1.899999976158142 1.5316372484758363 0.4609079564211997; 1.899999976158142 1.532310622168726 0.46017311938063665; 1.899999976158142 1.5329861204384028 0.45943596383886776; 1.899999976158142 1.532310622168726 0.46017311938063665; 1.899999976158142 1.5329861204384028 0.45943596383886776; 1.899999976158142 1.5336639594577852 0.4586962538914183; 1.899999976158142 1.5336639594577852 0.4586962538914183; 1.899999976158142 1.5343441791022234 0.45795394602324646; 1.899999976158142 1.5350269662858986 0.45720883625925585; 1.899999976158142 1.5343441791022234 0.45795394602324646; 1.899999976158142 1.5350269662858986 0.45720883625925585; 1.899999976158142 1.535712365592254 0.4564608759465728; 1.899999976158142 1.535712365592254 0.4564608759465728; 1.899999976158142 1.53640054289454 0.45570988407157936; 1.899999976158142 1.5370915860763195 0.45495576472898797; 1.899999976158142 1.53640054289454 0.45570988407157936; 1.899999976158142 1.5370915860763195 0.45495576472898797; 1.899999976158142 1.5377856814361026 0.45419831461557025; 1.899999976158142 1.5377856814361026 0.45419831461557025; 1.899999976158142 1.538482867276464 0.453437491932618; 1.899999976158142 1.539183366004614 0.45267305397231844; 1.899999976158142 1.538482867276464 0.453437491932618; 1.899999976158142 1.539183366004614 0.45267305397231844; 1.899999976158142 1.539887265103275 0.45190490526681226; 1.899999976158142 1.539887265103275 0.45190490526681226; 1.899999976158142 1.5405946972723221 0.4511329010038136; 1.899999976158142 1.541305744280264 0.4503569519512525; 1.899999976158142 1.5405946972723221 0.4511329010038136; 1.899999976158142 1.541305744280264 0.4503569519512525; 1.899999976158142 1.5420206313022784 0.4495768123806974; 1.899999976158142 1.5420206313022784 0.4495768123806974; 1.899999976158142 1.542739448739834 0.44879238363912893; 1.899999976158142 1.5434623706625012 0.44800347576847643; 1.899999976158142 1.542739448739834 0.44879238363912893; 1.899999976158142 1.5434623706625012 0.44800347576847643; 1.899999976158142 1.5441895435380169 0.4472099289319068; 1.899999976158142 1.5441895435380169 0.4472099289319068; 1.899999976158142 1.5449210748191282 0.4464116258687377; 1.899999976158142 1.5456571713803235 0.44560834082165446; 1.899999976158142 1.5449210748191282 0.4464116258687377; 1.899999976158142 1.5456571713803235 0.44560834082165446; 1.899999976158142 1.546397940100025 0.4447999571567221; 1.899999976158142 1.546397940100025 0.4447999571567221; 1.899999976158142 1.5471435220038123 0.4439863209760073; 1.899999976158142 1.5478941474540755 0.44316718089039486; 1.899999976158142 1.5471435220038123 0.4439863209760073; 1.899999976158142 1.5478941474540755 0.44316718089039486; 1.899999976158142 1.5486499596580996 0.44234238062110703; 1.899999976158142 1.5486499596580996 0.44234238062110703; 1.899999976158142 1.54941113193556 0.441511731028416; 1.899999976158142 1.5501777839642121 0.4406751015107717; 1.899999976158142 1.54941113193556 0.441511731028416; 1.899999976158142 1.5501777839642121 0.4406751015107717; 1.899999976158142 1.5509501726337283 0.4398322117304534; 1.899999976158142 1.5509501726337283 0.4398322117304534; 1.899999976158142 1.5513346791267395 0.43941260874271393]

@show rank(arc_points)

Windows Result:

rank(arc_points) = 3
3

julia> versioninfo()
Julia Version 1.8.3
Commit 0434deb161 (2022-11-14 20:14 UTC)
Platform Info:
  OS: Windows (x86_64-w64-mingw32)
  CPU: 16 × Intel(R) Core(TM) i7-10875H CPU @ 2.30GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, skylake)
  Threads: 1 on 16 virtual cores
Environment:
  JULIA_PKG_USE_CLI_GIT = true

Linux Result:

rank(arc_points) = 2

NAME="Zorin OS"
VERSION="16.1"
ID=zorin
ID_LIKE=ubuntu
PRETTY_NAME="Zorin OS 16.1"
VERSION_ID="16"

Julia Version 1.8.2
Commit 36034abf260 (2022-09-29 15:21 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 16 × AMD Ryzen 7 2700 Eight-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-13.0.1 (ORCJIT, znver1)
  Threads: 1 on 16 virtual cores
oscardssmith commented 1 year ago

I don't believe this is surprising. rank is not well defined numerically so different BLAS can give different results.

LilithHafner commented 1 year ago

rank appears to have a precise mathematical definition in its docstring. I imagine that implementing that definition exactly might be computationally difficult or impractical, though. As rank is currently documented, I would call this a bug.

dkarrasch commented 1 year ago

@JustinSGray Could you please post the results of

(minimum(svdvals(A)), 3*eps(eltype(A)))

for A = arc_points? On my Mac, I get that the first divided by the second yields 4.5.

JustinSGray commented 1 year ago

I get

On windows.


(2.5448910415241994e-14, 6.661338147750939e-16)

julia> first/second
38.20390115435632```

One Linux, I get `4.500860108262188`
gbaraldi commented 1 year ago

This is just a case of floating point differences on CPUs/OSs. Both values are very close to 0 there, and something like rref from RowEchelon says the matrix is rank 3, even on linux for me.

LilithHafner commented 1 year ago

Functions returning integers don't have to have floating point rounding errors because the mathematically correct answer is always either representable as an integer or too large and an error is thrown.rank is one of these functions, and its semantics could, in theory, be implemented in a floating-point-error free way (e.g. using extended precision). Every input to rank has a single mathematically correct and exactly representable answer (though it might be hard to find due to #48098) and we do not always produce that answer.

It would be great if someone could figure out how to efficiently produce the correct answer but failing that, this limitation should be documented.

oscardssmith commented 1 year ago

@LilithHafner this isn't practically true. rank just isn't an especially meaningful function in a numerical linear algebra context. For example, in exact arithmatic rank([2 3; .2 .3]) is 2 (since .2*3/2 isn't exactly .3 but that doesn't make 2 a useful answer here. Computing the "exact" rank requires exponentially many bits (so it's impossible for matrices >30x30 or so) and isn't a useful concept anyway.

LilithHafner commented 1 year ago

This is the useful and mathematically unambiguous definition according to the docstring:

Compute the rank of a matrix by counting how many singular values of A have magnitude greater than max(atol, rtol*σ₁) where σ₁ is A's largest singular value.

It is mathematically unambiguous because we can think of rtol*σ₁, σ₁ and the other singular values as real numbers rather than as floating point representations of a real numbers. It is useful because according to its semantics rank([2 3; .2 .3]) = 1.

dkarrasch commented 1 year ago

Ah, sorry, I messed it up. You should look at (i) the maximum singular value, (ii) multiply it by 3eps() and compare to the (ii) minimal singular value. Can you post those three numbers for each OS? On my Mac, (i) * (ii) is 10 times as big as (iii), so the (numerical) rank is pretty clearly 2 for those results. So, something must be different by an order of magnitude on Windows?

In any case, you have a very ill-defined matrix (in Float64) to begin with:

julia> cond(arc_points)
1.2126687153030292e16 # in particular > inv(eps())

so it may be generally very hard to trust (almost) any computation (in Float64) with this matrix.

gbaraldi commented 1 year ago

It's an order of magnitude difference, but it's not that large of a difference, it seems the smallest svd value that they're getting is 2.5448910415241994e-14 while I get 2.9981751136857335e-15 . It's almost 10x smaller, but both are very close to 0 so it's not an unexpected result.

dkarrasch commented 1 year ago

It's an order of magnitude difference, but it's not that large of a difference, it seems the smallest svd value that they're getting is 2.5448910415241994e-14 while I get 2.9981751136857335e-15 . It's almost 10x smaller, but both are very close to 0 so it's not an unexpected result.

Well, I think it depends on the context. An error on the order of a magnitude may be very bad. But the specific context here is the maximal singular value, and relative to it, the difference is below machine precision.

I'm closing this because I don't think there is anything to fix/actionable here (including documentation).

gbaraldi commented 1 year ago

Oh, I agree an error of that magnitude is very bad. But that's the life of ill conditioned matrices and finite precision :). Closing seems correct.

LilithHafner commented 1 year ago

@dkarrasch, we currently document a mathematically precise semantic and do not implement it. Would you clarify why you think that is not an issue?

gbaraldi commented 1 year ago

Unless I'm mistaking what you mean, we follow it. It's just that SVD isn't an exact process.

StefanKarpinski commented 1 year ago

One actionable take away is to add a note/warning that the result of rank can be different on different architectures, os and BLAS libraries dues to differences in association of floating point ops.

LilithHafner commented 1 year ago

It's just that SVD isn't an exact process.

SVD is an exact process. For example, the singular values of

1 0 0 0 2
0 0 3 0 0
0 0 0 0 0
0 2 0 0 0

are exactly 3, √5, and 2.

While the Matrix{Float64} implementation of svdvals cannot be exact because it returns a Vector{Float64}, someone who is not familiar with the interaction between extended precision and singular value decomposition might assume that rank is implemented exactly according to the thresholds indicated in its docstring. Given that

a) The docstring specifies a sensible, single, unambiguous answer for all inputs b) It is possible to compute that correct answer c) We do not always compute that correct answer (or even something ≈ to it) d) It requires a deep understanding of computational linear algebra to know that it requires exponentially many bits to always compute the correct answer

I think it is worth documenting c.

dkarrasch commented 1 year ago

I'm not sure I follow the discussion. Computing the difference of two numbers is also an exact process, yet what you compute may be (almost) complete garbage or may have very few correct digits. That very fact is completely independent of the programming language but is due to finite precision, and what exactly the garbage part of the result looks like may dependent on all sorts of things (though it's perhaps standardized in this case). The same applies to solving ill-conditioned/ill-posed linear systems. Documenting genuine issues with finite precision arithmetics may look like we as the Julia community take responsibility for that. Also, where would we start?

The concrete problem at hand is that rank uses information from the maximal and the minimal singular value. The latter, relative to the first, is below the representable (relative) precision, and should be perhaps considered as complete garbage. Apparently, we cannot even tell it's order of magnitude, not to mention the correctness of any digit! How reliable can the result of a function be whose "input" is complete garbage?

If we document accuracy issues, then it should be svdvals, because rank follows the docstring exactly AFAICT.

StefanKarpinski commented 1 year ago

I wouldn't worry about documentation making it look like we're at fault for numerical discrepancies—I think it's much more likely that people will reach that conclusion if we don't document it. We can document it in a way that makes it clear that this is due to different hardware and cannot be fixed without sacrificing major performance. We could link to this SO answer for more details: https://stackoverflow.com/a/74535127/659248.

StefanKarpinski commented 1 year ago

I also don't think there's any harm in repeating such warnings to some extent, so we can put a warning on both svdvals that values and rank, but I think that getting different results for rank on different systems is much more surprising because it feels like a discrete computation, but is actually laundering it's compute through a bunch of very complex floating point machinery that is highly system-dependent. That's why I think having the warning on rank specifically would be warranted.

LilithHafner commented 1 year ago

rank follows the docstring exactly AFAICT.

This would be true if the rank docstring referred to the output of svdvals(A) but it refers to the singular values of A which are exact theoretical values.

JustinSGray commented 1 year ago

FWIW, I have a modest familiarity with linear algebra and floating point issues generally. From my perspective, a note about the practical implementation using svdvals and its associated potential for numerical instability on highly singular matrices would have made it so that I was not surprised that we got different ranks on different architectures for the data I gave it here.

So I agree that a note about potential hardware/OS discrepancies on highly rank-deficient matricies would be helpful, and I think a reasonable resolution.

LilithHafner commented 1 year ago

I think there is some consensus that it would be nice to document this limitation of rank and possibly also of svdvalues.

bvdmitri commented 1 year ago

rank appears to have a precise mathematical definition in its docstring.

Its not true, right? I'm reading the output of the ?rank and it says:

Compute the rank of a matrix by counting how many singular values of A have magnitude greater than max(atol, rtol*σ₁)

Nowhere in the documentation it says that it returns a mathematical definition of the rank. It simply counts how many singular values are greater than some other value. It also documents that the rank function has a notion of "precision", consider the following example:

julia> matrix = [ 1.0 0.0; 0.0 1e-16 ]
2×2 Matrix{Float64}:
 1.0  0.0
 0.0  1.0e-16

julia> rank(matrix)
1

Which is clearly false from a mathematical point of view, because iszero(1e-16) === false, but it still a correct result from a limited precision point of view (and a similar example is documented as well).

but it refers to the singular values of A which are exact theoretical values.

Singular values of A are exact theoretical values, but the documented version of the rank function does explicitly say that it simply counts "non-zero" singular values with a given precision.

stevengj commented 1 year ago

I agree that a more explicit warning on rank may be helpful. I wouldn't put one on svdvals

People are used to the idea that any floating-point computation involving theoretically continuous outputs, from complicated things like svdvals and qr to simple things like sum and sqrt, generally produce inexact results, and that the precise outputs may depend slightly on architecture/compiler/implementation details. So it makes no sense to me to put a "warning" only on a particular function like svdvals.

However, people tend to fall into a false sense of security that any function with integer outputs is necessarily exact. So I think it's useful to remind people that, although rank produces an integer output, the result is a count of inexact floating-point singular values above some threshold and hence can vary between architectures or even between Julia releases if you have an ill-conditioned matrix with singular values close to the threshold.

In numerical linear algebra, what rank(A) is computing is commonly referred to as a numerical rank rather than the "rank" per se. e.g. Golub & van Loan write:

image

Indeed, it might be useful to have an expanded API (e.g. rankerr or ranktol) that returns both a numerical rank and an "error bar" based on the tolerance δ and the number of singular values close to δ. On the other hand, anyone who knows enough to use such a function might as well just look at svdvals directly.

LilithHafner commented 1 year ago

@bvdmitri, I believe that three definitions are at play and confusing this discussion: 1) The dimension of the vector space defined by the column vectors 2) The number of singular values that fall below a certain threshold 3) The number of outputs of svdvals that fall below a certain threshold

rank is documented to follow the second definition and actually follows the third. 1 and 2 are both precise mathematical definitions. When I refer to the "precise mathematical definition in its docstring" I am referring to definition 2.

wrt documenting this, I agree with @stevengj that the core of the issue is that returning an integer implicitly (and in this case falsely) guarantees an exact answer. IIUC all other operations in Base or LinearAlgebra that produce integral results are supposed to produce exact results with the exception of the clearly doccumented rationalize.