JuliaDynamics / TransferEntropy.jl

Transfer entropy (conditional mutual information) estimators for the Julia language
https://juliadynamics.github.io/CausalityTools.jl/stable/
Other
14 stars 6 forks source link

V1 #57

Closed kahaaga closed 3 years ago

kahaaga commented 4 years ago

Version 1.0 stuff in the /src/v1 folder. Keeping the old files for reference until everything's ready.

kahaaga commented 4 years ago

@Datseris This is ready to go. The syntax will remain roughly the same once I've finished up Entropies.jl.

However, I'd like your input on the syntax before I merge. I ended up doing this (similar to what we discussed in an earlier issue).

# General syntax
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::TransferEntropyEstimator)
# Implemented estimators
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::VisitationFrequency)
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::NearestNeighborMI)
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::SymbolicPerm)
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::TransferOperatorGrid)
transferentropy(src, targ, [, cond], emb::EmbeddingTE, est::SimplexEstimator)

(the last argument will just be an EntropyEstimator once Entropies.jl is out.

Here, the EmbeddingTE instance will contain embedding parameters, and EmbeddingTE() will give as default the lowest-dimensional embedding.

There will be some other entropy-based causality measures included in CausalityTools that also rely on generalized delay embeddings. Those other functions will the same syntax, except their embeddings are provided using different structs. For example, the predictive asymmetry method will have the syntax:

predictive_asymmetry(src, targ, [, cond], emb::EmbeddingTE, est::VisitationFrequency)
predictive_asymmetry(src, targ, [, cond], emb::EmbeddingTE, est::NearestNeighborMI)
predictive_asymmetry(src, targ, [, cond], emb::EmbeddingTE, est::SymbolicPerm)
# etc...

Exactly how the embeddings for these methods are constructed, how many components they have, and which symbols they have will differ, so I've opted for this struct-based approach to keep the syntax clean.

What do you think?

Datseris commented 4 years ago

Yeah, I think this API is fine.

What is EmbeddingTE ? You don't want to re-use GeneralizedEmbedding from DelayEmbeddings.jl?

the last argument will just be an EntropyEstimator once Entropies.jl is out.

I think it makes sense to have a working draft of Entropies.jl before attempting a v1 release here.

kahaaga commented 4 years ago

What is EmbeddingTE? You don't want to re-use GeneralizedEmbedding from DelayEmbeddings.jl?

The EmbeddingTE provides the instructions for how the generalized embedding should be constructed (lags and dimensions for each of the TE marginals - future of target variable, present/past of target variable, and present/past of source variable):

@Base.kwdef struct EmbeddingTE
    dS::Union{Int, AbstractVector{Int}, OptimiseDim} = 1
    dT::Union{Int, AbstractVector{Int}, OptimiseDim} = 1
    d𝒯::Union{Int, AbstractVector{Int}, OptimiseDim} = 1
    dC::Union{Int, AbstractVector{Int}, OptimiseDim, Nothing} = 1
    τS::Union{Int, AbstractVector{Int}, OptimiseDelay} = -1
    τT::Union{Int, AbstractVector{Int}, OptimiseDelay} = -1
    τC::Union{Int, AbstractVector{Int}, OptimiseDelay, Nothing} = -1
    η𝒯::Union{Int, AbstractVector{Int}, OptimiseDelay} = 1
end

From these instructions, a GeneralizedEmbedding is constructed, which is then used to compute transfer entropy.

GeneralizedEmbedding in itself has conceptual connection to the marginals of a state space reconstruction for transfer entropy. This would require the users themselves to construct the embedding manually, which goes way beyond the "easy to use" principle. The EmbeddingTE struct takes care of the mapping of coordinate axes of the reconstruction to the relevant marginals during entropy computation.

The mapping of state space coordinate axes to marginals might be different for other transfer entropy-related methods. These might share some, but not all marginals, with TE. That is why I here constructed a transfer entropy-specific embedding struct.

Alternatively, one could use the following api:

transferentropy(src, targ, [cond], est::TransferEntropyEstimator; τT = -1, τS  = -1, τC = -1, η𝒯 = 1, dT = 1, dS = 1, dC = 1, d𝒯 = 1)

where the lags/dimensions are given explicitly. This serves the same purpose as EmbeddingTE, but takes away the immediate conceptual part of transfer entropy being computed over the marginals of an embedding, using some entropy estimator. However, I am happy with using either approach.

I think it makes sense to have a working draft of Entropies.jl before attempting a v1 release here.

That makes sense.

Datseris commented 4 years ago

Sure, sounds good to me. Both APIs are fine but the first should be the one used internally throughout, while the second is only a "convenience" wrapper.