Nothing
# Tests for second-order perturbation (v0.8.0)
# --- Helper: simple nonlinear model for testing ---
build_nl_ar1 <- function(rhoA = 0.90) {
dsgenl_model(
"Y = A",
"A(+1) = A^rhoA",
observed = "Y",
unobserved = character(0),
exo_state = "A",
fixed = list(),
start = list(rhoA = rhoA),
ss_guess = c(Y = 1, A = 1)
)
}
# Helper: model with genuine nonlinearity (curvature matters)
build_nl_consumption <- function() {
dsgenl_model(
"Y = A * H",
"1 / C = beta * R / C(+1)",
"Y = C",
"R = 1 / beta",
"A(+1) = A^rhoA",
observed = "Y",
unobserved = c("C", "H", "R"),
exo_state = "A",
fixed = list(beta = 0.99),
start = list(rhoA = 0.90),
ss_guess = c(Y = 1, C = 1, H = 1, R = 1.0101, A = 1)
)
}
test_that("solve_dsge with order=2 returns second-order solution", {
mod <- build_nl_ar1()
sol <- solve_dsge(mod, params = c(rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
expect_s3_class(sol, "dsge_solution")
expect_equal(sol$order, 2L)
expect_false(is.null(sol$g_xx))
expect_false(is.null(sol$h_xx))
expect_false(is.null(sol$g_ss))
expect_false(is.null(sol$h_ss))
})
test_that("first-order solution preserved in second-order object", {
mod <- build_nl_ar1()
sol1 <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01), order = 1)
sol2 <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01), order = 2)
# G and H should be identical
expect_equal(sol2$G, sol1$G, tolerance = 1e-6)
expect_equal(sol2$H, sol1$H, tolerance = 1e-6)
expect_equal(sol2$M, sol1$M, tolerance = 1e-6)
expect_true(sol2$stable)
})
test_that("g_xx and h_xx have correct dimensions", {
mod <- build_nl_ar1()
sol <- solve_dsge(mod, params = c(rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
n_c <- nrow(sol$G)
n_s <- ncol(sol$G)
expect_equal(dim(sol$g_xx), c(n_c, n_s, n_s))
expect_equal(dim(sol$h_xx), c(n_s, n_s, n_s))
expect_equal(length(sol$g_ss), n_c)
expect_equal(length(sol$h_ss), n_s)
})
test_that("order=2 with linear model gives error", {
m <- dsge_model(
obs(y ~ z),
state(z ~ rho * z),
start = list(rho = 0.9)
)
expect_error(
solve_dsge(m, params = c(rho = 0.9), order = 2),
"dsgenl_model"
)
})
test_that("order=1 is default and backward compatible", {
mod <- build_nl_ar1()
sol_default <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01))
sol_explicit <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01), order = 1)
expect_equal(sol_default$G, sol_explicit$G)
expect_equal(sol_default$H, sol_explicit$H)
expect_null(sol_default$order) # first-order doesn't set order field
})
test_that("simulate_2nd_order produces output", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
sim <- simulate_2nd_order(sol, n = 50, n_burn = 10, seed = 42)
expect_equal(nrow(sim$states), 50L)
expect_equal(nrow(sim$controls), 50L)
expect_false(is.null(sim$state_levels))
expect_false(is.null(sim$control_levels))
expect_equal(sim$order, 2L)
})
test_that("simulate_2nd_order errors on first-order solution", {
mod <- build_nl_ar1()
sol <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01), order = 1)
expect_error(simulate_2nd_order(sol), "order = 2")
})
test_that("irf_2nd_order produces output", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
irf <- irf_2nd_order(sol, shock = "A", size = 1, periods = 20)
expect_s3_class(irf, "dsge_irf_2nd")
expect_true("period" %in% names(irf))
expect_true("variable" %in% names(irf))
expect_true("response" %in% names(irf))
expect_equal(irf$order[1], 2L)
})
test_that("irf_2nd_order errors on unknown shock", {
mod <- build_nl_ar1()
sol <- solve_dsge(mod, params = c(rhoA = 0.90), shock_sd = c(A = 0.01), order = 2)
expect_error(irf_2nd_order(sol, shock = "bad_shock"), "Unknown shock")
})
test_that("second-order IRF is asymmetric for positive vs negative shocks", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
irf_pos <- irf_2nd_order(sol, shock = "A", size = 3, periods = 10)
irf_neg <- irf_2nd_order(sol, shock = "A", size = -3, periods = 10)
# Extract Y responses
y_pos <- irf_pos$response[irf_pos$variable == "Y"]
y_neg <- irf_neg$response[irf_neg$variable == "Y"]
# Second-order: positive and negative should NOT be mirror images
# (they would be mirror images under first-order)
# Test that the asymmetry is nonzero (even if small)
asymmetry <- y_pos + y_neg # would be zero for first-order
# With small models the asymmetry may be tiny, so just check structure
expect_equal(length(y_pos), length(y_neg))
expect_equal(length(y_pos), 10L)
})
test_that("second-order solution with multi-variable model works", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
expect_equal(sol$order, 2L)
expect_true(nrow(sol$G) >= 2) # multiple controls
expect_true(ncol(sol$G) >= 1) # at least one state
# g_ss should be named
expect_false(is.null(names(sol$g_ss)))
expect_false(is.null(names(sol$h_ss)))
})
test_that("invalid order value gives error", {
mod <- build_nl_ar1()
expect_error(solve_dsge(mod, params = c(rhoA = 0.9), order = 4), "order must be 1, 2, or 3")
expect_error(solve_dsge(mod, params = c(rhoA = 0.9), order = 0), "order must be 1, 2, or 3")
})
test_that("second-order constant correction (g_ss) is finite", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
expect_true(all(is.finite(sol$g_ss)))
expect_true(all(is.finite(sol$h_ss)))
})
test_that("second-order simulation is reproducible with seed", {
mod <- build_nl_consumption()
sol <- suppressWarnings(
solve_dsge(mod, params = c(beta = 0.99, rhoA = 0.90),
shock_sd = c(A = 0.01), order = 2)
)
sim1 <- simulate_2nd_order(sol, n = 20, n_burn = 5, seed = 123)
sim2 <- simulate_2nd_order(sol, n = 20, n_burn = 5, seed = 123)
expect_equal(sim1$states, sim2$states)
expect_equal(sim1$controls, sim2$controls)
})
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.