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

implement pinv with regularization for Diagonal matrices? #972

Open
daanhb opened this issue Nov 26, 2022 · 1 comment
Open

implement pinv with regularization for Diagonal matrices? #972

daanhb opened this issue Nov 26, 2022 · 1 comment
Labels
feature Indicates new feature / enhancement requests

Comments

@daanhb
Copy link
Contributor

daanhb commented Nov 26, 2022

It seems that Diagonal matrices in LinearAlgebra do not fully implement the interface of pinv. There is an implementation of pinv(D::Diagonal) here and of pinv(D::Diagonal, rtol) just below. There is no implementation of a pseudo-inverse with regularization, as in pinv(D::Diagonal; atol = ..., rtol = ...).

On the other hand, the generic routine for dense arrays explicitly checks for diagonal matrices and implements a custom pinv for that case with thresholds here. However, this code does not return a diagonal matrix:

julia> using LinearAlgebra

julia> D = Diagonal((1/10).^(1:5))
5×5 Diagonal{Float64, Vector{Float64}}:
 0.1                     
     0.01                
          0.001          
                0.0001   
                       1.0e-5

julia> pinv(D; atol = 1e-3)
5×5 Matrix{Float64}:
 10.0    0.0     0.0  0.0  0.0
  0.0  100.0     0.0  0.0  0.0
  0.0    0.0  1000.0  0.0  0.0
  0.0    0.0     0.0  0.0  0.0
  0.0    0.0     0.0  0.0  0.0

Might anyone else be interested in having a more complete implementation of pinv for Diagonal matrices which returns a Diagonal?

Combining the implementation in dense.jl with the one in diagonal.jl, it could look something like this:

function LinearAlgebra.pinv(D::Diagonal{T}; atol::Real = 0.0, rtol::Real = (eps(real(float(oneunit(T))))*length(D.diag))*iszero(atol)) where T
    Di = similar(D.diag, typeof(inv(oneunit(T))))
    if !isempty(D.diag)
        maxabsA = maximum(abs, D.diag)
        abstol = max(rtol * maxabsA, atol)
        for i in 1:length(D.diag)
            if abs(D.diag[i]) > abstol
                invD = inv(D.diag[i])
                if isfinite(invD)
                    Di[i] = invD
                    continue
                end
            end
            # fallback
            Di[i] = zero(T)
        end
    end
    Diagonal(Di)
end

There has been quite a discussion about default choices of the regularization parameters (e.g. #652, #444 and issues referenced there). The implementation above does not change that. It is merely about returning a Diagonal matrix to get this behaviour:

julia> pinv(D; atol=1e-3)
5×5 Diagonal{Float64, Vector{Float64}}:
 10.0                    
      100.0              
            1000.0       
                   0.0   
                       0.0
@brenhinkeller brenhinkeller added the feature Indicates new feature / enhancement requests label Nov 26, 2022
@daanhb
Copy link
Contributor Author

daanhb commented Apr 7, 2023

On the same topic: for consistency it may be worth adding atol and rtol keyword arguments to pinv(x::Number) as well, returning the same as it would for the corresponding 1-by-1 matrix.

@KristofferC KristofferC transferred this issue from JuliaLang/julia Nov 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Indicates new feature / enhancement requests
Projects
None yet
Development

No branches or pull requests

2 participants