Code
using LinearAlgebra, Distributions, Random
using Optim, ForwardDiff
using Tables, MarkdownTables
using StatsPlotsLearning note of using Julia for simulation
Mark Lai
February 23, 2024
In this note, I’m trying to use the Julia language to run some simulations.
First, here’s an example of simulating multivariate normal random variables
3×200 Matrix{Float64}:
0.0540757 -0.102689 0.658599 0.611513 … 0.879624 0.605667 0.595295
1.14527 -0.222159 2.24221 1.30428 0.254144 2.05194 2.90419
2.6294 0.572331 1.47629 2.98324 4.10777 1.05924 1.56324
Specify implied moments from a growth model
(μ = [0, 1, 2, 3], Σ = [2.0 1.4 1.44 1.608; 1.4 2.65 2.3000000000000003 2.59; 1.44 2.3000000000000003 3.8 3.7; 1.608 2.59 3.7 5.45])
Simulate data from a growth model
4×500 Matrix{Float64}:
0.629762 -0.205364 1.64222 0.486685 … -0.292158 0.13341 -0.624102
1.95646 3.54633 -0.797253 0.608302 1.88673 1.03129 -0.378568
1.91765 2.56468 0.553998 3.44475 2.42096 3.01259 0.746786
1.88494 2.93066 0.570765 1.94388 0.564698 3.71769 0.984581
Fit a growth model using maximum likelihood estimation using Optim.jl
"""
growth_ll(α, Ψ, Θ, Λ, y)
Compute log-likelihood of the input parameters given data y
α = latent means
Ψ = latent variance and covariances
Θ = error covariances
Λ = loading matrix
"""
function growth_ll(α, Ψ, Θ, Λ, y)
μhat, Σhat = growth_moments(Λ, α, Ψ, Θ)
if !isposdef(Σhat)
return Inf
end
d = MvNormal(μhat, Σhat)
loglikelihood(MvNormal(μhat, Σhat), y)
end
growth_ll(α, Ψ, Θ, Λ, sim_dat)
"""
growth_ll(θ, Λ, y)
Compute log-likelihood of the input parameter vector given data y
"""
function growth_ll(θ, Λ, y)
α = view(θ, 1:2)
Ψ = reshape(θ[[3, 4, 4, 5]], (2, 2))
Θ = [θ[6] * θ[7]^abs(i - j) for i in 1:4, j in 1:4]
growth_ll(α, Ψ, Θ, Λ, y)
end
# Maximum likelihood estimation
# Starting values:
θ₀ = vcat(α, Ψ[[1, 2, 4]], 1, .2)
opt = optimize(x -> -growth_ll(x, Λ, sim_dat), θ₀, Newton(); autodiff=:forward) * Status: success
* Candidate solution
Final objective value: 3.378396e+03
* Found with
Algorithm: Newton's Method
* Convergence measures
|x - x'| = 1.09e-07 ≰ 0.0e+00
|x - x'|/|x'| = 1.11e-07 ≰ 0.0e+00
|f(x) - f(x')| = 1.36e-12 ≰ 0.0e+00
|f(x) - f(x')|/|f(x')| = 4.04e-16 ≰ 0.0e+00
|g(x)| = 4.75e-12 ≤ 1.0e-08
* Work counters
Seconds run: 0 (vs limit Inf)
Iterations: 4
f(x) calls: 12
∇f(x) calls: 12
∇²f(x) calls: 4
Wrap into a function
sim_θhat (generic function with 1 method)
Repeat \(R\) replications
7×1 Matrix{Float64}:
0.0006639386137645048
0.9978489158537106
0.9454197504862293
0.20577261923685305
0.24026813468427904
1.0373020388366407
0.2070202636496464
| Parameter | Estimate | Bias |
|---|---|---|
| αᵢ | 0.0006639386137645048 | 0.0006639386137645048 |
| αₛ | 0.9978489158537106 | -0.002151084146289395 |
| ψᵢ | 0.9454197504862293 | -0.05458024951377072 |
| ψᵢₛ | 0.20577261923685305 | 0.005772619236853044 |
| ψₛ | 0.24026813468427904 | -0.009731865315720956 |
| θ | 1.0373020388366407 | 0.03730203883664074 |
| ρ | 0.2070202636496464 | 0.007020263649646391 |
---
title: "Monte Carlo Simulation with Julia (Growth Model)"
engine: julia
description: |
Learning note of using Julia for simulation
author: Mark Lai
date: "2024-02-23"
categories:
- Statistics
- Structural equation modeling
- Simulation
- Julia
---
In this note, I'm trying to use the [Julia](https://julialang.org/) language to run some simulations.
```{julia}
using LinearAlgebra, Distributions, Random
using Optim, ForwardDiff
using Tables, MarkdownTables
using StatsPlots
```
First, here's an example of simulating multivariate normal random variables
```{julia}
Random.seed!(1234)
μ = [0, 1, 2]
Σ = cov(randn(100, 3))
n = 200
y = rand(MvNormal(μ, Σ), n)
```
## Growth Model Example
Specify implied moments from a growth model
```{julia}
growth_moments(Λ, α, Ψ, Θ) = (μ = Λ * α, Σ = Hermitian(Λ * Ψ * Λ') + Θ)
Λ = [1 0
1 1
1 2
1 3]
α = [0, 1]
Ψ = Symmetric([1 0.2
0.2 0.25])
Θ = Symmetric([ 1 0.2 0.04 0.008
0.2 1 0.2 0.04
0.04 0.2 1 0.2
0.008 0.04 0.2 1]) # autoregressive structure
gm = growth_moments(Λ, α, Ψ, Θ)
```
Simulate data from a growth model
```{julia}
sim_dat = rand(MvNormal(growth_moments(Λ, α, Ψ, Θ)...), 500)
```
Fit a growth model using maximum likelihood estimation using `Optim.jl`
```{julia}
"""
growth_ll(α, Ψ, Θ, Λ, y)
Compute log-likelihood of the input parameters given data y
α = latent means
Ψ = latent variance and covariances
Θ = error covariances
Λ = loading matrix
"""
function growth_ll(α, Ψ, Θ, Λ, y)
μhat, Σhat = growth_moments(Λ, α, Ψ, Θ)
if !isposdef(Σhat)
return Inf
end
d = MvNormal(μhat, Σhat)
loglikelihood(MvNormal(μhat, Σhat), y)
end
growth_ll(α, Ψ, Θ, Λ, sim_dat)
"""
growth_ll(θ, Λ, y)
Compute log-likelihood of the input parameter vector given data y
"""
function growth_ll(θ, Λ, y)
α = view(θ, 1:2)
Ψ = reshape(θ[[3, 4, 4, 5]], (2, 2))
Θ = [θ[6] * θ[7]^abs(i - j) for i in 1:4, j in 1:4]
growth_ll(α, Ψ, Θ, Λ, y)
end
# Maximum likelihood estimation
# Starting values:
θ₀ = vcat(α, Ψ[[1, 2, 4]], 1, .2)
opt = optimize(x -> -growth_ll(x, Λ, sim_dat), θ₀, Newton(); autodiff=:forward)
```
Wrap into a function
```{julia}
function sim_θhat(n)
ysim = rand(MvNormal(growth_moments(Λ, α, Ψ, Θ)...), n)
opt = optimize(x -> -growth_ll(x, Λ, ysim), θ₀, Newton(); autodiff=:forward)
opt.minimizer
end
```
Repeat $R$ replications
```{julia}
num_obs = 250
num_rep = 500
# Initialize output
results = Matrix(undef, length(θ₀), num_rep)
for i in 1:num_rep
results[:, i] = sim_θhat(num_obs)
end
# Mean estimates
mean(results, dims = 2)
```
### Result table
```{julia}
#| label: tbl-results
#| tbl-cap: Bias of Parameter Estimates
#| output: asis
parnames = ["αᵢ", "αₛ", "ψᵢ", "ψᵢₛ", "ψₛ", "θ", "ρ"]
results_tbl = hcat(parnames,
mean(results, dims = 2),
mean(results, dims = 2) .- θ₀)
print(markdown_table(Tables.table(results_tbl; header = [:Parameter, :Estimate, :Bias]), String))
```
### Plot
```{julia}
#| label: fig-results
#| fig-cap: Bias of Parameter Estimates
Plots.boxplot(repeat(parnames, outer = 500), vec(results .- θ₀), legend = false)
```