diff --git a/src/SequentialSamplingModels.jl b/src/SequentialSamplingModels.jl index 47032d9f..ea728944 100644 --- a/src/SequentialSamplingModels.jl +++ b/src/SequentialSamplingModels.jl @@ -42,6 +42,7 @@ export AbstractMLBA export AbstractMDFT export AbstractPoissonRace export AbstractRDM +export AbstractShiftedLogNormal export AbstractstDDM export AbstractWald export aDDM @@ -57,6 +58,7 @@ export maaDDM export MLBA export MDFT export PoissonRace +export ShiftedLogNormal export SSM1D export SSM2D export stDDM @@ -106,4 +108,5 @@ include("stDDM.jl") include("MDFT.jl") include("ClassicMDFT.jl") include("MLBA.jl") +include("ShiftedLogNormal.jl") end diff --git a/src/ShiftedLogNormal.jl b/src/ShiftedLogNormal.jl new file mode 100644 index 00000000..b1216a06 --- /dev/null +++ b/src/ShiftedLogNormal.jl @@ -0,0 +1,55 @@ +""" + ShiftedLogNormal{T <: Real} <: AbstractShiftedLogNormal + +A special case of the lognormal race (LNR) model for a single response. The first passage time is lognormally distributed. + +# Parameters + +- `ν`: mean finishing time in log-space +- `σ`: standard deviation parameter in log-space +- `τ`: a encoding-response offset + +# Constructors + +Two constructors are defined below. The first constructor uses positional arguments, and is therefore order dependent: + + ShiftedLogNormal(ν, σ, τ) + +The second constructor uses keywords with default values, and is not order dependent: + + ShiftedLogNormal(; ν = -1, σ=.5, τ = .20) + +# Example + +```julia +using SequentialSamplingModels +dist = ShiftedLogNormal(ν = -1, σ=.5, τ = .20) +rts = rand(dist, 10) +like = pdf.(dist, rts) +loglike = logpdf.(dist, rts) +``` +# References + +Heathcote, A., & Bohlscheid, E. Analysis and Modeling of Response Time using the Shifted Lognormal Distribution. +""" +struct ShiftedLogNormal{T <: Real} <: AbstractShiftedLogNormal + ν::T + σ::T + τ::T +end + +ShiftedLogNormal(ν, σ, τ) = ShiftedLogNormal(promote(ν, σ, τ)...) + +ShiftedLogNormal(; ν = -1, σ=.5, τ = .20) = ShiftedLogNormal(ν, σ, τ) + +function logpdf(dist::AbstractShiftedLogNormal, rt) + (; τ, ν, σ) = dist + return logpdf(LogNormal(ν, σ), rt - τ) +end + +function rand(dist::AbstractShiftedLogNormal, n_trials::Int) + (; τ, ν, σ) = dist + return rand(LogNormal(ν, σ), n_trials) .+ τ +end + +model = ShiftedLogNormal(ν = 1, σ = 1, τ = .20) \ No newline at end of file diff --git a/src/type_system.jl b/src/type_system.jl index d29198db..917c0f9c 100644 --- a/src/type_system.jl +++ b/src/type_system.jl @@ -98,6 +98,13 @@ An abstract type for the racing diffusion model. """ abstract type AbstractRDM <: SSM2D end +""" + AbstractShiftedLogNormal <: SSM1D + +An abstract type for the shifted lognormal model. +""" +abstract type AbstractShiftedLogNormal <: SSM1D end + abstract type PDFType end """ diff --git a/test/shiftedlognormaldistribution.jl b/test/shiftedlognormaldistribution.jl new file mode 100644 index 00000000..13af0939 --- /dev/null +++ b/test/shiftedlognormaldistribution.jl @@ -0,0 +1,68 @@ +@safetestset "Shifted Lognormal Distribution" begin + + @safetestset "rand" begin + using Distributions + using Random + using SequentialSamplingModels + using Test + + Random.seed!(5411) + + lognormal = LogNormal(-1, .3) + shifted_lognormal = ShiftedLogNormal(ν = -1, σ=.3, τ = 0.5) + mean(rand(shifted_lognormal, 10_000)) + # τ is properly added + @test mean(rand(lognormal, 10_000)) - mean(rand(shifted_lognormal, 10_000)) ≈ -.5 atol = .01 + end + + + @safetestset "pdf" begin + using Distributions + using SequentialSamplingModels + using Test + + lognormal = LogNormal(-1, .3) + shifted_lognormal = ShiftedLogNormal(ν = -1, σ=.3, τ = 0.0) + x = rand(shifted_lognormal, 100) + @test pdf.(lognormal, x) ≈ pdf.(shifted_lognormal, x) + end + + @safetestset "logpdf 1" begin + using Distributions + using SequentialSamplingModels + using Test + + lognormal = LogNormal(-1, .3) + shifted_lognormal = ShiftedLogNormal(ν = -1, σ=.3, τ = 0.0) + x = rand(shifted_lognormal, 100) + @test logpdf.(lognormal, x) ≈ logpdf.(shifted_lognormal, x) + end + + @safetestset "logpdf 2" begin + using Distributions + using Random + using SequentialSamplingModels + using Test + + Random.seed!(2008) + + parms = (ν = -1, σ=.3, τ = 0.0) + x = rand(ShiftedLogNormal(; parms...), 10_000) + + νs = range(.80 * parms.ν, 1.2 * parms.ν, length = 100) + LLs = map(ν -> sum(logpdf.(ShiftedLogNormal(; parms..., ν), x)), νs) + _,idx = findmax(LLs) + @test νs[idx] ≈ parms.ν rtol = .01 + + σs = range(.80 * parms.σ, 1.2 * parms.σ, length = 100) + LLs = map(σ -> sum(logpdf.(ShiftedLogNormal(; parms..., σ), x)), σs) + _,idx = findmax(LLs) + @test σs[idx] ≈ parms.σ rtol = .01 + + + τs = range(.80 * parms.τ, 1.2 * parms.τ, length = 100) + LLs = map(τ -> sum(logpdf.(ShiftedLogNormal(; parms..., τ), x)), τs) + _,idx = findmax(LLs) + @test τs[idx] ≈ parms.τ rtol = .01 + end +end \ No newline at end of file