Matrix vs. AbstractMatrix

I am pondering to change the definition of the basic object DescriptorStateSpace used in the DescriptorSystems package from

struct DescriptorStateSpace{T, ET <: ETYPE{T}} <: AbstractDescriptorStateSpace 
    A::Matrix{T}
    E::ET
    B::Matrix{T}
    C::Matrix{T}
    D::Matrix{T}
    Ts::Float64
    function DescriptorStateSpace{T}(A::Matrix{T}, E::ETYPE{T}, 
                                     B::Matrix{T}, C::Matrix{T}, D::Matrix{T},  Ts::Real) where {T} 
        dss_validation(A, E, B, C, D, Ts)
        new{T, typeof(E)}(A, E, B, C, D, Float64(Ts))
    end
end

to

struct DescriptorStateSpace{T, ET <: ETYPE{T}} <: AbstractDescriptorStateSpace
    A::AbstractMatrix{T}
    E::ET
    B::AbstractMatrix{T}
    C::AbstractMatrix{T}
    D::AbstractMatrix{T}
    Ts::Float64
    function DescriptorStateSpace{T}(A::AbstractMatrix{T}, E::ETYPE{T},
                                     B::AbstractMatrix{T}, C::AbstractMatrix{T},
                                     D::AbstractMatrix{T},  Ts::Real) where {T}
        dss_validation(A, E, B, C, D, Ts)
        new{T, typeof(E)}(A, E, B, C, D, Float64(Ts))
    end
end

I must confess, I can not see all implications of such a change regarding the performance of the package (especially regarding compilation times).
With this change, all tests run without any problem, as before.

My motivation for this change is to allow the use of sparse matrices in descriptor system models. I need this functionality in the PeriodicSystems package on which I am currently working (at this moment, simply as a container for lifted systems). I can of course manage without the above modification, by working directly with the system matrices, but the possibility of handling systems models with sparse matrix data is for me appealing for further extensions of the DescriptorSystems package.

Note: This issue would be easy (i.e., without any modification) if the SparseMatrixCSC type would be a subtype of Matrix, but unfortunately it is not (it is only a subtype of AbstractMatrix).

I would appreciate any opinion on this issue.

Typically, this would be done as

struct DescriptorStateSpace{T, ET <: ETYPE{T}, MT<:AbstractMatrix{T}} <: AbstractDescriptorStateSpace
    A::MT
    E::ET
    B::MT
    C::MT
    D::MT
    Ts::Float64
    function DescriptorStateSpace{T}(A::MT, E::ETYPE{T},
                                     B::MT, C::MT, D::MT,  Ts::Real) where {T, MT<:AbstractMatrix{T}}
        dss_validation(A, E, B, C, D, Ts)
        new{T, typeof(E), MT}(A, E, B, C, D, Float64(Ts))
    end
end

This would be equally efficient, as all the types are concrete, but it would be more general. The alternative as posed in the post would also work, but it might be less performant, as it would involve dynamically dispatching to the correct methods. Whether the difference in performance is a factor for actual problems remains to be seen, and may be checked by benchmarking.

3 Likes

I am using

const ETYPE{T} = Union{AbstractMatrix{T},UniformScaling{Bool}}

to define ETYPE{T}. Is this consistent with the definition suggested by you?
Thanks in advance for your advice.

Yes, this is fine, since the actual type ET that is used in the struct is a concrete subtype of ETYPE{T}.