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

add unary and number broadcast; add all #83

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,7 @@ The `reverse` can transform between Hankel and Toeplitz. It is used to achieve f
|copy|✓|✓|✓|✓|✓|✓|
|similar|✓|✓|✓|✓|✓|✓|
|zero|✓|✓|✓|✓|✓|✓|
|real|✓|✓|✓|✓|✓|✓|
|imag|✓|✓|✓|✓|✓|✓|
|fill!|✓|✗|✗|✗|✗|✓|
|conj|✓|✓|✓|✓|✓|✓|
|transpose|✓|✓|✓|✓|✓|✓|
|adjoint|✓|✓|✓|✓|✓|✓|
|tril!|✓|✗|✗|✓|✓|✗|
Expand All @@ -94,22 +91,22 @@ The `reverse` can transform between Hankel and Toeplitz. It is used to achieve f
|triu|✓|✓|✓|✓|✓|✗|
|+|✓|✓|✓|✓|✓|✓|
|-|✓|✓|✓|✓|✓|✓|
|scalar<br>mult|✓|✓|✓|✓|✓|✓|
|==|✓|✓|✓|✓|✓|✓|
|issymmetric|||||||
|istriu|||||||
|istril|||||||
|iszero|✓|✓|✓|✓|✓||
|isone|||||||
|diag|✓|✓|✓|✓|✓|✓|
|copyto!|✓|✓|✓|✓|✓|✓|
|reverse|✓|✓|✓|✓|✓|✓|
|broadcast|||||||
|\_all|✓|✓|✓|✓|✓|✓|
|unary broadcast|✓|✓|✓|✓|✓|✓|
|number broadcast|✓|✓|✓|✓|✓|✓|
|broadcast!|||||||

</details>

Note that scalar multiplication, `conj`, `+` and `-` could be removed once `broadcast` is implemented.
Note that `+` and `-` could be removed once binary `broadcast` is implemented.

`reverse(Hankel)` returns a `Toeplitz`, while `reverse(AbstractToeplitz)` returns a `Hankel`.

Expand Down
11 changes: 9 additions & 2 deletions src/ToeplitzMatrices.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
module ToeplitzMatrices
import DSP: conv

import Base: adjoint, convert, transpose, size, getindex, similar, copy, getproperty, inv, sqrt, copyto!, reverse, conj, zero, fill!, checkbounds, real, imag, isfinite, DimsInteger, iszero
import Base: convert, size, getindex, similar, copy, getproperty, copyto!, reverse, zero, fill!, checkbounds, isfinite, DimsInteger, iszero
import Base: all, adjoint, transpose, real, imag, inv, sqrt, conj
import Base: parent
import Base: ==, +, -, *, \
import Base: AbstractMatrix
import Base.Broadcast: broadcasted, DefaultMatrixStyle
import LinearAlgebra: Cholesky, Factorization
import LinearAlgebra: ldiv!, factorize, lmul!, pinv, eigvals, eigvecs, eigen, Eigen, det
import LinearAlgebra: cholesky!, cholesky, tril!, triu!, checksquare, rmul!, dot, mul!, tril, triu, diag
Expand Down Expand Up @@ -34,7 +36,7 @@
include("iterativeLinearSolvers.jl")

# Abstract
abstract type AbstractToeplitz{T<:Number} <: AbstractMatrix{T} end
abstract type AbstractToeplitz{T} <: AbstractMatrix{T} end
putianyi889 marked this conversation as resolved.
Show resolved Hide resolved

size(A::AbstractToeplitz) = (length(A.vc),length(A.vr))
@inline _vr(A::AbstractToeplitz) = A.vr
Expand All @@ -46,6 +48,11 @@
convert(::Type{AbstractToeplitz{T}}, A::AbstractToeplitz) where T = AbstractToeplitz{T}(A)

isconcrete(A::AbstractToeplitz) = isconcretetype(typeof(A.vc)) && isconcretetype(typeof(A.vr))

for op in (:+, :max)
@eval mapreduce(f, ::typeof($op), A::AbstractToeplitz) = mapreduce(identity, $op, [mapreduce(f, $op, diag(A, k)) for k in -size(A,1)+1:size(A,2)-1])

Check warning on line 53 in src/ToeplitzMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/ToeplitzMatrices.jl#L53

Added line #L53 was not covered by tests
end
all(f, A::AbstractToeplitz, ::Colon) = all(f, A.vc, :) && all(f, A.vr, :)

Check warning on line 55 in src/ToeplitzMatrices.jl

View check run for this annotation

Codecov / codecov/patch

src/ToeplitzMatrices.jl#L55

Added line #L55 was not covered by tests
iszero(A::AbstractToeplitz) = iszero(A.vc) && iszero(A.vr)
function diag(A::AbstractToeplitz, k::Integer=0)
if k >= size(A, 2) || -k >= size(A, 1)
Expand Down
8 changes: 5 additions & 3 deletions src/hankel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
convert(::Type{AbstractArray{T}}, A::Hankel) where {T<:Number} = convert(Hankel{T}, A)
convert(::Type{AbstractMatrix{T}}, A::Hankel) where {T<:Number} = convert(Hankel{T}, A)
convert(::Type{Hankel{T}}, A::Hankel) where {T<:Number} = Hankel{T}(convert(AbstractVector{T}, A.v), size(A))
broadcasted(::DefaultMatrixStyle, f, A::Hankel) = Hankel(f.(A.v), A.size)
broadcasted(::DefaultMatrixStyle, f, x::Number, A::Hankel) = Hankel(f.(x, A.v), A.size)
broadcasted(::DefaultMatrixStyle, f, A::Hankel, x::Number) = Hankel(f.(A.v, x), A.size)
all(f, A::Hankel, ::Colon) = all(f, A.v, :)

Check warning on line 66 in src/hankel.jl

View check run for this annotation

Codecov / codecov/patch

src/hankel.jl#L66

Added line #L66 was not covered by tests

# Size
size(H::Hankel) = H.size
Expand All @@ -72,7 +76,7 @@
return A.v[i+j-1]
end
AbstractMatrix{T}(A::Hankel) where {T} = Hankel{T}(AbstractVector{T}(A.v), A.size)
for fun in (:zero, :conj, :copy, :-, :similar, :real, :imag)
for fun in (:zero, :copy)
@eval $fun(A::Hankel) = Hankel($fun(A.v), size(A))
end
for op in (:+, :-)
Expand Down Expand Up @@ -102,8 +106,6 @@
transpose(A::Hankel) = Hankel(A.v, reverse(size(A)))
adjoint(A::Hankel) = transpose(conj(A))
(==)(A::Hankel, B::Hankel) = A.v == B.v && size(A) == size(B)
(*)(scalar::Number, C::Hankel) = Hankel(scalar * C.v, size(C))
(*)(C::Hankel,scalar::Number) = Hankel(C.v * scalar, size(C))

isconcrete(A::Hankel) = isconcretetype(A.v)

Expand Down
19 changes: 18 additions & 1 deletion src/special.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
@eval $TYPE(v::$TY) = $TYPE{eltype(v)}(v)
end
end
TriangularToeplitz{T}=Union{UpperTriangularToeplitz{T},LowerTriangularToeplitz{T}}
SymToeplitzOrCirc{T} = Union{SymmetricToeplitz{T}, Circulant{T}}
TriangularToeplitz{T} = Union{UpperTriangularToeplitz{T}, LowerTriangularToeplitz{T}}

# vc and vr
function getproperty(A::SymmetricToeplitz, s::Symbol)
Expand Down Expand Up @@ -129,6 +130,22 @@
transpose(A::LowerTriangularToeplitz) = UpperTriangularToeplitz(A.v)
transpose(A::UpperTriangularToeplitz) = LowerTriangularToeplitz(A.v)

# _all
all(f, A::SymToeplitzOrCirc, ::Colon) = all(f, A.v, :)
all(f, A::TriangularToeplitz, ::Colon) = f(zero(eltype(A))) && all(f, A.v, :)

Check warning on line 135 in src/special.jl

View check run for this annotation

Codecov / codecov/patch

src/special.jl#L134-L135

Added lines #L134 - L135 were not covered by tests

# broadcast
for TYPE in (:SymmetricToeplitz, :Circulant)
@eval broadcasted(::DefaultMatrixStyle, f, A::$TYPE) = $TYPE(f.(A.v))
@eval broadcasted(::DefaultMatrixStyle, f, x::Number, A::$TYPE) = $TYPE(f.(x, A.v))

Check warning on line 140 in src/special.jl

View check run for this annotation

Codecov / codecov/patch

src/special.jl#L140

Added line #L140 was not covered by tests
@eval broadcasted(::DefaultMatrixStyle, f, A::$TYPE, x::Number) = $TYPE(f.(A.v, x))
end
for TYPE in (:UpperTriangularToeplitz, :LowerTriangularToeplitz)
@eval broadcasted(::DefaultMatrixStyle, f, A::$TYPE) = iszero(f(zero(eltype(A)))) ? $TYPE(f.(A.v)) : _toep_broadcast(f, A)
@eval broadcasted(::DefaultMatrixStyle, f, x::Number, A::$TYPE) = iszero(f(x, zero(eltype(A)))) ? $TYPE(f.(x, A.v)) : _toep_broadcast(f, x, A)

Check warning on line 145 in src/special.jl

View check run for this annotation

Codecov / codecov/patch

src/special.jl#L145

Added line #L145 was not covered by tests
@eval broadcasted(::DefaultMatrixStyle, f, A::$TYPE, x::Number) = iszero(f(zero(eltype(A)), x)) ? $TYPE(f.(A.v, x)) : _toep_broadcast(f, A, x)
end

# getindex
Base.@propagate_inbounds function getindex(A::SymmetricToeplitz, i::Integer, j::Integer)
@boundscheck checkbounds(A,i,j)
Expand Down
13 changes: 9 additions & 4 deletions src/toeplitz.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct Toeplitz{T, VC<:AbstractVector{T}, VR<:AbstractVector{T}} <: AbstractToep

function Toeplitz{T, VC, VR}(vc::VC, vr::VR) where {T, VC<:AbstractVector{T}, VR<:AbstractVector{T}}
require_one_based_indexing(vr, vc)
if first(vc) != first(vr)
if !isequal(first(vc), first(vr))
putianyi889 marked this conversation as resolved.
Show resolved Hide resolved
error("First element of the vectors must be the same")
end
return new{T,VC,VR}(vc, vr)
Expand Down Expand Up @@ -54,6 +54,13 @@ Base.@propagate_inbounds function getindex(A::AbstractToeplitz, i::Integer, j::I
end
end

broadcasted(::DefaultMatrixStyle, f, A::AbstractToeplitz) = _toep_broadcast(f, A)
broadcasted(::DefaultMatrixStyle, f, x::Number, A::AbstractToeplitz) = _toep_broadcast(f, x, A)
broadcasted(::DefaultMatrixStyle, f, A::AbstractToeplitz, x::Number) = _toep_broadcast(f, A, x)
_toep_broadcast(f, A::AbstractToeplitz) = Toeplitz(f.(A.vc), f.(A.vr))
_toep_broadcast(f, x::Number, A::AbstractToeplitz) = Toeplitz(f.(x, A.vc), f.(x, A.vr))
_toep_broadcast(f, A::AbstractToeplitz, x::Number) = Toeplitz(f.(A.vc, x), f.(A.vr, x))

checknonaliased(A::Toeplitz) = Base.mightalias(A.vc, A.vr) && throw(ArgumentError("Cannot modify Toeplitz matrices in place with aliased data"))

function tril!(A::Toeplitz, k::Integer=0)
Expand Down Expand Up @@ -110,7 +117,7 @@ function AbstractMatrix{T}(A::AbstractToeplitz) where {T}
vr = AbstractVector{T}(_vr(A))
Toeplitz{T}(vc,vr)
end
for fun in (:zero, :conj, :copy, :-, :real, :imag)
for fun in (:zero, :copy)
@eval $fun(A::AbstractToeplitz)=Toeplitz($fun(A.vc),$fun(A.vr))
end
for op in (:+, :-)
Expand All @@ -129,8 +136,6 @@ function fill!(A::Toeplitz, x::Number)
fill!(A.vr,x)
A
end
(*)(scalar::Number, C::AbstractToeplitz) = Toeplitz(scalar * C.vc, scalar * C.vr)
(*)(C::AbstractToeplitz,scalar::Number) = Toeplitz(C.vc * scalar, C.vr * scalar)

function lmul!(x::Number, A::Toeplitz)
checknonaliased(A)
Expand Down
17 changes: 17 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,23 @@ end
end
end

@testset "Broadcast" begin
A = rand(ComplexF64, 3, 3)
T = Toeplitz(A)
S = Symmetric(T)
C = Circulant(T)
U = UpperTriangular(T)
L = LowerTriangular(T)

for M in (T, S, C, U, L)
@testset "$(typeof(M))" begin
@test M .+ 1 == Matrix(M) .+ 1
@test exp.(M) == exp.(Matrix(M))
@test isa(M .* 2, typeof(M))
end
end
end

@testset "Circulant mathematics" begin
C1 = Circulant(rand(5))
C2 = Circulant(rand(5))
Expand Down