Skip to content

implement pinv with regularization for Diagonal matrices? #972

Open
@daanhb

Description

@daanhb

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    featureIndicates new feature / enhancement requests

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions