Skip to content

Commit

Permalink
Add tests for type conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
RafaelArutjunjan committed Jul 16, 2024
1 parent daf0857 commit 8b7f1ed
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 24 deletions.
40 changes: 21 additions & 19 deletions src/DataStructures/DistributionTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,41 @@ GeneralProduct(DM::Union{AbstractDataModel,AbstractDataSet}) = GeneralProduct([x



Base.length(P::GeneralProduct) = sum(length(P.v[i]) for i in 1:length(P.v))
Base.length(P::InformationGeometry.GeneralProduct) = sum(length(P.v[i]) for i in 1:length(P.v))


# insupport(P::GeneralProduct, X::AbstractVector)::Bool = all([insupport(P.v[i], X[P.lengths[i],]) for i in 1:length(P.lengths)])
# insupport(P::InformationGeometry.GeneralProduct, X::AbstractVector)::Bool = all([insupport(P.v[i], X[P.lengths[i],]) for i in 1:length(P.lengths)])
# sum(!insupport(P.v[i],X[i]) for i in 1:length(P.v)) == 0
Distributions.mean(P::GeneralProduct) = reduce(vcat, map(GetMean, P.v))
Distributions.cov(P::GeneralProduct) = BlockMatrix(map(Sigma, P.v)...)
Distributions.mean(P::InformationGeometry.GeneralProduct) = reduce(vcat, map(GetMean, P.v))
Distributions.cov(P::InformationGeometry.GeneralProduct) = BlockMatrix(map(Sigma, P.v)...)


for F = (Symbol("Distributions.logpdf"), Symbol("Distributions.gradlogpdf"))
# For MetaProgramming using the module prefix "Distributions." doesn't appear to work and functions must be imported explicitly
import Distributions: logpdf, gradlogpdf
for F = (Symbol("logpdf"), Symbol("gradlogpdf"))
eval(quote
# Base.$F(a::MyNumber) = MyNumber($F(a.x))
$F(P::GeneralProduct, X::AbstractVector) = sum($F(P.v[i], X[i]) for i in 1:length(P.v))
$F(P::GeneralProduct, X::AbstractVector{<:Real}) = $F(P, X, Val(length(P.v)))
$F(P::GeneralProduct, X::AbstractVector{<:Real}, ::Val{1}) = $F(P.v[1], X)
$F(P::GeneralProduct, X::AbstractVector{<:Real}, ::Val{2}) = $F(P.v[1], view(X,1:length(P.v[1]))) + $F(P.v[2], view(X,length(P.v[1])+1:lastindex(X)))
$F(P::InformationGeometry.GeneralProduct, X::AbstractVector) = sum($F(P.v[i], X[i]) for i in 1:length(P.v))
$F(P::InformationGeometry.GeneralProduct, X::AbstractVector{<:Real}) = $F(P, X, Val(length(P.v)))
$F(P::InformationGeometry.GeneralProduct, X::AbstractVector{<:Real}, ::Val{1}) = $F(P.v[1], X)
$F(P::InformationGeometry.GeneralProduct, X::AbstractVector{<:Real}, ::Val{2}) = $F(P.v[1], view(X,1:length(P.v[1]))) + $F(P.v[2], view(X,length(P.v[1])+1:lastindex(X)))
# There has to be a more performant way than this!
function $F(P::GeneralProduct, X::AbstractVector{<:Real}, ::Val)
function $F(P::InformationGeometry.GeneralProduct, X::AbstractVector{<:Real}, ::Val)
C = vcat([0],cumsum(length.(P.v)))
sum($F(P.v[i], X[C[i]+1:C[i+1]]) for i in 1:length(P.v))
end
end)
end


Distributions.pdf(P::GeneralProduct, X::AbstractVector) = exp(logpdf(P,X))
Distributions.invcov(P::GeneralProduct) = BlockMatrix(map(InvCov,P.v)...)
Distributions.pdf(P::InformationGeometry.GeneralProduct, X::AbstractVector) = exp(logpdf(P,X))
Distributions.invcov(P::InformationGeometry.GeneralProduct) = BlockMatrix(map(InvCov,P.v)...)
Distributions.product_distribution(X::AbstractVector{<:ContinuousMultivariateDistribution}) = GeneralProduct(X)


Sigma(P::GeneralProduct) = cov(P)
Sigma(P::InformationGeometry.GeneralProduct) = cov(P)


# LogLike(P::GeneralProduct, args...) = logpdf(P,[args...])
# LogLike(P::InformationGeometry.GeneralProduct, args...) = logpdf(P,[args...])



Expand Down Expand Up @@ -87,14 +88,15 @@ Distributions.gradlogpdf(P::Product,x::AbstractVector) = [gradlogpdf(P.v[i],x[i]


# Get Symbol for everything before {} in type.
UnparametrizeType(D) = (S=string(typeof(D)); Symbol(S[1:findfirst('{',S)-1]))
UnparametrizeType(D) = (S=string(typeof(D)); ind=findfirst('{',S); isnothing(ind) ? Symbol(S) : Symbol(S[1:(ind-1)]))

## Change Number Type of distributions
ConvertDist(D::UnivariateDistribution, ::Type{T}) where T<:Number = eval(quote $(UnparametrizeType(D)){$T}($(T).($(params(D)))...) end)
ConvertDist(D::Distributions.Distribution, ::Type{T}) where T<:Number = eval(quote $(UnparametrizeType(D))(broadcast(x->broadcast($T,x), $(Distributions.params(D)))...) end)
# Dirac not exported so MetaProgramming solution does not work
ConvertDist(D::InformationGeometry.Dirac, ::Type{T}) where T<:Number = InformationGeometry.Dirac(T.(D.μ))
ConvertDist(D::MultivariateNormal, ::Type{T}) where T<:Number = MvNormal(T.(mean(D)), T.(cov(D)))
# Has type specializations depending on structure of covariance which should be ignored due to missing constructors
ConvertDist(D::MvNormal, ::Type{T}) where T<:Number = MvNormal(T.(mean(D)), T.(cov(D)))

function ConvertDist(D::Union{Distributions.Product, InformationGeometry.GeneralProduct}, ::Type{T}) where T<:Number
@assert eltype(D.v) <: Distribution{Univariate, Continuous}
product_distribution(broadcast(x->ConvertDist(x,T), D.v))
end
2 changes: 1 addition & 1 deletion src/DataStructures/GeneralizedDataSet.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Does the data have mixed covariance, i.e. offdiagonal blocks in total covariance
"""
isseparable(GDS::GeneralizedDataSet) = isseparable(dist(GDS))
isseparable(P::Distribution) = false
isseparable(P::GeneralProduct) = length(P) == 2 ? true : false
isseparable(P::InformationGeometry.GeneralProduct) = length(P) == 2 ? true : false


xInvCov(GDS::GeneralizedDataSet) = InvCov(dist(GDS))[1:(Npoints(GDS)*xdim(GDS)),1:(Npoints(GDS)*xdim(GDS))]
Expand Down
34 changes: 30 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,9 @@ end
@test norm(EmbeddingMatrix(DME,MLE(DME)) .- EmbeddingMatrix(ODM,MLE(DME)), 1) < 1e-9

CDM = DataModel(CompositeDataSet(Data(ODM)), Predictor(ODM), dPredictor(ODM), MLE(ODM))
@test abs(loglikelihood(ODM, P) - loglikelihood(CDM, P)) < 5e-6
@test norm(Score(ODM, P) - Score(CDM, P)) < 5e-5
@test norm(FisherMetric(ODM, P) - FisherMetric(CDM, P)) < 8e-5
@test abs(loglikelihood(ODM, P) - loglikelihood(CDM, P)) < 1e-5
@test norm(Score(ODM, P) - Score(CDM, P)) < 2e-4
@test norm(FisherMetric(ODM, P) - FisherMetric(CDM, P)) < 2e-4
@test norm(InformationGeometry.ResidualStandardError(ODM) - InformationGeometry.ResidualStandardError(CDM)) < 1e-10

lastDS = Data(Data(CDM))[3]
Expand All @@ -242,7 +242,33 @@ end
splitCDM = DataModel(newCDS, newmodel, MLE(CDM))
@test abs(loglikelihood(splitCDM, P) - loglikelihood(CDM, P)) < 1e-5
@test norm(Score(splitCDM, P) - Score(CDM, P)) < 2e-4
@test norm(FisherMetric(splitCDM, P) - FisherMetric(CDM, P)) < 2e-3
@test norm(FisherMetric(splitCDM, P) - FisherMetric(CDM, P)) < 2e-4


GDM = DataModel(GeneralizedDataSet(DME), Predictor(DME), MLE(DME))
@test abs(loglikelihood(GDM, P) - loglikelihood(CDM, P)) < 1e-5
@test norm(Score(GDM, P) - Score(CDM, P)) < 2e-4
@test norm(FisherMetric(GDM, P) - FisherMetric(CDM, P)) < 2e-4

# Test Type conversions for Datasets
function TypeTester(DM::AbstractDataModel, ::Type{T}) where T<:Number
dm = T(DM)
@test eltype(xdata(dm)) <: T
@test eltype(ydata(dm)) <: T
@test eltype(yInvCov(dm)) <: T
@test eltype(eltype(WoundX(dm))) <: T
@test eltype(MLE(dm)) <: T
end
TypeTester(DM, Float16)
TypeTester(DME, Float16)
TypeTester(CDM, Float16)
TypeTester(splitCDM, Float16)
TypeTester(GDM, Float16)
TypeTester(DM, BigFloat)
TypeTester(DME, BigFloat)
TypeTester(CDM, BigFloat)
TypeTester(splitCDM, BigFloat)
TypeTester(GDM, BigFloat)
end


Expand Down

0 comments on commit 8b7f1ed

Please sign in to comment.