I am trying to model a sequence of behaviours with two end points as a hidden markov model. To do this, I tried fixing some parameters in the transition and emission matrices, but I ran into the following error:
MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{DynamicPPL.DynamicPPLTag, Float64}, Float64, 9})
I’ve tried finding related issues and believe that I’d have to be working with Dual
numbers as explained by @gdalle in this post: Autodifferentiation of model with fixed and mutable parameters - #3 by gdalle, but I’m not sure how to apply this in the context of Turing.
Some context about the setting:
In the experiment, ravens solved puzzles and we want to understand if they used either a “trial-and-error” or a “goal-oriented” strategy (the hidden states). The observations indicate whether the raven manipulated a “goal-relevant” or “goal-irrelevant” part of the puzzle. The trials are nested in experimental conditions but these aren’t relevant for the MWE below. Finally, the end states represent whether the raven succeeded or failed at solving a given puzzle.
Here’s what I’ve tried so far:
using HiddenMarkovModels
using Turing
using NNlib
using LinearAlgebra
# Simulation for two hidden states and two end states
trans = [0.92 0.05 0.025 0.005;
0.03 0.94 0.005 0.025;
0.0 0.0 1.0 0.0;
0.0 0.0 0.0 1.0]
emiss = [0.9 0.1 0.0 0.0;
0.1 0.9 0.0 0.0;
0.0 0.0 1.0 0.0;
0.0 0.0 0.0 1.0]
init = [0.6, 0.4, 0.0, 0.0]
dists = HiddenMarkovModels.LightCategorical.(eachrow(emiss))
hmm = HMM(init, trans, dists)
obs_seqs = [last(rand(hmm, 100)) for _ in 1:10]
function truncate_at_end(x; endstate=[3, 4])
i = findfirst(in(endstate), x)
return isnothing(i) ? x : x[1:i]
end
trunc_obs_seqs = truncate_at_end.(obs_seqs)
# Helper functions for the model
function with_endstates(v, size)
a = zeros(size)
for (i, x) in enumerate(v)
a[i] = x
end
return a
end
function with_endstates(M::AbstractMatrix, size; fld=size)
A = Matrix(Diagonal(ones(size)))
for (i, m) in enumerate(M)
A[fldmod1(i, fld)...] = m
end
return A
end
forward_loglikelihood(hmm, obs_seq) = only(last(forward(hmm, obs_seq)))
# Turing model
# K = number of hidden states
# E = number of end states
@model function unsupervised_hmm(K, E, obs_seqs)
π1 ~ Dirichlet(ones(K)) # can't start in the end states
Θ ~ filldist(Dirichlet(ones(K + E)), K)
Φ ~ filldist(Dirichlet(ones(K)), K) # only end states can emit end observations
init = with_endstates(π1, K + E)
trans = with_endstates(Θ, K + E)
emiss = with_endstates(permutedims(Φ), K + E; fld=2)
dists = [Categorical(emiss[:, i]) for i in axes(emiss, 2)]
for obs_seq in obs_seqs
hmm = HMM(init, trans, dists)
Turing.@addlogprob! forward_loglikelihood(hmm, obs_seq)
end
return nothing
end
model = unsupervised_hmm(2, 2, trunc_obs_seqs)
chn = sample(model, NUTS(), 500)
Thank you for your help!