# How to specify a Turing model to have the 2 vectors as input for a distribution that returns a tuple

**URL:** <https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493>\
**Category:** Probabilistic Programming\
**Tags:** turing, distributions, lba, sequantial-models\
**Created:** [June 17, 2023, 3:30pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493 "2023-06-17T15:30:11Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![DominiqueMakowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dominiquemakowski/32/51410_2.png) [@DominiqueMakowski](https://discourse.julialang.org/u/DominiqueMakowski)\
**Post date:** [June 17, 2023, 3:30pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/1 "2023-06-17T15:30:11Z")

</div>

The [SequentialSamplingModels.jl](https://github.com/itsdfish/SequentialSamplingModels.jl) package implements distributions that are used in psychology research. One such model, the Linear Ballistic Accumulator (LBA), is used to model simultaneously reaction time (RT) and choice (a binary variable).

The `LBA()` distribution returns a tuple of the two vectors:

```julia
using Turing
using SequentialSamplingModels
using Random
using LinearAlgebra
using DataFrames

rand(LBA(ν=[3.0, 2.0], A=0.8, k=0.2, τ=0.3), 3)

```

```julia
(choice = [1, 1, 1], rt = [0.4951585198314631, 0.4486594724069529, 0.5222694726662244])

```

One can use such distribution in Turing in a simple model as follows:

```julia
data = DataFrame(rand(LBA(ν=[3.0, 2.0], A=0.8, k=0.2, τ=0.3), 500))

@model function model_lba(data)
    data = (choice=data.choice, rt=data.rt)
    min_rt = minimum(data[2])

    # Priors
    drift ~ filldist(MvNormal(zeros(2), I * 2), 2)
    A ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    k ~ truncated(Normal(0.2, 0.2), 0.0, Inf)
    tau ~ Uniform(0.0, min_rt)

    # Likelihood
    data ~ LBA(; τ=tau, A=A, k=k, ν=drift)
    return (; data, drift, A, k, tau)
end

chain = sample(model_lba(data), Prior(), 50000)

```

I would like to re-write the model to accept two independent vectors as inputs.

My initial input was to make a simple change and capture the `LBA()` output as a tuple:

```julia
@model function model_lba(choice, rt)
    min_rt = minimum(rt)

    # Priors
    drift ~ filldist(MvNormal(zeros(2), I * 2), 2)
    A ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    k ~ truncated(Normal(0.2, 0.2), 0.0, Inf)
    tau ~ Uniform(0.0, min_rt)

    # Likelihood
    (choice, rt) ~ LBA(; τ=tau, A=A, k=k, ν=drift)
    return (; choice, rt, drift, A, k, tau)
end

```

```julia
ERROR: LoadError: Malformed variable name (choice, rt)!

```

But that seems incompatible with Turing (?). I then tried a workaround by creating the `data` tuple inside the model:

```julia
@model function model_lba(choice, rt)
    data = (choice=choice, rt=rt)
    min_rt = minimum(rt)

    # Priors
    drift ~ filldist(MvNormal(zeros(2), I * 2), 2)
    A ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    k ~ truncated(Normal(0.2, 0.2), 0.0, Inf)
    tau ~ Uniform(0.0, min_rt)

    # Likelihood
    data ~ LBA(; τ=tau, A=A, k=k, ν=drift)
    return (; data.choice, data.rt, drift, A, k, tau)
end

chain = sample(model_lba(data.choice, data.rt), Prior(), 50000)

```

```julia
ERROR: MethodError: no method matching iterate(::LBA{Matrix{Float64}, Float64, Float64, Float64})
  ...

```

But to no avail. Any ideas or recommendations are welcome!

_(note: this question was originally asked on the [package’s repo](https://github.com/itsdfish/SequentialSamplingModels.jl/issues/19) in the context of using `predict()` from the priors, but the author suggested to ask the wider Turing community)_

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 4:03pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/2 "2023-06-17T16:03:15Z")

</div>

IMO this is an issue with SequentialSamplingModels.jl 😕

SequentialSamplingModels.jl sort of “lies” about what the distribution actually implements, The below indicates that it’s a `UnivariateContinuousDistribution`, which, according to Distributions.jl’s design, should imply that `rand(dist)` results in a `Real`; not a `NamedTuple`.

> <https://github.com/itsdfish/SequentialSamplingModels.jl/blob/cb801320358e7279af037c49f31a9cb4d45099f6/src/utilities.jl#LL1C77-L1C77>

This is why Turing.jl doesn’t know what to do with it. It just happens to work out when `data` is an observation because in that scenario, Turing.jl really only needs to be able to compute the `logpdf`.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 4:10pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/3 "2023-06-17T16:10:27Z")

</div>

If you _really_ want to work around this, you can do something like:

```julia
# This is using internals of Turing.jl so we don't guarantee that this will continue to work between releases.
macro isobservation(variable)
    tmp = gensym(:isobservation)
    return esc(:($tmp = $(DynamicPPL.isassumption(variable)); !$tmp))
end

# Alternative constructor because we need `data` to occur in
# the arguments of the model to be considered an observation.
model_lba(choice, rt) = model_lba((choice=choice, rt=rt))
model_lba(data::DataFrame) = model_lba((choice=data.choice, rt=data.rt))
@model function model_lba(data)
    choice, rt = data.choice, data.rt
    min_rt = minimum(rt)

    # Priors
    drift ~ filldist(MvNormal(zeros(2), I * 2), 2)
    A ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    k ~ truncated(Normal(0.2, 0.2), 0.0, Inf)
    tau ~ Uniform(0.0, min_rt)

    # Likelihood
    data_dist = LBA(; τ=tau, A=A, k=k, ν=drift)
    if @isobservation(data)
        Turing.@addlogprob! logpdf(data_dist, data)
    else
        # If `data` is not considered an observation, we sample.
        data = rand(data_dist)
    end
    return (; data.choice, data.rt, drift, A, k, tau)
end

```

Note that the above has the drawback of not being reproducible since we’re calling `rand` without providing the `rng` that is being used; I can provide functionality for doing this though, if desired.

EDIT: Also, this won’t be compatible with the construction of `Chains` because Turing won’t record the values when you call `rand` instead of using `~` 😕 So if you want to sample from the prior, you’ll need to manually call `model()` multiple times instead. For example

```julia
model_lba(data)()

```

and then extract the desired variables from there.

---

<div class="post-metadata">

**Author:** ![DominiqueMakowski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dominiquemakowski/32/51410_2.png) [@DominiqueMakowski](https://discourse.julialang.org/u/DominiqueMakowski)\
**Post date:** [June 17, 2023, 4:15pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/4 "2023-06-17T16:15:07Z")

</div>

Interesting, thanks a lot. Indeed the author of SequentialSamplingModels.jl mentioned [this issue](https://github.com/itsdfish/SequentialSamplingModels.jl/issues/17#issuecomment-1595011463).

Do you think it would be worth it to ask the Distributions.jl community for possible alternative classes more suitable for these models?

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 4:17pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/5 "2023-06-17T16:17:10Z")

</div>

> Do you think it would be worth it to ask the Distributions.jl community for possible alternative classes more suitable for these models?

Distributions.jl do support _some_ variate-types that are not just simple arrays or reals, _but_ my prior tells me that the probability of them adding support for `NamedTuple` _in the near future_ is low 😕

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [June 17, 2023, 5:09pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/6 "2023-06-17T17:09:32Z")

</div>

@torfjelde, thank you for your insights. You are right that the type is misspecified. Unfortunately, I have not figured out a way support mixed Multivariate types: the choice is discrete whereas the reaction time is continuous. Is there a way to support something like `MixedMultivariateDistribution`?

Alternatively, would a `Tuple` be easier to support than a `NamedTuple`? The other option is to return `Vector{Union{Int,Float64}}`, but I’m not sure whether that would cause performance issues.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:23pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/7 "2023-06-17T18:23:12Z")

</div>

> [@Christopher\_Fisher](#):
>
> Alternatively, would a `Tuple` be easier to support than a `NamedTuple`?

No, that’s the same.

> The other option is to return `Vector{Union{Int,Float64}}` , but I’m not sure whether that would cause performance issues.

This would make it work with Turing.jl yes, but as you say, not sure if this is the way to go.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:24pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/8 "2023-06-17T18:24:51Z")

</div>

Honestly, the most straight-forward approach would probably be to just implement the distribution as a Turing `@model`, and then you can just use it within another model using `@submodel`.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [June 17, 2023, 6:35pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/9 "2023-06-17T18:35:53Z")

</div>

Is there any documentation at all on how submodel works? I feel like Turing needs an update to its documentation given all of the things that have been going on behind the scenes and actually I would love to participate in building out that documentation but unfortunately I don’t know enough about what’s changed to do the work. However maybe a thread here in the forum on things that need to get changed with some quick discussions about how they work could induce some people who are adept at writing documentation to take a stab at that I know I certainly would.

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [June 17, 2023, 6:41pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/10 "2023-06-17T18:41:40Z")

</div>

I modified the example above and was able to get `sample` to work without the missing method error. However, `predict` did not work, which was presented [here](https://github.com/TuringLang/Turing.jl/issues/2012) in some form. It just uses the same data that was input into the model. But I am not sure how to apply that solution to the present example. I tried different data containers such as matrix and Tuple, but the result was the same. Would you be able to look at the most recent version of the code if you have a moment please?

```julia
using Turing
using SequentialSamplingModels
using Random
using LinearAlgebra

# Generate data with different drifts for two conditions A vs. B
Random.seed!(254)
data = rand(LBA(ν=[3.0, 2.0], A=0.8, k=0.2, τ=0.3), 10)

@model function model_lba(data)
    min_rt = minimum(data[2])

    # Priors
    drift ~ filldist(MvNormal(zeros(2), I * 2), 2)
    A ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    k ~ truncated(Normal(0.2, 0.2), 0.0, Inf)
    tau ~ Uniform(0.0, min_rt)

    # Likelihood
    data ~ LBA(; τ=tau, A=A, k=k, ν=drift)
    println("data $data")
    return (; data..., drift, A, k, tau)
end
# this does work now
chain = sample(model_lba(data), Prior(), 100)
# this does not work, but if you rerun predict, it does generate data, but its the same as the input
predictions = predict(model_lba(data), chain)

```

**versions**

```julia
SequentialSamplingModels v0.3.3
Turing v0.26.0

```

Yes, I was able to also ask the same question as dlakelan.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:41pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/11 "2023-06-17T18:41:43Z")

</div>

That would be awesome! And yes, there has been a lot of stuff going on behind the scenes over the past year, so we’re in dire need of updating the docs. One improvement on the docside is that at least now all the separate packages has it’s own documentation, e.g. [Home · DynamicPPL](https://turinglang.org/library/DynamicPPL/stable/), meaning that documentation for these things at least _exists somewhere_. For example, if you just look at the docstring for `@submodel`, it should be fairly well-explained. But we really need to a) update the tutorials to use new best practices, and b) show off some of the new features.

How about I make an issue on Turing.jl on “new stuff” and how we need docs for it? Then we can discuss there:) I’ll try to be as suppportive as possible throughout too.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:45pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/12 "2023-06-17T18:45:18Z")

</div>

> [@Christopher\_Fisher](#):
>
> I modified the example above and was able to get `sample` to work without the missing method error.

But here too the samples for `data` does not end up in the resulting `chain`.

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [June 17, 2023, 6:47pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/13 "2023-06-17T18:47:52Z")

</div>

Gotcha. I was not aware that the data was supposed to be in the chain too. Hmmm. I wonder why it didn’t work with the other data containers.

In any case, could you please provide some guidance on the submodel approach or point us to a resource? Thanks!

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:52pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/14 "2023-06-17T18:52:44Z")

</div>

Here’s the issue: [Docs are in dire need of update · Issue #2013 · TuringLang/Turing.jl · GitHub](https://github.com/TuringLang/Turing.jl/issues/2013)

We can discuss the doc-issue further there 👍

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 17, 2023, 6:54pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/15 "2023-06-17T18:54:06Z")

</div>

> [@Christopher\_Fisher](#):
>
> In any case, could you please provide some guidance on the submodel approach or point us to a resource? Thanks!

`?@submodel` should be a good starting point. But if you can give me, say, the `LBA` model implemented as a `@model`, I can replicate the above example using `@submode` as a demo 👍

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [June 18, 2023, 9:59am UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/16 "2023-06-18T09:59:16Z")

</div>

Thanks. I looked through the doc strings, but still have some questions about what it means to implement a model as @model. However, before I get too far into the details, I wanted to ask you about the code below. I adapted Dominique’s example for a Wald model (basically a reparameterized InverseGaussian), which is actually a subtype of `UnivariateContinuousDistribution`:

```julia
julia> dist = Wald(ν = 2.0, α=1.0, θ=.3); rand(dist)
0.5196099061252006

```

If that was the problem (or sole problem), I would expect the code below to work. For some reason it does not. I want to bring that up to make sure we are addressing the correct issue. In addition, the same thing happens if I use `InverseGaussian` directly instead of `Wald`.

**Wald**

```julia
using Turing
using SequentialSamplingModels
using Random
using LinearAlgebra

# Generate data with different drifts for two conditions A vs. B
Random.seed!(254)
rts = rand(Wald(ν=2.0, α=0.8, θ=0.3), 10)

@model function model_wald(rts)
    min_rt = minimum(rts)

    ν ~ truncated(Normal(2, 1), 0.0, Inf)
    α ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    θ ~ Uniform(0.0, min_rt)

    # Likelihood
    rts ~ Wald(; ν, α, θ)
    return (; rts, ν, α, θ)
end

chain = sample(model_wald(rts), Prior(), 100)

predictions = predict(model_wald(rts), chain)

```

**Inverse Gaussian**

```julia
using Turing
using Distributions
using Random
using LinearAlgebra

# Generate data with different drifts for two conditions A vs. B
Random.seed!(254)
rts = rand(InverseGaussian(1.0, 0.8,), 10)

@model function model_IG(rts)

    ν ~ truncated(Normal(2, 1), 0.0, Inf)
    α ~ truncated(Normal(0.8, 0.4), 0.0, Inf)

    # Likelihood
    rts ~ InverseGaussian(ν, α)
    return (; rts, ν, α)
end

chain = sample(model_IG(rts), Prior(), 100)

predictions = predict(model_IG(rts), chain)

```

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 18, 2023, 4:41pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/17 "2023-06-18T16:41:37Z")

</div>

> [@Christopher\_Fisher](#):
>
> For some reason it does not. I want to bring that up to make sure we are addressing the correct issue. In addition, the same thing happens if I use `InverseGaussian` directly instead of `Wald`.

What exactly do you mean you say"it doesn’t work"? It shouldn’t be the same error as we saw before. If you mean that it won’t sample `rts` when you call `predict`, then this is (possibly unfortunately) how `predict` is intended to work (see [predict() from a Prior-sampled chain returns no predictions · Issue #2012 · TuringLang/Turing.jl · GitHub](https://github.com/TuringLang/Turing.jl/issues/2012#issuecomment-1595721797) for an explanation).

In short, if you now want to sample `rts`, you need to do:

```julia
predictions = predict(model_IG(missing), chain)

```

This won’t work in our case since you depend on `minimum(rts)` in your model, so the full example would be

```julia
using Turing
using SequentialSamplingModels
using Random
using LinearAlgebra

# Generate data with different drifts for two conditions A vs. B
Random.seed!(254)
rts = rand(Wald(ν=2.0, α=0.8, θ=0.3), 10)

@model function model_wald(rts; min_rt=minimum(rts))
    ν ~ truncated(Normal(2, 1), 0.0, Inf)
    α ~ truncated(Normal(0.8, 0.4), 0.0, Inf)
    θ ~ Uniform(0.0, min_rt)

    # Likelihood
    rts ~ Wald(; ν, α, θ)
    return (; rts, ν, α, θ)
end

chain = sample(model_wald(rts), Prior(), 100)

predictions = predict(model_wald(missing; min_rt=minimum(rts)), chain)

```

Though running this code seems to fail because `Wald` doesn’t have `rand` implemented properly:

```julia
julia> dist
Wald
┌───────────┬───────┐
│ Parameter │ Value │
├───────────┼───────┤
│ ν │ 1.36 │
│ α │ 0.74 │
│ θ │ 0.10 │
└───────────┴───────┘

julia> rand(dist)
0.39546979136012583

julia> # Passing `rng` is not supported!
       rand(Random.default_rng(), dist)
ERROR: MethodError: no method matching iterate(::Wald{Float64, Float64, Float64})

Closest candidates are:
  iterate(::Union{LinRange, StepRangeLen})
   @ Base range.jl:880
  iterate(::Union{LinRange, StepRangeLen}, ::Integer)
   @ Base range.jl:880
  iterate(::T) where T<:Union{Base.KeySet{<:Any, <:Dict}, Base.ValueIterator{<:Dict}}
   @ Base dict.jl:698
  ...

Stacktrace:
 [1] copyto!(dest::Vector{Float64}, src::Wald{Float64, Float64, Float64})
   @ Base ./abstractarray.jl:946
 [2] _collect(cont::UnitRange{Int64}, itr::Wald{Float64, Float64, Float64}, #unused#::Base.HasEltype, isz::Base.HasLength)
   @ Base ./array.jl:713
 [3] collect(itr::Wald{Float64, Float64, Float64})
   @ Base ./array.jl:707
 [4] quantile(itr::Wald{Float64, Float64, Float64}, p::Float64; sorted::Bool, alpha::Float64, beta::Float64)
   @ Statistics ~/.julia/juliaup/julia-1.9.0+0.x64.linux.gnu/share/julia/stdlib/v1.9/Statistics/src/Statistics.jl:1070
 [5] quantile(itr::Wald{Float64, Float64, Float64}, p::Float64)
   @ Statistics ~/.julia/juliaup/julia-1.9.0+0.x64.linux.gnu/share/julia/stdlib/v1.9/Statistics/src/Statistics.jl:1070
 [6] rand(rng::TaskLocalRNG, d::Wald{Float64, Float64, Float64})
   @ Distributions ~/.julia/packages/Distributions/uREy8/src/univariates.jl:156
 [7] top-level scope
   @ REPL[32]:1

```

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [June 18, 2023, 5:25pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/18 "2023-06-18T17:25:55Z")

</div>

Got it. Sorry. I misunderstood the nature of the problem.

---

<div class="post-metadata">

**Author:** ![torfjelde](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/torfjelde/32/206542_2.png) [@torfjelde](https://discourse.julialang.org/u/torfjelde)\
**Post date:** [June 18, 2023, 9:51pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/19 "2023-06-18T21:51:40Z")

</div>

Absolutely no worries:)

---

<div class="post-metadata">

**Author:** ![Christopher\_Fisher](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/christopher_fisher/32/26132_2.png) [@Christopher\_Fisher](https://discourse.julialang.org/u/Christopher_Fisher)\
**Post date:** [July 1, 2023, 1:23pm UTC](https://discourse.julialang.org/t/how-to-specify-a-turing-model-to-have-the-2-vectors-as-input-for-a-distribution-that-returns-a-tuple/100493/20 "2023-07-01T13:23:06Z")

</div>

To follow up, the solution was quite simple as soon as I figured out the failure point. Basically, I needed to define a mixed multivariate type and implement `DynampicPPL.vectorize(d::MixedMultivariateDistribution, r::NamedTuple) = [r...]`. A simple working example can be found below for anyone who is interested.

> **Summary**
>
> ```julia
> using Distributions
> using Turing 
> using Random
> import Distributions: logpdf
> import Distributions: loglikelihood
> import Distributions: rand
> import DynamicPPL: vectorize
> import Base: length
> 
> abstract type Mixed <: ValueSupport end 
> 
> const MixedMultivariateDistribution = Distribution{Multivariate, Mixed}
> 
> abstract type SSM1D <: ContinuousUnivariateDistribution end 
> 
> abstract type SSM2D <: MixedMultivariateDistribution end 
> 
> struct MyType{T<:Real} <: SSM2D
> μ::T 
> σ::T
> end
> 
> Base.broadcastable(x::MyType) = Ref(x)
> 
> vectorize(d::MixedMultivariateDistribution, r::NamedTuple) = [r...]
> 
> Base.length(d::MixedMultivariateDistribution) = 2
> 
> rand(d::MixedMultivariateDistribution) = rand(Random.default_rng(), d)
> rand(d::MixedMultivariateDistribution, n::Int) = rand(Random.default_rng(), d, n)
> 
> function rand(rng::AbstractRNG, d::MyType)
> choice = rand(1:2)
> rt = rand(rng, LogNormal(d.μ, d.σ))
> return (;choice, rt)
> end
> 
> function rand(rng::AbstractRNG, d::MyType, N::Int)
> choice = fill(0, N)
> rt = fill(0.0, N)
> for i in 1:N
> choice[i],rt[i] = rand(rng, d)
> end
> return (choice=choice,rt=rt)
> end
> 
> function logpdf(d::MyType, choice::Int, rt::Float64) 
> return logpdf(LogNormal(d.μ, d.σ), rt)
> end
> 
> logpdf(d::MyType, data::NamedTuple) = logpdf(d::MyType, data.choice, data.rt)
> 
> loglikelihood(d::MyType, data::NamedTuple) = sum(logpdf.(d, data...))
> 
> @model function my_model(data)
> μ ~ Normal(0, 1)
> σ ~ truncated(Normal(0, 1), 0, Inf)
> data ~ MyType(μ, σ)
> return (;data, μ, σ)
> end
> 
> data = rand(MyType(0, 1), 10)
> chain = sample(my_model(data), NUTS(), 1_000)
> 
> predictions = predict(my_model(missing), chain)
> 
> ```

@DominiqueMakowski, can you please mark this as the solution? Thanks!
